Procházet zdrojové kódy

Merge branch 'master' into windows-llvm-13.0.0

gingerBill před 2 roky
rodič
revize
edcd9a6b9b
100 změnil soubory, kde provedl 3146 přidání a 1744 odebrání
  1. 3 0
      .github/workflows/nightly.yml
  2. 11 9
      build.bat
  3. 22 44
      core/bufio/read_writer.odin
  4. 13 45
      core/bufio/reader.odin
  5. 20 39
      core/bufio/writer.odin
  6. 22 60
      core/bytes/buffer.odin
  7. 20 39
      core/bytes/reader.odin
  8. 2 2
      core/c/frontend/preprocessor/preprocess.odin
  9. 3 2
      core/compress/common.odin
  10. 1 1
      core/compress/gzip/gzip.odin
  11. 3 3
      core/compress/zlib/zlib.odin
  12. 2 2
      core/container/topological_sort/topological_sort.odin
  13. 4 4
      core/crypto/blake/blake.odin
  14. 1 1
      core/crypto/blake2b/blake2b.odin
  15. 1 1
      core/crypto/blake2s/blake2s.odin
  16. 1 1
      core/crypto/gost/gost.odin
  17. 4 4
      core/crypto/groestl/groestl.odin
  18. 15 15
      core/crypto/haval/haval.odin
  19. 4 4
      core/crypto/jh/jh.odin
  20. 4 4
      core/crypto/keccak/keccak.odin
  21. 1 1
      core/crypto/md2/md2.odin
  22. 1 1
      core/crypto/md4/md4.odin
  23. 1 1
      core/crypto/md5/md5.odin
  24. 4 4
      core/crypto/ripemd/ripemd.odin
  25. 1 1
      core/crypto/sha1/sha1.odin
  26. 4 4
      core/crypto/sha2/sha2.odin
  27. 4 4
      core/crypto/sha3/sha3.odin
  28. 2 2
      core/crypto/shake/shake.odin
  29. 1 1
      core/crypto/sm3/sm3.odin
  30. 2 2
      core/crypto/streebog/streebog.odin
  31. 3 3
      core/crypto/tiger/tiger.odin
  32. 3 3
      core/crypto/tiger2/tiger2.odin
  33. 1 1
      core/crypto/whirlpool/whirlpool.odin
  34. 3 3
      core/dynlib/lib_js.odin
  35. 2 2
      core/encoding/hxa/read.odin
  36. 5 5
      core/encoding/json/unmarshal.odin
  37. 9 9
      core/fmt/fmt.odin
  38. 10 13
      core/fmt/fmt_js.odin
  39. 11 11
      core/fmt/fmt_os.odin
  40. 6 6
      core/image/netpbm/netpbm.odin
  41. 5 5
      core/image/png/helpers.odin
  42. 30 74
      core/io/conv.odin
  43. 144 296
      core/io/io.odin
  44. 47 39
      core/io/multi.odin
  45. 43 36
      core/io/util.odin
  46. 13 13
      core/log/log.odin
  47. 27 27
      core/log/log_allocator.odin
  48. 3 3
      core/math/big/helpers.odin
  49. 2 2
      core/math/big/internal.odin
  50. 1 1
      core/math/big/radix.odin
  51. 1 1
      core/math/big/rat.odin
  52. 513 0
      core/math/cmplx/cmplx.odin
  53. 273 0
      core/math/cmplx/cmplx_invtrig.odin
  54. 409 0
      core/math/cmplx/cmplx_trig.odin
  55. 1 1
      core/math/ease/ease.odin
  56. 74 0
      core/math/math.odin
  57. 308 0
      core/math/math_sincos.odin
  58. 6 6
      core/mem/allocators.odin
  59. 1 1
      core/mem/virtual/arena.odin
  60. 6 2
      core/os/os2/errors_windows.odin
  61. 38 30
      core/os/os2/file.odin
  62. 63 21
      core/os/os2/file_linux.odin
  63. 6 76
      core/os/os2/file_stream.odin
  64. 68 23
      core/os/os2/file_windows.odin
  65. 5 4
      core/os/os_darwin.odin
  66. 29 2
      core/os/os_linux.odin
  67. 46 52
      core/os/stream.odin
  68. 2 2
      core/reflect/reflect.odin
  69. 6 6
      core/runtime/core_builtin.odin
  70. 2 2
      core/runtime/default_allocators_js.odin
  71. 49 1
      core/runtime/default_allocators_nil.odin
  72. 2 2
      core/runtime/default_allocators_wasi.odin
  73. 10 10
      core/runtime/error_checks.odin
  74. 27 6
      core/runtime/internal.odin
  75. 10 10
      core/slice/ptr.odin
  76. 18 12
      core/slice/slice.odin
  77. 16 25
      core/strings/builder.odin
  78. 19 39
      core/strings/reader.odin
  79. 4 4
      core/strings/strings.odin
  80. 34 0
      core/sys/unix/syscalls_linux.odin
  81. 1 1
      core/testing/runner_windows.odin
  82. 5 5
      core/testing/testing.odin
  83. 2 2
      core/text/i18n/i18n.odin
  84. 1 1
      core/text/table/table.odin
  85. 33 100
      core/thread/thread.odin
  86. 1 9
      core/thread/thread_js.odin
  87. 1 1
      core/thread/thread_pool.odin
  88. 7 7
      core/thread/thread_unix.odin
  89. 8 7
      core/thread/thread_windows.odin
  90. 1 1
      core/unicode/tools/generate_entity_table.odin
  91. 0 2
      examples/all/all_experimental.odin
  92. 2 0
      examples/all/all_main.odin
  93. 9 1
      examples/all/all_vendor.odin
  94. 3 3
      examples/demo/demo.odin
  95. 2 0
      misc/shell.bat
  96. 3 1
      src/array.cpp
  97. 410 400
      src/check_expr.cpp
  98. 53 8
      src/check_stmt.cpp
  99. 8 3
      src/check_type.cpp
  100. 5 1
      src/entity.cpp

+ 3 - 0
.github/workflows/nightly.yml

@@ -7,6 +7,7 @@ on:
 
 jobs:
   build_windows:
+    if: github.repository == 'odin-lang/Odin'
     runs-on: windows-2022
     steps:
       - uses: actions/checkout@v1
@@ -37,6 +38,7 @@ jobs:
           name: windows_artifacts
           path: dist
   build_ubuntu:
+    if: github.repository == 'odin-lang/Odin'
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v1
@@ -61,6 +63,7 @@ jobs:
           name: ubuntu_artifacts
           path: dist
   build_macos:
+    if: github.repository == 'odin-lang/Odin'
     runs-on: macOS-latest
     steps:
       - uses: actions/checkout@v1

+ 11 - 9
build.bat

@@ -3,18 +3,20 @@
 setlocal EnableDelayedExpansion
 
 where /Q cl.exe || (
-  set __VSCMD_ARG_NO_LOGO=1
-  for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
-  if "!VS!" equ "" (
-    echo ERROR: Visual Studio installation not found
-    exit /b 1
-  )  
-  call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
+	set __VSCMD_ARG_NO_LOGO=1
+	for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
+	if "!VS!" equ "" (
+		echo ERROR: Visual Studio installation not found
+		exit /b 1
+	)
+	call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
 )
 
 if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
-  echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
-  exit /b 1
+	if "%ODIN_IGNORE_MSVC_CHECK%" == "" (
+		echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
+		exit /b 1
+	)
 )
 
 for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (

+ 22 - 44
core/bufio/read_writer.odin

@@ -14,51 +14,29 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
 }
 
 read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
-	s.stream_data = rw
-	s.stream_vtable = &_read_writer_vtable
+	s.procedure = _read_writer_procedure
+	s.data = rw
 	return
 }
 
 @(private)
-_read_writer_vtable := io.Stream_VTable{
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Read_Writer)(s.stream_data).r
-		return reader_read(b, p)
-	},
-	impl_unread_byte = proc(s: io.Stream) -> io.Error {
-		b := (^Read_Writer)(s.stream_data).r
-		return reader_unread_byte(b)
-	},
-	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
-		b := (^Read_Writer)(s.stream_data).r
-		return reader_read_rune(b)
-	},
-	impl_unread_rune = proc(s: io.Stream) -> io.Error {
-		b := (^Read_Writer)(s.stream_data).r
-		return reader_unread_rune(b)
-	},
-	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
-		b := (^Read_Writer)(s.stream_data).r
-		return reader_write_to(b, w)
-	},
-	impl_flush = proc(s: io.Stream)  -> io.Error {
-		b := (^Read_Writer)(s.stream_data).w
-		return writer_flush(b)
-	},
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Read_Writer)(s.stream_data).w
-		return writer_write(b, p)
-	},
-	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
-		b := (^Read_Writer)(s.stream_data).w
-		return writer_write_byte(b, c)
-	},
-	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
-		b := (^Read_Writer)(s.stream_data).w
-		return writer_write_rune(b, r)
-	},
-	impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
-		b := (^Read_Writer)(s.stream_data).w
-		return writer_read_from(b, r)
-	},
-}
+_read_writer_procedure := proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	rw := (^Read_Writer)(stream_data)
+	n_int: int
+	#partial switch mode {
+	case .Flush:
+		err = writer_flush(rw.w)
+		return
+	case .Read:
+		n_int, err = reader_read(rw.r, p)
+		n = i64(n_int)
+		return
+	case .Write:
+		n_int, err = writer_write(rw.w, p)
+		n = i64(n_int)
+		return
+	case .Query:
+		return io.query_utility({.Flush, .Read, .Write, .Query})
+	}
+	return 0, .Empty
+}

+ 13 - 45
core/bufio/reader.odin

@@ -311,18 +311,6 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
 	}
 
 	m: i64
-	if nr, ok := io.to_writer_to(b.rd); ok {
-		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
-	}
-
 	if b.w-b.r < len(b.buf) {
 		if err = _reader_read_new_chunk(b); err != nil {
 			return
@@ -352,48 +340,28 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
 
 // 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
+	s.data = b
+	s.procedure = _reader_proc
 	return
 }
 
 
 
 @(private)
-_reader_vtable := io.Stream_VTable{
-	impl_destroy = proc(s: io.Stream) -> io.Error {
-		b := (^Reader)(s.stream_data)
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	b := (^Reader)(stream_data)
+	#partial switch mode {
+	case .Read:
+		return io._i64_err(reader_read(b, p))
+	case .Destroy:
 		reader_destroy(b)
-		return nil
-	},
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Reader)(s.stream_data)
-		return reader_read(b, p)
-	},
-	impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
-		b := (^Reader)(s.stream_data)
-		return reader_read_byte(b)
-	},
-	impl_unread_byte = proc(s: io.Stream) -> io.Error {
-		b := (^Reader)(s.stream_data)
-		return reader_unread_byte(b)
-	},
-	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
-		b := (^Reader)(s.stream_data)
-		return reader_read_rune(b)
-	},
-	impl_unread_rune = proc(s: io.Stream) -> io.Error {
-		b := (^Reader)(s.stream_data)
-		return reader_unread_rune(b)
-	},
-	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
-		b := (^Reader)(s.stream_data)
-		return reader_write_to(b, w)
-	},
+		return
+	case .Query:
+		return io.query_utility({.Read, .Destroy, .Query})
+	}
+	return 0, .Empty
 }
 
-
-
 //
 // Utility procedures
 //

+ 20 - 39
core/bufio/writer.odin

@@ -173,14 +173,6 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
 	if b.err != nil {
 		return 0, b.err
 	}
-	if writer_buffered(b) == 0 {
-		if w, ok := io.to_reader_from(b.wr); !ok {
-			n, err = io.read_from(w, r)
-			b.err = err
-			return
-		}
-	}
-
 	for {
 		if writer_available(b) == 0 {
 			writer_flush(b) or_return
@@ -222,46 +214,35 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
 
 // 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
+	s.data = b
+	s.procedure = _writer_proc
 	return
 }
 
 // writer_to_stream converts a Writer into an io.Stream
 writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
-	s.stream_data = b
-	s.stream_vtable = &_writer_vtable
-	return
+	return writer_to_stream(b)
 }
 
 
 
-
 @(private)
-_writer_vtable := io.Stream_VTable{
-	impl_destroy = proc(s: io.Stream) -> io.Error {
-		b := (^Writer)(s.stream_data)
+_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	b := (^Writer)(stream_data)
+	#partial switch mode {
+	case .Flush:
+		err = writer_flush(b)
+		return
+	case .Write:
+		n_int: int
+		n_int, err = writer_write(b, p)
+		n = i64(n_int)
+		return
+	case .Destroy:
 		writer_destroy(b)
-		return nil
-	},
-	impl_flush = proc(s: io.Stream)  -> io.Error {
-		b := (^Writer)(s.stream_data)
-		return writer_flush(b)
-	},
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Writer)(s.stream_data)
-		return writer_write(b, p)
-	},
-	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
-		b := (^Writer)(s.stream_data)
-		return writer_write_byte(b, c)
-	},
-	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
-		b := (^Writer)(s.stream_data)
-		return writer_write_rune(b, r)
-	},
-	impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
-		b := (^Writer)(s.stream_data)
-		return writer_read_from(b, r)
-	},
+		return
+	case .Query:
+		return io.query_utility({.Flush, .Write, .Destroy, .Query})
+	}
+	return 0, .Empty
 }

+ 22 - 60
core/bytes/buffer.odin

@@ -375,69 +375,31 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
 
 
 buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
-	s.stream_data = b
-	s.stream_vtable = &_buffer_vtable
+	s.data = b
+	s.procedure = _buffer_proc
 	return
 }
 
 @(private)
-_buffer_vtable := io.Stream_VTable{
-	impl_size = proc(s: io.Stream) -> i64 {
-		b := (^Buffer)(s.stream_data)
-		return i64(buffer_capacity(b))
-	},
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Buffer)(s.stream_data)
-		return buffer_read(b, p)
-	},
-	impl_read_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))
-	},
-	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
-		b := (^Buffer)(s.stream_data)
-		return buffer_read_byte(b)
-	},
-	impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
-		b := (^Buffer)(s.stream_data)
-		return buffer_read_rune(b)
-	},
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Buffer)(s.stream_data)
-		return buffer_write(b, p)
-	},
-	impl_write_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))
-	},
-	impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
-		b := (^Buffer)(s.stream_data)
-		return buffer_write_byte(b, c)
-	},
-	impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
-		b := (^Buffer)(s.stream_data)
-		return buffer_write_rune(b, r)
-	},
-	impl_unread_byte = proc(s: io.Stream) -> io.Error {
-		b := (^Buffer)(s.stream_data)
-		return buffer_unread_byte(b)
-	},
-	impl_unread_rune = proc(s: io.Stream) -> io.Error {
-		b := (^Buffer)(s.stream_data)
-		return buffer_unread_rune(b)
-	},
-	impl_destroy = proc(s: io.Stream) -> io.Error {
-		b := (^Buffer)(s.stream_data)
+_buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	b := (^Buffer)(stream_data)
+	#partial switch mode {
+	case .Read:
+		return io._i64_err(buffer_read(b, p))
+	case .Read_At:
+		return io._i64_err(buffer_read_at(b, p, int(offset)))
+	case .Write:
+		return io._i64_err(buffer_write(b, p))
+	case .Write_At:
+		return io._i64_err(buffer_write_at(b, p, int(offset)))
+	case .Size:
+		n = i64(buffer_capacity(b))
+		return
+	case .Destroy:
 		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)
-	},
-	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)
-	},
+		return
+	case .Query:
+		return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy})
+	}
+	return 0, .Empty
 }
-

+ 20 - 39
core/bytes/reader.odin

@@ -16,8 +16,8 @@ reader_init :: proc(r: ^Reader, s: []byte) {
 }
 
 reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
-	s.stream_data = r
-	s.stream_vtable = &_reader_vtable
+	s.data = r
+	s.procedure = _reader_proc
 	return
 }
 
@@ -137,41 +137,22 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
 
 
 @(private)
-_reader_vtable := io.Stream_VTable{
-	impl_size = proc(s: io.Stream) -> i64 {
-		r := (^Reader)(s.stream_data)
-		return reader_size(r)
-	},
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read(r, p)
-	},
-	impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_at(r, p, off)
-	},
-	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_byte(r)
-	},
-	impl_unread_byte = proc(s: io.Stream) -> io.Error {
-		r := (^Reader)(s.stream_data)
-		return reader_unread_byte(r)
-	},
-	impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_rune(r)
-	},
-	impl_unread_rune = proc(s: io.Stream) -> io.Error {
-		r := (^Reader)(s.stream_data)
-		return reader_unread_rune(r)
-	},
-	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_seek(r, offset, whence)
-	},
-	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_write_to(r, w)
-	},
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	r := (^Reader)(stream_data)
+	#partial switch mode {
+	case .Read:
+		return io._i64_err(reader_read(r, p))
+	case .Read_At:
+		return io._i64_err(reader_read_at(r, p, offset))
+	case .Seek:
+		n, err = reader_seek(r, offset, whence)
+		return
+	case .Size:
+		n = reader_size(r)
+		return
+	case .Query:
+		return io.query_utility({.Read, .Read_At, .Seek, .Size, .Query})
+	}
+	return 0, .Empty
 }
+

+ 2 - 2
core/c/frontend/preprocessor/preprocess.odin

@@ -1118,7 +1118,7 @@ expand_macro :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> bool {
 
 search_include_next :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) {
 	for ; cpp.include_next_index < len(cpp.include_paths); cpp.include_next_index += 1 {
-		tpath := filepath.join(elems={cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator)
+		tpath := filepath.join({cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator)
 		if os.exists(tpath) {
 			return strings.clone(tpath), true
 		}
@@ -1136,7 +1136,7 @@ search_include_paths :: proc(cpp: ^Preprocessor, filename: string) -> (path: str
 	}
 
 	for include_path in cpp.include_paths {
-		tpath := filepath.join(elems={include_path, filename}, allocator=context.temp_allocator)
+		tpath := filepath.join({include_path, filename}, allocator=context.temp_allocator)
 		if os.exists(tpath) {
 			path, ok = strings.clone(tpath), true
 			cpp.filepath_cache[filename] = path

+ 3 - 2
core/compress/common.odin

@@ -188,7 +188,8 @@ input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Erro
 }
 
 input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
-	return io.size(z.input), nil
+	res, _ = io.size(z.input)
+	return
 }
 
 input_size :: proc{input_size_from_memory, input_size_from_stream}
@@ -215,7 +216,7 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int
 	// TODO: REMOVE ALL USE OF context.temp_allocator here
 	// the is literally no need for it
 	b := make([]u8, size, context.temp_allocator)
-	_, e := z.input->impl_read(b[:])
+	_, e := io.read(z.input, b[:])
 	if e == .None {
 		return b, .None
 	}

+ 1 - 1
core/compress/gzip/gzip.odin

@@ -335,7 +335,7 @@ 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, expected_output_size=expected_output_size)
 	if zlib_error != nil {
 		return zlib_error
 	}

+ 3 - 3
core/compress/zlib/zlib.odin

@@ -471,7 +471,7 @@ 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(ctx, expected_output_size=expected_output_size) or_return
 
 	if !raw {
 		compress.discard_to_next_byte_lsb(ctx)
@@ -665,7 +665,7 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, e
 	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, 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) {
@@ -674,7 +674,7 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
 	ctx.input_data = input
 	ctx.output = buf
 
-	return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
+	return inflate_raw(&ctx, expected_output_size=expected_output_size)
 }
 
 inflate :: proc{inflate_from_context, inflate_from_byte_array}

+ 2 - 2
core/container/topological_sort/topological_sort.odin

@@ -32,7 +32,7 @@ init :: proc(sorter: ^$S/Sorter($K)) {
 }
 
 destroy :: proc(sorter: ^$S/Sorter($K)) {
-	for _, v in &sorter.relations {
+	for _, v in sorter.relations {
 		delete(v.dependents)
 	}
 	delete(sorter.relations)
@@ -80,7 +80,7 @@ sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
 		}
 	}
 
-	for root in &sorted do for k, _ in relations[root].dependents {
+	for root in sorted do for k, _ in relations[root].dependents {
 		relation := &relations[k]
 		relation.dependencies -= 1
 		if relation.dependencies == 0 {

+ 4 - 4
core/crypto/blake/blake.odin

@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/blake2b/blake2b.odin

@@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _blake2.update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/blake2s/blake2s.odin

@@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _blake2.update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/gost/gost.odin

@@ -65,7 +65,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 4 - 4
core/crypto/groestl/groestl.odin

@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 15 - 15
core/crypto/haval/haval.odin

@@ -79,7 +79,7 @@ hash_stream_128_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -164,7 +164,7 @@ hash_stream_128_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -249,7 +249,7 @@ hash_stream_128_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -334,7 +334,7 @@ hash_stream_160_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -419,7 +419,7 @@ hash_stream_160_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -504,7 +504,7 @@ hash_stream_160_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -589,7 +589,7 @@ hash_stream_192_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -674,7 +674,7 @@ hash_stream_192_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -759,7 +759,7 @@ hash_stream_192_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -844,7 +844,7 @@ hash_stream_224_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -929,7 +929,7 @@ hash_stream_224_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -1014,7 +1014,7 @@ hash_stream_224_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -1099,7 +1099,7 @@ hash_stream_256_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -1184,7 +1184,7 @@ hash_stream_256_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])
@@ -1270,7 +1270,7 @@ hash_stream_256_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         ctx.str_len = u32(len(buf[:read]))
         if read > 0 {
             update(&ctx, buf[:read])

+ 4 - 4
core/crypto/jh/jh.odin

@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 4 - 4
core/crypto/keccak/keccak.odin

@@ -77,7 +77,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -159,7 +159,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -241,7 +241,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -323,7 +323,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/md2/md2.odin

@@ -64,7 +64,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
 	defer delete(buf)
 	read := 1
 	for read > 0 {
-	    read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
 	    if read > 0 {
 			update(&ctx, buf[:read])
 	    } 

+ 1 - 1
core/crypto/md4/md4.odin

@@ -68,7 +68,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/md5/md5.odin

@@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 4 - 4
core/crypto/ripemd/ripemd.odin

@@ -69,7 +69,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -145,7 +145,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -221,7 +221,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -297,7 +297,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/sha1/sha1.odin

@@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 4 - 4
core/crypto/sha2/sha2.odin

@@ -74,7 +74,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -153,7 +153,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -232,7 +232,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -311,7 +311,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 4 - 4
core/crypto/sha3/sha3.odin

@@ -73,7 +73,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -152,7 +152,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -231,7 +231,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -310,7 +310,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 

+ 2 - 2
core/crypto/shake/shake.odin

@@ -73,7 +73,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 
@@ -155,7 +155,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _sha3.update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/sm3/sm3.odin

@@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 2 - 2
core/crypto/streebog/streebog.odin

@@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 
@@ -146,7 +146,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             update(&ctx, buf[:read])
         } 

+ 3 - 3
core/crypto/tiger/tiger.odin

@@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 
@@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 
@@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 

+ 3 - 3
core/crypto/tiger2/tiger2.odin

@@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 
@@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 
@@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
     defer delete(buf)
     read := 1
     for read > 0 {
-        read, _ = s->impl_read(buf)
+        read, _ = io.read(s, buf)
         if read > 0 {
             _tiger.update(&ctx, buf[:read])
         } 

+ 1 - 1
core/crypto/whirlpool/whirlpool.odin

@@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
 	defer delete(buf)
 	read := 1
 	for read > 0 {
-	    read, _ = s->impl_read(buf)
+	    read, _ = io.read(s, buf)
 	    if read > 0 {
 			update(&ctx, buf[:read])
 	    } 

+ 3 - 3
core/dynlib/lib_js.odin

@@ -3,13 +3,13 @@
 package dynlib
 
 _load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
-	return
+	return nil, false
 }
 
 _unload_library :: proc(library: Library) -> bool {
-	return
+	return false
 }
 
 _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
-	return
+	return nil, false
 }

+ 2 - 2
core/encoding/hxa/read.odin

@@ -83,7 +83,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
 		meta_data = make([]Meta, int(capacity))
 		count := 0
 		defer meta_data = meta_data[:count]
-		for m in &meta_data {
+		for &m in meta_data {
 			m.name = read_name(r) or_return
 
 			type := read_value(r, Meta_Value_Type) or_return
@@ -116,7 +116,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
 		layer_count := 0
 		layers = make(Layer_Stack, stack_count)
 		defer layers = layers[:layer_count]
-		for layer in &layers {
+		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

+ 5 - 5
core/encoding/json/unmarshal.odin

@@ -72,7 +72,7 @@ unmarshal_string :: proc(data: string, ptr: ^$T, spec := DEFAULT_SPECIFICATION,
 @(private)
 assign_bool :: proc(val: any, b: bool) -> bool {
 	v := reflect.any_core(val)
-	switch dst in &v {
+	switch &dst in v {
 	case bool: dst = bool(b)
 	case b8:   dst = b8  (b)
 	case b16:  dst = b16 (b)
@@ -85,7 +85,7 @@ assign_bool :: proc(val: any, b: bool) -> bool {
 @(private)
 assign_int :: proc(val: any, i: $T) -> bool {
 	v := reflect.any_core(val)
-	switch dst in &v {
+	switch &dst in v {
 	case i8:      dst = i8     (i)
 	case i16:     dst = i16    (i)
 	case i16le:   dst = i16le  (i)
@@ -122,7 +122,7 @@ assign_int :: proc(val: any, i: $T) -> bool {
 @(private)
 assign_float :: proc(val: any, f: $T) -> bool {
 	v := reflect.any_core(val)
-	switch dst in &v {
+	switch &dst in v {
 	case f16:     dst = f16  (f)
 	case f16le:   dst = f16le(f)
 	case f16be:   dst = f16be(f)
@@ -150,7 +150,7 @@ assign_float :: proc(val: any, f: $T) -> bool {
 @(private)
 unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
 	val := val
-	switch dst in &val {
+	switch &dst in val {
 	case string:
 		dst = str
 		return true
@@ -215,7 +215,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 		}
 	}
 	
-	switch dst in &v {
+	switch &dst in v {
 	// Handle json.Value as an unknown type
 	case Value:
 		dst = parse_value(p) or_return

+ 9 - 9
core/fmt/fmt.odin

@@ -123,7 +123,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
 aprint :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str)
-	sbprint(buf=&str, args=args, sep=sep)
+	sbprint(&str, ..args, sep=sep)
 	return strings.to_string(str)
 }
 // 	Creates a formatted string with a newline character at the end
@@ -139,7 +139,7 @@ aprint :: proc(args: ..any, sep := " ") -> string {
 aprintln :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str)
-	sbprintln(buf=&str, args=args, sep=sep)
+	sbprintln(&str, ..args, sep=sep)
 	return strings.to_string(str)
 }
 // 	Creates a formatted string using a format string and arguments
@@ -171,7 +171,7 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
 tprint :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
-	sbprint(buf=&str, args=args, sep=sep)
+	sbprint(&str, ..args, sep=sep)
 	return strings.to_string(str)
 }
 // 	Creates a formatted string with a newline character at the end
@@ -187,7 +187,7 @@ tprint :: proc(args: ..any, sep := " ") -> string {
 tprintln :: proc(args: ..any, sep := " ") -> string {
 	str: strings.Builder
 	strings.builder_init(&str, context.temp_allocator)
-	sbprintln(buf=&str, args=args, sep=sep)
+	sbprintln(&str, ..args, sep=sep)
 	return strings.to_string(str)
 }
 // 	Creates a formatted string using a format string and arguments
@@ -217,7 +217,7 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
 //
 bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_bytes(buf[0:len(buf)])
-	return sbprint(buf=&sb, args=args, sep=sep)
+	return sbprint(&sb, ..args, sep=sep)
 }
 // Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer.
 //
@@ -230,7 +230,7 @@ bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 //
 bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
 	sb := strings.builder_from_bytes(buf[0:len(buf)])
-	return sbprintln(buf=&sb, args=args, sep=sep)
+	return sbprintln(&sb, ..args, sep=sep)
 }
 // Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
 //
@@ -327,7 +327,7 @@ ctprintf :: proc(format: string, args: ..any) -> cstring {
 // Returns: A formatted string
 //
 sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
-	wprint(w=strings.to_writer(buf), args=args, sep=sep)
+	wprint(strings.to_writer(buf), ..args, sep=sep)
 	return strings.to_string(buf^)
 }
 // Formats and writes to a strings.Builder buffer using the default print settings
@@ -340,7 +340,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 // Returns: The resulting formatted string
 //
 sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
-	wprintln(w=strings.to_writer(buf), args=args, sep=sep)
+	wprintln(strings.to_writer(buf), ..args, sep=sep)
 	return strings.to_string(buf^)
 }
 // Formats and writes to a strings.Builder buffer according to the specified format string
@@ -353,7 +353,7 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
 // Returns: The resulting formatted string
 //
 sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
-	wprintf(w=strings.to_writer(buf), fmt=fmt, args=args)
+	wprintf(strings.to_writer(buf), fmt, ..args)
 	return strings.to_string(buf^)
 }
 // Formats and writes to an io.Writer using the default print settings

+ 10 - 13
core/fmt/fmt_js.odin

@@ -11,27 +11,24 @@ foreign odin_env {
 }
 
 @(private="file")
-write_vtable := io.Stream_VTable{
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		fd := u32(uintptr(s.stream_data))
+write_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	if mode == .Write {
+		fd := u32(uintptr(stream_data))
 		write(fd, p)
-		return len(p), nil
-	},	
+		return i64(len(p)), nil
+	}
+	return 0, .Empty
 }
 
 @(private="file")
 stdout := io.Writer{
-	stream = {
-		stream_vtable = &write_vtable,
-		stream_data = rawptr(uintptr(1)),
-	},
+	procedure = write_stream_proc,
+	data      = rawptr(uintptr(1)),
 }
 @(private="file")
 stderr := io.Writer{
-	stream = {
-		stream_vtable = &write_vtable,
-		stream_data = rawptr(uintptr(2)),
-	},
+	procedure = write_stream_proc,
+	data      = rawptr(uintptr(2)),
 }
 
 // print formats using the default print settings and writes to stdout

+ 11 - 11
core/fmt/fmt_os.odin

@@ -12,9 +12,9 @@ fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
 
-	bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:])
+	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 	w := bufio.writer_to_writer(&b)
-	return wprint(w=w, args=args, sep=sep)
+	return wprint(w, ..args, sep=sep)
 }
 
 // fprintln formats using the default print settings and writes to fd
@@ -23,10 +23,10 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
 
-	bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:])
+	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 
 	w := bufio.writer_to_writer(&b)
-	return wprintln(w=w, args=args, sep=sep)
+	return wprintln(w, ..args, sep=sep)
 }
 // fprintf formats according to the specified format string and writes to fd
 fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
@@ -34,7 +34,7 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
 
-	bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:])
+	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 
 	w := bufio.writer_to_writer(&b)
 	return wprintf(w, fmt, ..args)
@@ -44,7 +44,7 @@ fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
 
-	bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:])
+	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 
 	w := bufio.writer_to_writer(&b)
 	return wprint_type(w, info)
@@ -54,22 +54,22 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
 	b: bufio.Writer
 	defer bufio.writer_flush(&b)
 
-	bufio.writer_init_with_buf(&b, {os.stream_from_handle(fd)}, buf[:])
+	bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
 
 	w := bufio.writer_to_writer(&b)
 	return wprint_typeid(w, id)
 }
 
 // print formats using the default print settings and writes to os.stdout
-print   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
+print   :: proc(args: ..any, sep := " ") -> int { return fprint(os.stdout, ..args, sep=sep) }
 // println formats using the default print settings and writes to os.stdout
-println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
+println :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stdout, ..args, sep=sep) }
 // printf formats according to the specified format string and writes to os.stdout
 printf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
 
 // eprint formats using the default print settings and writes to os.stderr
-eprint   :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
+eprint   :: proc(args: ..any, sep := " ") -> int { return fprint(os.stderr, ..args, sep=sep) }
 // eprintln formats using the default print settings and writes to os.stderr
-eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
+eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stderr, ..args, sep=sep) }
 // eprintf formats according to the specified format string and writes to os.stderr
 eprintf  :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }

+ 6 - 6
core/image/netpbm/netpbm.odin

@@ -161,18 +161,18 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
 		// convert from native endianness
 		if img.depth == 16 {
 			pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):])
-			for p in &pixels {
+			for &p in pixels {
 				p = u16be(transmute(u16) p)
 			}
 		} else if header.format in PFM {
 			if header.little_endian {
 				pixels := mem.slice_data_cast([]f32le, data.buf[len(header_buf):])
-				for p in &pixels {
+				for &p in pixels {
 					p = f32le(transmute(f32) p)
 				}
 			} else {
 				pixels := mem.slice_data_cast([]f32be, data.buf[len(header_buf):])
-				for p in &pixels {
+				for &p in pixels {
 					p = f32be(transmute(f32) p)
 				}
 			}
@@ -578,18 +578,18 @@ decode_image :: proc(img: ^Image, header: Header, data: []byte, allocator := con
 		if header.format in PFM {
 			pixels := mem.slice_data_cast([]f32, img.pixels.buf[:])
 			if header.little_endian {
-				for p in &pixels {
+				for &p in pixels {
 					p = f32(transmute(f32le) p)
 				}
 			} else {
-				for p in &pixels {
+				for &p in pixels {
 					p = f32(transmute(f32be) p)
 				}
 			}
 		} else {
 			if img.depth == 16 {
 				pixels := mem.slice_data_cast([]u16, img.pixels.buf[:])
-				for p in &pixels {
+				for &p in pixels {
 					p = u16(transmute(u16be) p)
 				}
 			}

+ 5 - 5
core/image/png/helpers.odin

@@ -36,7 +36,7 @@ destroy :: proc(img: ^Image) {
 	bytes.buffer_destroy(&img.pixels)
 
 	if v, ok := img.metadata.(^image.PNG_Info); ok {
-		for chunk in &v.chunks {
+		for chunk in v.chunks {
 			delete(chunk.data)
 		}
 		delete(v.chunks)
@@ -99,7 +99,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
 	case .tEXt:
 		ok = true
 
-		fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator)
+		fields := bytes.split(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]))
@@ -110,7 +110,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
 	case .zTXt:
 		ok = true
 
-		fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
+		fields := bytes.split_n(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
@@ -199,7 +199,7 @@ text_destroy :: proc(text: Text) {
 iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
 	runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
 
-	fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
+	fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
 
 	if len(fields[0]) < 1 || len(fields[0]) > 79 {
 		// Invalid profile name
@@ -263,7 +263,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
 	}
 	runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
 
-	fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
+	fields := bytes.split_n(c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
 	if len(fields) != 2 {
 		return
 	}

+ 30 - 74
core/io/conv.odin

@@ -1,124 +1,80 @@
 package io
 
 to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read == nil {
-		ok = false
-	}
+	r = s
+	ok = .Read in query(s)
 	return
 }
 to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write == nil {
-		ok = false
-	}
+	w = s
+	ok = .Write in query(s)
 	return
 }
 
 to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) #optional_ok {
-	c.stream = s
-	if s.stream_vtable == nil || s.impl_close == nil {
-		ok = false
-	}
+	c = s
+	ok = .Close in query(s)
 	return
 }
 to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) #optional_ok {
-	f.stream = s
-	if s.stream_vtable == nil || s.impl_flush == nil {
-		ok = false
-	}
+	f = s
+	ok = .Flush in query(s)
 	return
 }
 to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) #optional_ok {
-	seeker.stream = s
-	if s.stream_vtable == nil || s.impl_seek == nil {
-		ok = false
-	}
+	seeker = s
+	ok = .Seek in query(s)
 	return
 }
 
 to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
-		ok = false
-	}
+	r = s
+	ok = query(s) >= {.Read, .Write}
 	return
 }
 to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
-		ok = false
-	}
+	r = s
+	ok = query(s) >= {.Read, .Close}
 	return
 }
 to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
-		ok = false
-	}
+	r = s
+	ok = query(s) >= {.Read, .Write, .Close}
 	return
 }
 to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
-		ok = false
-	}
+	r = s
+	ok = query(s) >= {.Read, .Write, .Seek}
 	return
 }
 to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
-		ok = false
-	}
+	w = s
+	ok = query(s) >= {.Write, .Flush}
 	return
 }
 to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
-		ok = false
-	}
+	w = s
+	ok = query(s) >= {.Write, .Flush, .Close}
 	return
 }
 
 to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read_at == nil {
-		ok = false
-	}
+	r = s
+	ok = query(s) >= {.Read_At}
 	return
 }
 to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write_at == nil {
-		ok = false
-	}
-	return
-}
-to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) #optional_ok {
-	r.stream = s
-	if s.stream_vtable == nil || s.impl_read_from == nil {
-		ok = false
-	}
-	return
-}
-to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write_to == nil {
-		ok = false
-	}
+	w = s
+	ok = query(s) >= {.Write_At}
 	return
 }
 to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
-		ok = false
-	}
+	w = s
+	ok = query(s) >= {.Write, .Close}
 	return
 }
 to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #optional_ok {
-	w.stream = s
-	if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
-		ok = false
-	}
+	w = s
+	ok = query(s) >= {.Write, .Seek}
 	return
 }

+ 144 - 296
core/io/io.odin

@@ -53,137 +53,106 @@ 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
-
-
-Stream :: struct {
-	using stream_vtable: ^Stream_VTable,
-	stream_data:         rawptr,
+Stream_Mode :: enum {
+	Close,
+	Flush,
+	Read,
+	Read_At,
+	Write,
+	Write_At,
+	Seek,
+	Size,
+	Destroy,
+	Query, // query what modes are available
 }
-Stream_VTable :: struct {
-	impl_close: Close_Proc,
-	impl_flush: Flush_Proc,
-
-	impl_seek:  Seek_Proc,
-	impl_size:  Size_Proc,
 
-	impl_read:      Read_Proc,
-	impl_read_at:   Read_At_Proc,
-	impl_read_byte: Read_Byte_Proc,
-	impl_read_rune: Read_Rune_Proc,
-	impl_write_to:  Write_To_Proc,
+Stream_Mode_Set :: distinct bit_set[Stream_Mode; i64]
 
-	impl_write:      Write_Proc,
-	impl_write_at:   Write_At_Proc,
-	impl_write_byte: Write_Byte_Proc,
-	impl_write_rune: Write_Rune_Proc,
-	impl_read_from:  Read_From_Proc,
+Stream_Proc :: #type proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
 
-	impl_unread_byte: Unread_Byte_Proc,
-	impl_unread_rune: Unread_Rune_Proc,
-
-	impl_destroy: Destroy_Proc,
+Stream :: struct {
+	procedure: Stream_Proc,
+	data:      rawptr,
 }
 
+Reader             :: Stream
+Writer             :: Stream
+Closer             :: Stream
+Flusher            :: Stream
+Seeker             :: 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        :: Stream
+Read_Closer        :: Stream
+Read_Write_Closer  :: Stream
+Read_Write_Seeker  :: 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       :: Stream
+Write_Seeker       :: Stream
+Write_Flusher      :: Stream
+Write_Flush_Closer :: 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          :: Stream
+Writer_At          :: Stream
 
 
-destroy :: proc(s: Stream) -> Error {
-	close_err := close({s})
-	if s.stream_vtable != nil && s.impl_destroy != nil {
-		return s->impl_destroy()
+destroy :: proc(s: Stream) -> (err: Error) {
+	_ = flush(s)
+	_ = close(s)
+	if s.procedure != nil {
+		_, err = s.procedure(s.data, .Destroy, nil, 0, nil)
+	} else {
+		err = .Empty
 	}
-	if close_err != .None {
-		return close_err
+	return
+}
+
+query :: proc(s: Stream) -> (set: Stream_Mode_Set) {
+	if s.procedure != nil {
+		n, _ := s.procedure(s.data, .Query, nil, 0, nil)
+		set = transmute(Stream_Mode_Set)n
+		if set != nil {
+			set += {.Query}
+		}
 	}
-	return .Empty
+	return
+}
+
+query_utility :: #force_inline proc "contextless" (set: Stream_Mode_Set) -> (n: i64, err: Error) {
+	return transmute(i64)set, nil
 }
 
+_i64_err :: #force_inline proc "contextless" (n: int, err: Error) -> (i64, Error) {
+	return i64(n), err
+}
+
+
 // read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
 //
 // When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
 // bytes read along with the error.
 read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
-	if s.stream_vtable != nil {
-		if s.impl_read != nil {
-			n, err = s->impl_read(p)
-			if n_read != nil {
-				n_read^ += n
-			}
-			return
-		} else if s.impl_read_byte != nil {
-			bytes_read := 0
-			defer if n_read != nil {
-				n_read^ += bytes_read
-			}
-			for _, i in p {
-				p[i] = s->impl_read_byte() or_return
-				bytes_read += 1
-			}
-			return
-		}
+	if s.procedure != nil {
+		n64: i64
+		n64, err = s.procedure(s.data, .Read, p, 0, nil)
+		n = int(n64)
+		if n_read != nil { n_read^ += n }
+	} else {
+		err = .Empty
 	}
-	return 0, .Empty
+	return
 }
 
 // write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
 write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
-	if s.stream_vtable != nil {
-		if s.impl_write != nil {
-			n, err = s->impl_write(p)
-			if n_written != nil {
-				n_written^ += n
-			}
-			return
-		} else if s.impl_write_byte != nil {
-			bytes_written := 0
-			defer if n_written != nil {
-				n_written^ += bytes_written
-			}
-			for c in p {
-				s->impl_write_byte(c) or_return
-				bytes_written += 1
-			}
-			return
-		}
+	if s.procedure != nil {
+		n64: i64
+		n64, err = s.procedure(s.data, .Write, p, 0, nil)
+		n = int(n64)
+		if n_written != nil { n_written^ += n }
+	} else {
+		err = .Empty
 	}
-	return 0, .Empty
+	return
 }
 
 // seek sets the offset of the next read or write to offset.
@@ -194,57 +163,45 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
 //
 // seek returns the new offset to the start of the file/stream, and any error if occurred.
 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)
+	if s.procedure != nil {
+		n, err = s.procedure(s.data, .Seek, nil, offset, whence)
+	} else {
+		err = .Empty
 	}
-	return 0, .Empty
+	return
 }
 
 // The behaviour of close after the first call is stream implementation defined.
 // Different streams may document their own behaviour.
-close :: proc(s: Closer) -> Error {
-	if s.stream_vtable != nil && s.impl_close != nil {
-		return s->impl_close()
+close :: proc(s: Closer) -> (err: Error) {
+	if s.procedure != nil {
+		_, err = s.procedure(s.data, .Close, nil, 0, nil)
 	}
-	// Instead of .Empty, .None is fine in this case
-	return .None
+	return
 }
 
-flush :: proc(s: Flusher) -> Error {
-	if s.stream_vtable != nil && s.impl_flush != nil {
-		return s->impl_flush()
+flush :: proc(s: Flusher) -> (err: Error) {
+	if s.procedure != nil {
+		_, err = s.procedure(s.data, .Flush, nil, 0, nil)
 	}
-	// Instead of .Empty, .None is fine in this case
-	return .None
+	return
 }
 
 // size returns the size of the stream. If the stream does not support querying its size, 0 will be returned.
-size :: proc(s: Stream) -> i64 {
-	if s.stream_vtable == nil {
-		return 0
-	}
-	if s.impl_size != nil {
-		return s->impl_size()
-	}
-	if s.impl_seek == nil {
-		return 0
-	}
-
-	curr, end: i64
-	err: Error
-	if curr, err = s->impl_seek(0, .Current); err != nil {
-		return 0
-	}
-
-	if end, err = s->impl_seek(0, .End); err != nil {
-		return 0
-	}
-
-	if _, err = s->impl_seek(curr, .Start); err != nil {
-		return 0
+size :: proc(s: Stream) -> (n: i64, err: Error) {
+	if s.procedure != nil {
+		n, err = s.procedure(s.data, .Size, nil, 0, nil)
+		if err == .Empty {
+			n = 0
+			curr := seek(s, 0, .Current) or_return
+			end  := seek(s, 0, .End)     or_return
+			seek(s, curr, .Start)        or_return
+			n = end
+		}
+	} else {
+		err = .Empty
 	}
-
-	return end
+	return
 }
 
 
@@ -256,29 +213,24 @@ size :: proc(s: Stream) -> i64 {
 //
 // If n == len(p), err may be either nil or .EOF
 read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) {
-	defer if n_read != nil {
-		n_read^ += n
-	}
-	
-	if r.stream_vtable == nil {
-		return 0, .Empty
-	}
-	if r.impl_read_at != nil {
-		return r->impl_read_at(p, offset)
-	}
-	if r.impl_seek == nil || r.impl_read == nil {
-		return 0, .Empty
-	}
-
-	curr_offset := r->impl_seek(offset, .Current) or_return
-
-	n, err = r->impl_read(p)
-	_, err1 := r->impl_seek(curr_offset, .Start)
-	if err1 != nil && err == nil {
-		err = err1
+	if r.procedure != nil {
+		n64: i64
+		n64, err = r.procedure(r.data, .Read_At, p, offset, nil)
+		if err != .Empty {
+			n = int(n64)
+		} else {
+			curr := seek(r, offset, .Current) or_return
+			n, err = read(r, p)
+			_, err1 := seek(r, curr, .Start)
+			if err1 != nil && err == nil {
+				err = err1
+			}
+		}
+		if n_read != nil { n_read^ += n }
+	} else {
+		err = .Empty
 	}
 	return
-
 }
 
 // write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
@@ -287,97 +239,39 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
 // If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying
 // seek offset.
 write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) {
-	defer if n_written != nil {
-		n_written^ += n
-	}
-	
-	if w.stream_vtable == nil {
-		return 0, .Empty
-	}
-	if w.impl_write_at != nil {
-		return w->impl_write_at(p, offset)
-	}
-	if w.impl_seek == nil || w.impl_write == nil {
-		return 0, .Empty
-	}
-
-	curr_offset: i64
-	curr_offset, err = w->impl_seek(offset, .Current)
-	if err != nil {
-		return 0, err
-	}
-
-	n, err = w->impl_write(p)
-	_, err1 := w->impl_seek(curr_offset, .Start)
-	if err1 != nil && err == nil {
-		err = err1
+	if w.procedure != nil {
+		n64: i64
+		n64, err = w.procedure(w.data, .Write_At, p, offset, nil)
+		if err != .Empty {
+			n = int(n64)
+		} else {
+			curr := seek(w, offset, .Current) or_return
+			n, err = write(w, p)
+			_, err1 := seek(w, curr, .Start)
+			if err1 != nil && err == nil {
+				err = err1
+			}
+		}
+		if n_written != nil { n_written^ += n }
+	} else {
+		err = .Empty
 	}
 	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
-	}
-	if r.impl_write_to != nil {
-		return r->impl_write_to(w)
-	}
-	return 0, .Empty
-}
-read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
-	if r.stream_vtable == nil || w.stream_vtable == nil {
-		return 0, .Empty
-	}
-	if r.impl_read_from != nil {
-		return w->impl_read_from(r)
-	}
-	return 0, .Empty
-}
-
-
 // read_byte reads and returns the next byte from r.
 read_byte :: proc(r: Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
-	defer if err == nil && n_read != nil {
-		n_read^ += 1
-	}
-	
-	if r.stream_vtable == nil {
-		return 0, .Empty
-	}
-	if r.impl_read_byte != nil {
-		return r->impl_read_byte()
-	}
-	if r.impl_read == nil {
-		return 0, .Empty
-	}
-
 	buf: [1]byte
-	_, err = r->impl_read(buf[:])
-	return buf[0], err
+	_, err = read(r, buf[:], n_read)
+	b = buf[0]
+	return
 }
 
 write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
-	return _write_byte(auto_cast w, c, n_written)
-}
-
-@(private)
-_write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
-	defer if err == nil && n_written != nil {
-		n_written^ += 1
-	}
-	if w.stream_vtable == nil {
-		return .Empty
-	}
-	if w.impl_write_byte != nil {
-		return w->impl_write_byte(c)
-	}
-	if w.impl_write == nil {
-		return .Empty
-	}
-
-	b := [1]byte{c}
-	_, err = w->impl_write(b[:])
-	return err
+	buf: [1]byte
+	buf[0] = c
+	write(w, buf[:], n_written) or_return
+	return nil
 }
 
 // read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
@@ -385,19 +279,9 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
 	defer if err == nil && n_read != nil {
 		n_read^ += size
 	}
-	if br.stream_vtable == nil {
-		return 0, 0, .Empty
-	}
-	if br.impl_read_rune != nil {
-		return br->impl_read_rune()
-	}
-	if br.impl_read == nil {
-		return 0, 0, .Empty
-	}
 
 	b: [utf8.UTF_MAX]byte
-	_, err = br->impl_read(b[:1])
-	
+	_, err = read(br, b[:1])
 
 	s0 := b[0]
 	ch = rune(s0)
@@ -415,7 +299,7 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
 		return
 	}
 	sz := int(x&7)
-	size, err = br->impl_read(b[1:sz])
+	size, err = read(br, b[1:sz])
 	if err != nil || size+1 < sz {
 		ch = utf8.RUNE_ERROR
 		return
@@ -425,28 +309,6 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
 	return
 }
 
-unread_byte :: proc(s: Stream) -> Error {
-	if s.stream_vtable == nil {
-		return .Empty
-	}
-	if s.impl_unread_byte != nil {
-		return s->impl_unread_byte()
-	}
-	if s.impl_seek != nil {
-		_, err := s->impl_seek(-1, .Current)
-		return err
-	}
-
-	return .Empty
-}
-unread_rune :: proc(s: Writer) -> Error {
-	if s.stream_vtable != nil && s.impl_unread_rune != nil {
-		return s->impl_unread_rune()
-	}
-	return .Empty
-}
-
-
 // write_string writes the contents of the string s to w.
 write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) {
 	return write(s, transmute([]byte)str, n_written)
@@ -457,14 +319,6 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
 	defer if err == nil && n_written != nil {
 		n_written^ += size
 	}
-	
-	if s.stream_vtable == nil {
-		return 0, .Empty
-	}
-	if s.impl_write_rune != nil {
-		return s->impl_write_rune(r)
-	}
-
 	if r < utf8.RUNE_SELF {
 		err = write_byte(s, byte(r))
 		if err == nil {
@@ -542,21 +396,15 @@ copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
 
 @(private)
 _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
-	if dst.stream_vtable == nil || src.stream_vtable == nil {
+	if dst.procedure == nil || src.procedure == nil {
 		return 0, .Empty
 	}
-	if src.impl_write_to != nil {
-		return src->impl_write_to(dst)
-	}
-	if src.impl_read_from != nil {
-		return dst->impl_read_from(src)
-	}
 	buf := buf
 	if buf == nil {
 		DEFAULT_SIZE :: 4 * 1024
 		size := DEFAULT_SIZE
-		if src.stream_vtable == _limited_reader_vtable {
-			l := (^Limited_Reader)(src.stream_data)
+		if src.procedure == _limited_reader_proc {
+			l := (^Limited_Reader)(src.data)
 			if i64(size) > l.n {
 				if l.n < 1 {
 					size = 1

+ 47 - 39
core/io/multi.odin

@@ -5,33 +5,37 @@ 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)
-		for len(mr.readers) > 0 {
-			r := mr.readers[0]
-			n, err = read(r, p)
-			if err == .EOF {
-				ordered_remove(&mr.readers, 0)
-			}
-			if n > 0 || err != .EOF {
-				if err == .EOF && len(mr.readers) > 0 {
-					// Don't return EOF yet, more readers remain
-					err = nil
-				}
-				return
+_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	if mode == .Query {
+		return query_utility({.Read, .Query})
+	} else if mode != .Read {
+		return 0, .Empty
+	}
+	mr := (^Multi_Reader)(stream_data)
+	for len(mr.readers) > 0 {
+		r := mr.readers[0]
+		n, err = _i64_err(read(r, p))
+		if err == .EOF {
+			ordered_remove(&mr.readers, 0)
+		}
+		if n > 0 || err != .EOF {
+			if err == .EOF && len(mr.readers) > 0 {
+				// Don't return EOF yet, more readers remain
+				err = nil
 			}
+			return
 		}
-		return 0, .EOF
-	},
+	}
+	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)
 
 	for w in readers {
-		if w.stream_vtable == _multi_reader_vtable {
-			other := (^Multi_Reader)(w.stream_data)
+		if w.procedure == _multi_reader_proc {
+			other := (^Multi_Reader)(w.data)
 			append(&all_readers, ..other.readers[:])
 		} else {
 			append(&all_readers, w)
@@ -40,8 +44,8 @@ multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := con
 
 	mr.readers = all_readers
 
-	r.stream_vtable = _multi_reader_vtable
-	r.stream_data = mr
+	r.procedure = _multi_reader_proc
+	r.data = mr
 	return
 }
 
@@ -55,38 +59,42 @@ 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)
-		for w in mw.writers {
-			n, err = write(w, p)
-			if err != nil {
-				return
-			}
-			if n != len(p) {
-				err = .Short_Write
-				return
-			}
+_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	if mode == .Query {
+		return query_utility({.Write, .Query})
+	} else if mode != .Write {
+		return 0, .Empty
+	}
+	mw := (^Multi_Writer)(stream_data)
+	for w in mw.writers {
+		n, err = _i64_err(write(w, p))
+		if err != nil {
+			return
+		}
+		if n != i64(len(p)) {
+			err = .Short_Write
+			return
 		}
+	}
 
-		return len(p), nil
-	},
+	return i64(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)
 
 	for w in writers {
-		if w.stream_vtable == _multi_writer_vtable {
-			other := (^Multi_Writer)(w.stream_data)
+		if w.procedure == _multi_writer_proc {
+			other := (^Multi_Writer)(w.data)
 			append(&mw.writers, ..other.writers[:])
 		} else {
 			append(&mw.writers, w)
 		}
 	}
 
-	out.stream_vtable = _multi_writer_vtable
-	out.stream_data = mw
+	out.procedure = _multi_writer_proc
+	out.data = mw
 	return
 }
 

+ 43 - 36
core/io/util.odin

@@ -292,17 +292,21 @@ 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)
+_tee_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	t := (^Tee_Reader)(stream_data)
+	#partial switch mode {
+	case .Read:
+		n, err = _i64_err(read(t.r, p))
 		if n > 0 {
 			if wn, werr := write(t.w, p[:n]); werr != nil {
-				return wn, werr
+				return i64(wn), werr
 			}
 		}
 		return
-	},
+	case .Query:
+		return query_utility({.Read, .Query})
+	}
+	return 0, .Empty
 }
 
 // tee_reader_init returns a Reader that writes to 'w' what it reads from 'r'
@@ -317,8 +321,8 @@ tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := conte
 }
 
 tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) {
-	r.stream_data = t
-	r.stream_vtable = _tee_reader_vtable
+	r.data = t
+	r.procedure = _tee_reader_proc
 	return
 }
 
@@ -332,9 +336,10 @@ 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)
+_limited_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	l := (^Limited_Reader)(stream_data)
+	#partial switch mode {
+	case .Read:
 		if l.n <= 0 {
 			return 0, .EOF
 		}
@@ -342,10 +347,13 @@ _limited_reader_vtable := &Stream_VTable{
 		if i64(len(p)) > l.n {
 			p = p[0:l.n]
 		}
-		n, err = read(l.r, p)
+		n, err = _i64_err(read(l.r, p))
 		l.n -= i64(n)
 		return
-	},
+	case .Query:
+		return query_utility({.Read, .Query})
+	}
+	return 0, .Empty
 }
 
 limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
@@ -355,8 +363,8 @@ limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
 }
 
 limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
-	r.stream_vtable = _limited_reader_vtable
-	r.stream_data = l
+	r.procedure = _limited_reader_proc
+	r.data = l
 	return
 }
 
@@ -375,15 +383,16 @@ section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64)
 	return
 }
 section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
-	out.stream_data = s
-	out.stream_vtable = _section_reader_vtable
+	out.data = s
+	out.procedure = _section_reader_proc
 	return
 }
 
 @(private)
-_section_reader_vtable := &Stream_VTable{
-	impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
-		s := (^Section_Reader)(stream.stream_data)
+_section_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+	s := (^Section_Reader)(stream_data)
+	#partial switch mode {
+	case .Read:
 		if s.off >= s.limit {
 			return 0, .EOF
 		}
@@ -391,13 +400,11 @@ _section_reader_vtable := &Stream_VTable{
 		if max := s.limit - s.off; i64(len(p)) > max {
 			p = p[0:max]
 		}
-		n, err = read_at(s.r, p, s.off)
+		n, err = _i64_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
+	case .Read_At:
+		p, off := p, offset
 
 		if off < 0 || off >= s.limit - s.base {
 			return 0, .EOF
@@ -405,17 +412,15 @@ _section_reader_vtable := &Stream_VTable{
 		off += s.base
 		if max := s.limit - off; i64(len(p)) > max {
 			p = p[0:max]
-			n, err = read_at(s.r, p, off)
+			n, err = _i64_err(read_at(s.r, p, off))
 			if err == nil {
 				err = .EOF
 			}
 			return
 		}
-		return read_at(s.r, p, off)
-	},
-	impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
-		s := (^Section_Reader)(stream.stream_data)
+		return _i64_err(read_at(s.r, p, off))
 
+	case .Seek:
 		offset := offset
 		switch whence {
 		case:
@@ -433,10 +438,12 @@ _section_reader_vtable := &Stream_VTable{
 		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
-	},
-}
+	case .Size:
+		n = s.limit - s.base
+		return
+	case .Query:
+		return query_utility({.Read, .Read_At, .Seek, .Size, .Query})
+	}
+	return 0, nil
 
+}

+ 13 - 13
core/log/log.odin

@@ -76,43 +76,43 @@ nil_logger :: proc() -> Logger {
 }
 
 debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
-	logf(level=.Debug,   fmt_str=fmt_str, args=args, location=location)
+	logf(.Debug,   fmt_str, ..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(.Info,    fmt_str, ..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(.Warning, fmt_str, ..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(.Error,   fmt_str, ..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(.Fatal,   fmt_str, ..args, location=location)
 }
 
 debug :: proc(args: ..any, sep := " ", location := #caller_location) {
-	log(level=.Debug,   args=args, sep=sep, location=location)
+	log(.Debug,   ..args, sep=sep, location=location)
 }
 info  :: proc(args: ..any, sep := " ", location := #caller_location) {
-	log(level=.Info,    args=args, sep=sep, location=location)
+	log(.Info,    ..args, sep=sep, location=location)
 }
 warn  :: proc(args: ..any, sep := " ", location := #caller_location) {
-	log(level=.Warning, args=args, sep=sep, location=location)
+	log(.Warning, ..args, sep=sep, location=location)
 }
 error :: proc(args: ..any, sep := " ", location := #caller_location) {
-	log(level=.Error,   args=args, sep=sep, location=location)
+	log(.Error,   ..args, sep=sep, location=location)
 }
 fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
-	log(level=.Fatal,   args=args, sep=sep, location=location)
+	log(.Fatal,   ..args, sep=sep, location=location)
 }
 
 panic :: proc(args: ..any, location := #caller_location) -> ! {
-	log(level=.Fatal, args=args, location=location)
+	log(.Fatal, ..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)
+	logf(.Fatal, fmt_str, ..args, location=location)
 	runtime.panic("log.panicf", location)
 }
 
@@ -127,7 +127,7 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location)
 	if level < logger.lowest_level {
 		return
 	}
-	str := fmt.tprint(args=args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
+	str := fmt.tprint(..args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
 	logger.procedure(logger.data, level, str, logger.options, location)
 }
 

+ 27 - 27
core/log/log_allocator.odin

@@ -38,60 +38,60 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
 		switch mode {
 		case .Alloc:
 			logf(
-				level=la.level,
-				fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
-				args = {la.prefix, padding, size, alignment},
+				la.level,
+				"%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
+				la.prefix, padding, size, alignment,
 				location = location,
 			)
 		case .Alloc_Non_Zeroed:
 			logf(
-				level=la.level,
-				fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
-				args = {la.prefix, padding, size, alignment},
+				la.level,
+				"%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
+				la.prefix, padding, size, alignment,
 				location = location,
 			)
 		case .Free:
 			if old_size != 0 {
 				logf(
-					level=la.level,
-					fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
-					args = {la.prefix, padding, old_memory, old_size},
+					la.level,
+					"%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
+					la.prefix, padding, old_memory, old_size,
 					location = location,
 				)
 			} else {
 				logf(
-					level=la.level,
-					fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
-					args = {la.prefix, padding, old_memory},
+					la.level,
+					"%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
+					la.prefix, padding, old_memory,
 					location = location,
 				)
 			}
 		case .Free_All:
 			logf(
-				level=la.level,
-				fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
-				args = {la.prefix, padding},
+				la.level,
+				"%s%s<<< ALLOCATOR(mode=.Free_All)",
+				la.prefix, padding,
 				location = location,
 			)
 		case .Resize:
 			logf(
-				level=la.level,
-				fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
-				args = {la.prefix, padding, old_memory, old_size, size, alignment},
+				la.level,
+				"%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
+				la.prefix, padding, old_memory, old_size, size, alignment,
 				location = location,
 			)
 		case .Query_Features:
 			logf(
-				level=la.level,
-				fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
-				args = {la.prefix, padding},
+				la.level,
+				"%s%ALLOCATOR(mode=.Query_Features)",
+				la.prefix, padding,
 				location = location,
 			)
 		case .Query_Info:
 			logf(
-				level=la.level,
-				fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
-				args = {la.prefix, padding},
+				la.level,
+				"%s%ALLOCATOR(mode=.Query_Info)",
+				la.prefix, padding,
 				location = location,
 			)
 		}
@@ -103,9 +103,9 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
 		defer la.locked = false
 		if err != nil {
 			logf(
-				level=la.level,
-				fmt_str = "%s%ALLOCATOR ERROR=%v",
-				args = {la.prefix, padding, error},
+				la.level,
+				"%s%ALLOCATOR ERROR=%v",
+				la.prefix, padding, error,
 				location = location,
 			)
 		}

+ 3 - 3
core/math/big/helpers.odin

@@ -19,7 +19,7 @@ import rnd "core:math/rand"
 int_destroy :: proc(integers: ..^Int) {
 	integers := integers
 
-	for a in &integers {
+	for a in integers {
 		assert_if_nil(a)
 	}
 	#force_inline internal_int_destroy(..integers)
@@ -408,7 +408,7 @@ clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocato
 	args := args
 	assert_if_nil(..args)
 
-	for i in &args {
+	for i in args {
 		#force_inline internal_clear_if_uninitialized_single(i, allocator) or_return
 	}
 	return err
@@ -435,7 +435,7 @@ int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err
 	assert_if_nil(..integers)
 
 	integers := integers
-	for a in &integers {
+	for a in integers {
 		#force_inline internal_clear(a, true, allocator) or_return
 	}
 	return nil

+ 2 - 2
core/math/big/internal.odin

@@ -1857,7 +1857,7 @@ internal_root_n :: proc { internal_int_root_n, }
 internal_int_destroy :: proc(integers: ..^Int) {
 	integers := integers
 
-	for a in &integers {
+	for &a in integers {
 		if internal_int_allocated_cap(a) > 0 {
 			mem.zero_slice(a.digit[:])
 			free(&a.digit[0])
@@ -2909,7 +2909,7 @@ internal_int_init_multi :: proc(integers: ..^Int, allocator := context.allocator
 	context.allocator = allocator
 
 	integers := integers
-	for a in &integers {
+	for a in integers {
 		internal_clear(a) or_return
 	}
 	return nil

+ 1 - 1
core/math/big/radix.odin

@@ -429,7 +429,7 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(
 		len  = l,
 	}
 
-	ok := os.write_entire_file(name=filename, data=data, truncate=true)
+	ok := os.write_entire_file(filename, data, truncate=true)
 	return nil if ok else .Cannot_Write_File
 }
 

+ 1 - 1
core/math/big/rat.odin

@@ -137,7 +137,7 @@ rat_copy :: proc(dst, src: ^Rat, minimize := false, allocator := context.allocat
 internal_rat_destroy :: proc(rationals: ..^Rat) {
 	rationals := rationals
 
-	for z in &rationals {
+	for &z in rationals {
 		internal_int_destroy(&z.a, &z.b)
 	}
 }

+ 513 - 0
core/math/cmplx/cmplx.odin

@@ -0,0 +1,513 @@
+package math_cmplx
+
+import "core:builtin"
+import "core:math"
+
+// The original C code, the long comment, and the constants
+// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
+// The go code is a simplified version of the original C.
+//
+// Cephes Math Library Release 2.8:  June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+//    Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+//   The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+//   Stephen L. Moshier
+//   [email protected]
+
+abs  :: builtin.abs
+conj :: builtin.conj
+real :: builtin.real
+imag :: builtin.imag
+jmag :: builtin.jmag
+kmag :: builtin.kmag
+
+
+sin :: proc{
+	sin_complex128,
+}
+cos :: proc{
+	cos_complex128,
+}
+tan :: proc{
+	tan_complex128,
+}
+cot :: proc{
+	cot_complex128,
+}
+
+
+sinh :: proc{
+	sinh_complex128,
+}
+cosh :: proc{
+	cosh_complex128,
+}
+tanh :: proc{
+	tanh_complex128,
+}
+
+
+
+// sqrt returns the square root of x.
+// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
+sqrt :: proc{
+	sqrt_complex32,
+	sqrt_complex64,
+	sqrt_complex128,
+}
+ln :: proc{
+	ln_complex32,
+	ln_complex64,
+	ln_complex128,
+}
+log10 :: proc{
+	log10_complex32,
+	log10_complex64,
+	log10_complex128,
+}
+
+exp :: proc{
+	exp_complex32,
+	exp_complex64,
+	exp_complex128,
+}
+
+pow :: proc{
+	pow_complex32,
+	pow_complex64,
+	pow_complex128,
+}
+
+phase :: proc{
+	phase_complex32,
+	phase_complex64,
+	phase_complex128,
+}
+
+polar :: proc{
+	polar_complex32,
+	polar_complex64,
+	polar_complex128,
+}
+
+is_inf :: proc{
+	is_inf_complex32,
+	is_inf_complex64,
+	is_inf_complex128,
+}
+
+is_nan :: proc{
+	is_nan_complex32,
+	is_nan_complex64,
+	is_nan_complex128,
+}
+
+
+
+// sqrt_complex32 returns the square root of x.
+// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
+sqrt_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return complex32(sqrt_complex128(complex128(x)))
+}
+
+// sqrt_complex64 returns the square root of x.
+// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
+sqrt_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return complex64(sqrt_complex128(complex128(x)))
+}
+
+
+// sqrt_complex128 returns the square root of x.
+// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
+sqrt_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// The original C code, the long comment, and the constants
+	// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
+	// The go code is a simplified version of the original C.
+	//
+	// Cephes Math Library Release 2.8:  June, 2000
+	// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+	//
+	// The readme file at http://netlib.sandia.gov/cephes/ says:
+	//    Some software in this archive may be from the book _Methods and
+	// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+	// International, 1989) or from the Cephes Mathematical Library, a
+	// commercial product. In either event, it is copyrighted by the author.
+	// What you see here may be used freely but it comes with no support or
+	// guarantee.
+	//
+	//   The two known misprints in the book are repaired here in the
+	// source listings for the gamma function and the incomplete beta
+	// integral.
+	//
+	//   Stephen L. Moshier
+	//   [email protected]
+
+	// Complex square root
+	//
+	// DESCRIPTION:
+	//
+	// If z = x + iy,  r = |z|, then
+	//
+	//                       1/2
+	// Re w  =  [ (r + x)/2 ]   ,
+	//
+	//                       1/2
+	// Im w  =  [ (r - x)/2 ]   .
+	//
+	// Cancellation error in r-x or r+x is avoided by using the
+	// identity  2 Re w Im w  =  y.
+	//
+	// Note that -w is also a square root of z. The root chosen
+	// is always in the right half plane and Im w has the same sign as y.
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    DEC       -10,+10     25000       3.2e-17     9.6e-18
+	//    IEEE      -10,+10   1,000,000     2.9e-16     6.1e-17
+
+	if imag(x) == 0 {
+		// Ensure that imag(r) has the same sign as imag(x) for imag(x) == signed zero.
+		if real(x) == 0 {
+			return complex(0, imag(x))
+		}
+		if real(x) < 0 {
+			return complex(0, math.copy_sign(math.sqrt(-real(x)), imag(x)))
+		}
+		return complex(math.sqrt(real(x)), imag(x))
+	} else if math.is_inf(imag(x), 0) {
+		return complex(math.inf_f64(1.0), imag(x))
+	}
+	if real(x) == 0 {
+		if imag(x) < 0 {
+			r := math.sqrt(-0.5 * imag(x))
+			return complex(r, -r)
+		}
+		r := math.sqrt(0.5 * imag(x))
+		return complex(r, r)
+	}
+	a := real(x)
+	b := imag(x)
+	scale: f64
+	// Rescale to avoid internal overflow or underflow.
+	if abs(a) > 4 || abs(b) > 4 {
+		a *= 0.25
+		b *= 0.25
+		scale = 2
+	} else {
+		a *= 1.8014398509481984e16 // 2**54
+		b *= 1.8014398509481984e16
+		scale = 7.450580596923828125e-9 // 2**-27
+	}
+	r := math.hypot(a, b)
+	t: f64
+	if a > 0 {
+		t = math.sqrt(0.5*r + 0.5*a)
+		r = scale * abs((0.5*b)/t)
+		t *= scale
+	} else {
+		r = math.sqrt(0.5*r - 0.5*a)
+		t = scale * abs((0.5*b)/r)
+		r *= scale
+	}
+	if b < 0 {
+		return complex(t, -r)
+	}
+	return complex(t, r)
+}
+
+ln_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return complex(math.ln(abs(x)), phase(x))
+}
+ln_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return complex(math.ln(abs(x)), phase(x))
+}
+ln_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	return complex(math.ln(abs(x)), phase(x))
+}
+
+
+exp_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	switch re, im := real(x), imag(x); {
+	case math.is_inf(re, 0):
+		switch {
+		case re > 0 && im == 0:
+			return x
+		case math.is_inf(im, 0) || math.is_nan(im):
+			if re < 0 {
+				return complex(0, math.copy_sign(0, im))
+			} else {
+				return complex(math.inf_f64(1.0), math.nan_f64())
+			}
+		}
+	case math.is_nan(re):
+		if im == 0 {
+			return complex(math.nan_f16(), im)
+		}
+	}
+	r := math.exp(real(x))
+	s, c := math.sincos(imag(x))
+	return complex(r*c, r*s)
+}
+exp_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	switch re, im := real(x), imag(x); {
+	case math.is_inf(re, 0):
+		switch {
+		case re > 0 && im == 0:
+			return x
+		case math.is_inf(im, 0) || math.is_nan(im):
+			if re < 0 {
+				return complex(0, math.copy_sign(0, im))
+			} else {
+				return complex(math.inf_f64(1.0), math.nan_f64())
+			}
+		}
+	case math.is_nan(re):
+		if im == 0 {
+			return complex(math.nan_f32(), im)
+		}
+	}
+	r := math.exp(real(x))
+	s, c := math.sincos(imag(x))
+	return complex(r*c, r*s)
+}
+exp_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	switch re, im := real(x), imag(x); {
+	case math.is_inf(re, 0):
+		switch {
+		case re > 0 && im == 0:
+			return x
+		case math.is_inf(im, 0) || math.is_nan(im):
+			if re < 0 {
+				return complex(0, math.copy_sign(0, im))
+			} else {
+				return complex(math.inf_f64(1.0), math.nan_f64())
+			}
+		}
+	case math.is_nan(re):
+		if im == 0 {
+			return complex(math.nan_f64(), im)
+		}
+	}
+	r := math.exp(real(x))
+	s, c := math.sincos(imag(x))
+	return complex(r*c, r*s)
+}
+
+
+pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 {
+	if x == 0 { // Guaranteed also true for x == -0.
+		if is_nan(y) {
+			return nan_complex32()
+		}
+		r, i := real(y), imag(y)
+		switch {
+		case r == 0:
+			return 1
+		case r < 0:
+			if i == 0 {
+				return complex(math.inf_f16(1), 0)
+			}
+			return inf_complex32()
+		case r > 0:
+			return 0
+		}
+		unreachable()
+	}
+	modulus := abs(x)
+	if modulus == 0 {
+		return complex(0, 0)
+	}
+	r := math.pow(modulus, real(y))
+	arg := phase(x)
+	theta := real(y) * arg
+	if imag(y) != 0 {
+		r *= math.exp(-imag(y) * arg)
+		theta += imag(y) * math.ln(modulus)
+	}
+	s, c := math.sincos(theta)
+	return complex(r*c, r*s)
+}
+pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 {
+	if x == 0 { // Guaranteed also true for x == -0.
+		if is_nan(y) {
+			return nan_complex64()
+		}
+		r, i := real(y), imag(y)
+		switch {
+		case r == 0:
+			return 1
+		case r < 0:
+			if i == 0 {
+				return complex(math.inf_f32(1), 0)
+			}
+			return inf_complex64()
+		case r > 0:
+			return 0
+		}
+		unreachable()
+	}
+	modulus := abs(x)
+	if modulus == 0 {
+		return complex(0, 0)
+	}
+	r := math.pow(modulus, real(y))
+	arg := phase(x)
+	theta := real(y) * arg
+	if imag(y) != 0 {
+		r *= math.exp(-imag(y) * arg)
+		theta += imag(y) * math.ln(modulus)
+	}
+	s, c := math.sincos(theta)
+	return complex(r*c, r*s)
+}
+pow_complex128 :: proc "contextless" (x, y: complex128) -> complex128 {
+	if x == 0 { // Guaranteed also true for x == -0.
+		if is_nan(y) {
+			return nan_complex128()
+		}
+		r, i := real(y), imag(y)
+		switch {
+		case r == 0:
+			return 1
+		case r < 0:
+			if i == 0 {
+				return complex(math.inf_f64(1), 0)
+			}
+			return inf_complex128()
+		case r > 0:
+			return 0
+		}
+		unreachable()
+	}
+	modulus := abs(x)
+	if modulus == 0 {
+		return complex(0, 0)
+	}
+	r := math.pow(modulus, real(y))
+	arg := phase(x)
+	theta := real(y) * arg
+	if imag(y) != 0 {
+		r *= math.exp(-imag(y) * arg)
+		theta += imag(y) * math.ln(modulus)
+	}
+	s, c := math.sincos(theta)
+	return complex(r*c, r*s)
+}
+
+
+
+log10_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return math.LN10*ln(x)
+}
+log10_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return math.LN10*ln(x)
+}
+log10_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	return math.LN10*ln(x)
+}
+
+
+phase_complex32 :: proc "contextless" (x:  complex32) -> f16 {
+	return math.atan2(imag(x), real(x))
+}
+phase_complex64 :: proc "contextless" (x:  complex64) -> f32 {
+	return math.atan2(imag(x), real(x))
+}
+phase_complex128 :: proc "contextless" (x:  complex128) -> f64 {
+	return math.atan2(imag(x), real(x))
+}
+
+
+rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 {
+	s, c := math.sincos(θ)
+	return complex(r*c, r*s)
+}
+rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 {
+	s, c := math.sincos(θ)
+	return complex(r*c, r*s)
+}
+rect_complex128 :: proc "contextless" (r, θ: f64) -> complex128 {
+	s, c := math.sincos(θ)
+	return complex(r*c, r*s)
+}
+
+polar_complex32 :: proc "contextless" (x: complex32) -> (r, θ: f16) {
+	return abs(x), phase(x)
+}
+polar_complex64 :: proc "contextless" (x: complex64) -> (r, θ: f32) {
+	return abs(x), phase(x)
+}
+polar_complex128 :: proc "contextless" (x: complex128) -> (r, θ: f64) {
+	return abs(x), phase(x)
+}
+
+
+
+
+nan_complex32 :: proc "contextless" () -> complex32 {
+	return complex(math.nan_f16(), math.nan_f16())
+}
+nan_complex64 :: proc "contextless" () -> complex64 {
+	return complex(math.nan_f32(), math.nan_f32())
+}
+nan_complex128 :: proc "contextless" () -> complex128 {
+	return complex(math.nan_f64(), math.nan_f64())
+}
+
+
+inf_complex32 :: proc "contextless" () -> complex32 {
+	inf := math.inf_f16(1)
+	return complex(inf, inf)
+}
+inf_complex64 :: proc "contextless" () -> complex64 {
+	inf := math.inf_f32(1)
+	return complex(inf, inf)
+}
+inf_complex128 :: proc "contextless" () -> complex128 {
+	inf := math.inf_f64(1)
+	return complex(inf, inf)
+}
+
+
+is_inf_complex32 :: proc "contextless" (x: complex32) -> bool {
+	return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0)
+}
+is_inf_complex64 :: proc "contextless" (x: complex64) -> bool {
+	return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0)
+}
+is_inf_complex128 :: proc "contextless" (x: complex128) -> bool {
+	return math.is_inf(real(x), 0) || math.is_inf(imag(x), 0)
+}
+
+
+is_nan_complex32 :: proc "contextless" (x: complex32) -> bool {
+	if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) {
+		return false
+	}
+	return math.is_nan(real(x)) || math.is_nan(imag(x))
+}
+is_nan_complex64 :: proc "contextless" (x: complex64) -> bool {
+	if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) {
+		return false
+	}
+	return math.is_nan(real(x)) || math.is_nan(imag(x))
+}
+is_nan_complex128 :: proc "contextless" (x: complex128) -> bool {
+	if math.is_inf(real(x), 0) || math.is_inf(imag(x), 0) {
+		return false
+	}
+	return math.is_nan(real(x)) || math.is_nan(imag(x))
+}

+ 273 - 0
core/math/cmplx/cmplx_invtrig.odin

@@ -0,0 +1,273 @@
+package math_cmplx
+
+import "core:builtin"
+import "core:math"
+
+// The original C code, the long comment, and the constants
+// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
+// The go code is a simplified version of the original C.
+//
+// Cephes Math Library Release 2.8:  June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+//    Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+//   The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+//   Stephen L. Moshier
+//   [email protected]
+
+acos :: proc{
+	acos_complex32,
+	acos_complex64,
+	acos_complex128,
+}
+acosh :: proc{
+	acosh_complex32,
+	acosh_complex64,
+	acosh_complex128,
+}
+
+asin :: proc{
+	asin_complex32,
+	asin_complex64,
+	asin_complex128,
+}
+asinh :: proc{
+	asinh_complex32,
+	asinh_complex64,
+	asinh_complex128,
+}
+
+atan :: proc{
+	atan_complex32,
+	atan_complex64,
+	atan_complex128,
+}
+
+atanh :: proc{
+	atanh_complex32,
+	atanh_complex64,
+	atanh_complex128,
+}
+
+
+acos_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	w := asin(x)
+	return complex(math.PI/2 - real(w), -imag(w))
+}
+acos_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	w := asin(x)
+	return complex(math.PI/2 - real(w), -imag(w))
+}
+acos_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	w := asin(x)
+	return complex(math.PI/2 - real(w), -imag(w))
+}
+
+
+acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	if x == 0 {
+		return complex(0, math.copy_sign(math.PI/2, imag(x)))
+	}
+	w := acos(x)
+	if imag(w) <= 0 {
+		return complex(-imag(w), real(w))
+	}
+	return complex(imag(w), -real(w))
+}
+acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	if x == 0 {
+		return complex(0, math.copy_sign(math.PI/2, imag(x)))
+	}
+	w := acos(x)
+	if imag(w) <= 0 {
+		return complex(-imag(w), real(w))
+	}
+	return complex(imag(w), -real(w))
+}
+acosh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	if x == 0 {
+		return complex(0, math.copy_sign(math.PI/2, imag(x)))
+	}
+	w := acos(x)
+	if imag(w) <= 0 {
+		return complex(-imag(w), real(w))
+	}
+	return complex(imag(w), -real(w))
+}
+
+asin_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return complex32(asin_complex128(complex128(x)))
+}
+asin_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return complex64(asin_complex128(complex128(x)))
+}
+asin_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	switch re, im := real(x), imag(x); {
+	case im == 0 && abs(re) <= 1:
+		return complex(math.asin(re), im)
+	case re == 0 && abs(im) <= 1:
+		return complex(re, math.asinh(im))
+	case math.is_nan(im):
+		switch {
+		case re == 0:
+			return complex(re, math.nan_f64())
+		case math.is_inf(re, 0):
+			return complex(math.nan_f64(), re)
+		case:
+			return nan_complex128()
+		}
+	case math.is_inf(im, 0):
+		switch {
+		case math.is_nan(re):
+			return x
+		case math.is_inf(re, 0):
+			return complex(math.copy_sign(math.PI/4, re), im)
+		case:
+			return complex(math.copy_sign(0, re), im)
+		}
+	case math.is_inf(re, 0):
+		return complex(math.copy_sign(math.PI/2, re), math.copy_sign(re, im))
+	}
+	ct := complex(-imag(x), real(x)) // i * x
+	xx := x * x
+	x1 := complex(1-real(xx), -imag(xx)) // 1 - x*x
+	x2 := sqrt(x1)                       // x2 = sqrt(1 - x*x)
+	w  := ln(ct + x2)
+	return complex(imag(w), -real(w)) // -i * w
+}
+
+asinh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return complex32(asinh_complex128(complex128(x)))
+}
+asinh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return complex64(asinh_complex128(complex128(x)))
+}
+asinh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	switch re, im := real(x), imag(x); {
+	case im == 0 && abs(re) <= 1:
+		return complex(math.asinh(re), im)
+	case re == 0 && abs(im) <= 1:
+		return complex(re, math.asin(im))
+	case math.is_inf(re, 0):
+		switch {
+		case math.is_inf(im, 0):
+			return complex(re, math.copy_sign(math.PI/4, im))
+		case math.is_nan(im):
+			return x
+		case:
+			return complex(re, math.copy_sign(0.0, im))
+		}
+	case math.is_nan(re):
+		switch {
+		case im == 0:
+			return x
+		case math.is_inf(im, 0):
+			return complex(im, re)
+		case:
+			return nan_complex128()
+		}
+	case math.is_inf(im, 0):
+		return complex(math.copy_sign(im, re), math.copy_sign(math.PI/2, im))
+	}
+	xx := x * x
+	x1 := complex(1+real(xx), imag(xx)) // 1 + x*x
+	return ln(x + sqrt(x1))            // log(x + sqrt(1 + x*x))
+}
+
+
+atan_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	return complex32(atan_complex128(complex128(x)))
+}
+atan_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	return complex64(atan_complex128(complex128(x)))
+}
+atan_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex circular arc tangent
+	//
+	// DESCRIPTION:
+	//
+	// If
+	//     z = x + iy,
+	//
+	// then
+	//          1       (    2x     )
+	// Re w  =  - arctan(-----------)  +  k PI
+	//          2       (     2    2)
+	//                  (1 - x  - y )
+	//
+	//               ( 2         2)
+	//          1    (x  +  (y+1) )
+	// Im w  =  - log(------------)
+	//          4    ( 2         2)
+	//               (x  +  (y-1) )
+	//
+	// Where k is an arbitrary integer.
+	//
+	// catan(z) = -i catanh(iz).
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    DEC       -10,+10      5900       1.3e-16     7.8e-18
+	//    IEEE      -10,+10     30000       2.3e-15     8.5e-17
+	// The check catan( ctan(z) )  =  z, with |x| and |y| < PI/2,
+	// had peak relative error 1.5e-16, rms relative error
+	// 2.9e-17.  See also clog().
+
+	switch re, im := real(x), imag(x); {
+	case im == 0:
+		return complex(math.atan(re), im)
+	case re == 0 && abs(im) <= 1:
+		return complex(re, math.atanh(im))
+	case math.is_inf(im, 0) || math.is_inf(re, 0):
+		if math.is_nan(re) {
+			return complex(math.nan_f64(), math.copy_sign(0, im))
+		}
+		return complex(math.copy_sign(math.PI/2, re), math.copy_sign(0, im))
+	case math.is_nan(re) || math.is_nan(im):
+		return nan_complex128()
+	}
+	x2 := real(x) * real(x)
+	a := 1 - x2 - imag(x)*imag(x)
+	if a == 0 {
+		return nan_complex128()
+	}
+	t := 0.5 * math.atan2(2*real(x), a)
+	w := _reduce_pi_f64(t)
+
+	t = imag(x) - 1
+	b := x2 + t*t
+	if b == 0 {
+		return nan_complex128()
+	}
+	t = imag(x) + 1
+	c := (x2 + t*t) / b
+	return complex(w, 0.25*math.ln(c))
+}
+
+atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
+	z := complex(-imag(x), real(x)) // z = i * x
+	z = atan(z)
+	return complex(imag(z), -real(z)) // z = -i * z
+}
+atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
+	z := complex(-imag(x), real(x)) // z = i * x
+	z = atan(z)
+	return complex(imag(z), -real(z)) // z = -i * z
+}
+atanh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	z := complex(-imag(x), real(x)) // z = i * x
+	z = atan(z)
+	return complex(imag(z), -real(z)) // z = -i * z
+}

+ 409 - 0
core/math/cmplx/cmplx_trig.odin

@@ -0,0 +1,409 @@
+package math_cmplx
+
+import "core:math"
+import "core:math/bits"
+
+// The original C code, the long comment, and the constants
+// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
+// The go code is a simplified version of the original C.
+//
+// Cephes Math Library Release 2.8:  June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+//    Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+//   The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+//   Stephen L. Moshier
+//   [email protected]
+
+sin_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex circular sine
+	//
+	// DESCRIPTION:
+	//
+	// If
+	//     z = x + iy,
+	//
+	// then
+	//
+	//     w = sin x  cosh y  +  i cos x sinh y.
+	//
+	// csin(z) = -i csinh(iz).
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    DEC       -10,+10      8400       5.3e-17     1.3e-17
+	//    IEEE      -10,+10     30000       3.8e-16     1.0e-16
+	// Also tested by csin(casin(z)) = z.
+
+	switch re, im := real(x), imag(x); {
+	case im == 0 && (math.is_inf(re, 0) || math.is_nan(re)):
+		return complex(math.nan_f64(), im)
+	case math.is_inf(im, 0):
+		switch {
+		case re == 0:
+			return x
+		case math.is_inf(re, 0) || math.is_nan(re):
+			return complex(math.nan_f64(), im)
+		}
+	case re == 0 && math.is_nan(im):
+		return x
+	}
+	s, c := math.sincos(real(x))
+	sh, ch := _sinhcosh_f64(imag(x))
+	return complex(s*ch, c*sh)
+}
+
+cos_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex circular cosine
+	//
+	// DESCRIPTION:
+	//
+	// If
+	//     z = x + iy,
+	//
+	// then
+	//
+	//     w = cos x  cosh y  -  i sin x sinh y.
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    DEC       -10,+10      8400       4.5e-17     1.3e-17
+	//    IEEE      -10,+10     30000       3.8e-16     1.0e-16
+
+	switch re, im := real(x), imag(x); {
+	case im == 0 && (math.is_inf(re, 0) || math.is_nan(re)):
+		return complex(math.nan_f64(), -im*math.copy_sign(0, re))
+	case math.is_inf(im, 0):
+		switch {
+		case re == 0:
+			return complex(math.inf_f64(1), -re*math.copy_sign(0, im))
+		case math.is_inf(re, 0) || math.is_nan(re):
+			return complex(math.inf_f64(1), math.nan_f64())
+		}
+	case re == 0 && math.is_nan(im):
+		return complex(math.nan_f64(), 0)
+	}
+	s, c := math.sincos(real(x))
+	sh, ch := _sinhcosh_f64(imag(x))
+	return complex(c*ch, -s*sh)
+}
+
+sinh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex hyperbolic sine
+	//
+	// DESCRIPTION:
+	//
+	// csinh z = (cexp(z) - cexp(-z))/2
+	//         = sinh x * cos y  +  i cosh x * sin y .
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    IEEE      -10,+10     30000       3.1e-16     8.2e-17
+
+	switch re, im := real(x), imag(x); {
+	case re == 0 && (math.is_inf(im, 0) || math.is_nan(im)):
+		return complex(re, math.nan_f64())
+	case math.is_inf(re, 0):
+		switch {
+		case im == 0:
+			return complex(re, im)
+		case math.is_inf(im, 0) || math.is_nan(im):
+			return complex(re, math.nan_f64())
+		}
+	case im == 0 && math.is_nan(re):
+		return complex(math.nan_f64(), im)
+	}
+	s, c := math.sincos(imag(x))
+	sh, ch := _sinhcosh_f64(real(x))
+	return complex(c*sh, s*ch)
+}
+
+cosh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex hyperbolic cosine
+	//
+	// DESCRIPTION:
+	//
+	// ccosh(z) = cosh x  cos y + i sinh x sin y .
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    IEEE      -10,+10     30000       2.9e-16     8.1e-17
+
+	switch re, im := real(x), imag(x); {
+	case re == 0 && (math.is_inf(im, 0) || math.is_nan(im)):
+		return complex(math.nan_f64(), re*math.copy_sign(0, im))
+	case math.is_inf(re, 0):
+		switch {
+		case im == 0:
+			return complex(math.inf_f64(1), im*math.copy_sign(0, re))
+		case math.is_inf(im, 0) || math.is_nan(im):
+			return complex(math.inf_f64(1), math.nan_f64())
+		}
+	case im == 0 && math.is_nan(re):
+		return complex(math.nan_f64(), im)
+	}
+	s, c := math.sincos(imag(x))
+	sh, ch := _sinhcosh_f64(real(x))
+	return complex(c*ch, s*sh)
+}
+
+tan_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	// Complex circular tangent
+	//
+	// DESCRIPTION:
+	//
+	// If
+	//     z = x + iy,
+	//
+	// then
+	//
+	//           sin 2x  +  i sinh 2y
+	//     w  =  --------------------.
+	//            cos 2x  +  cosh 2y
+	//
+	// On the real axis the denominator is zero at odd multiples
+	// of PI/2. The denominator is evaluated by its Taylor
+	// series near these points.
+	//
+	// ctan(z) = -i ctanh(iz).
+	//
+	// ACCURACY:
+	//
+	//                      Relative error:
+	// arithmetic   domain     # trials      peak         rms
+	//    DEC       -10,+10      5200       7.1e-17     1.6e-17
+	//    IEEE      -10,+10     30000       7.2e-16     1.2e-16
+	// Also tested by ctan * ccot = 1 and catan(ctan(z))  =  z.
+
+	switch re, im := real(x), imag(x); {
+	case math.is_inf(im, 0):
+		switch {
+		case math.is_inf(re, 0) || math.is_nan(re):
+			return complex(math.copy_sign(0, re), math.copy_sign(1, im))
+		}
+		return complex(math.copy_sign(0, math.sin(2*re)), math.copy_sign(1, im))
+	case re == 0 && math.is_nan(im):
+		return x
+	}
+	d := math.cos(2*real(x)) + math.cosh(2*imag(x))
+	if abs(d) < 0.25 {
+		d = _tan_series_f64(x)
+	}
+	if d == 0 {
+		return inf_complex128()
+	}
+	return complex(math.sin(2*real(x))/d, math.sinh(2*imag(x))/d)
+}
+
+tanh_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	switch re, im := real(x), imag(x); {
+	case math.is_inf(re, 0):
+		switch {
+		case math.is_inf(im, 0) || math.is_nan(im):
+			return complex(math.copy_sign(1, re), math.copy_sign(0, im))
+		}
+		return complex(math.copy_sign(1, re), math.copy_sign(0, math.sin(2*im)))
+	case im == 0 && math.is_nan(re):
+		return x
+	}
+	d := math.cosh(2*real(x)) + math.cos(2*imag(x))
+	if d == 0 {
+		return inf_complex128()
+	}
+	return complex(math.sinh(2*real(x))/d, math.sin(2*imag(x))/d)
+}
+
+cot_complex128 :: proc "contextless" (x: complex128) -> complex128 {
+	d := math.cosh(2*imag(x)) - math.cos(2*real(x))
+	if abs(d) < 0.25 {
+		d = _tan_series_f64(x)
+	}
+	if d == 0 {
+		return inf_complex128()
+	}
+	return complex(math.sin(2*real(x))/d, -math.sinh(2*imag(x))/d)
+}
+
+
+@(private="file")
+_sinhcosh_f64 :: proc "contextless" (x: f64) -> (sh, ch: f64) {
+	if abs(x) <= 0.5 {
+		return math.sinh(x), math.cosh(x)
+	}
+	e := math.exp(x)
+	ei := 0.5 / e
+	e *= 0.5
+	return e - ei, e + ei
+}
+
+
+// taylor series of cosh(2y) - cos(2x)
+@(private)
+_tan_series_f64 :: proc "contextless" (z: complex128) -> f64 {
+	MACH_EPSILON :: 1.0 / (1 << 53)
+
+	x := abs(2 * real(z))
+	y := abs(2 * imag(z))
+	x = _reduce_pi_f64(x)
+	x, y = x * x, y * y
+	x2, y2 := 1.0, 1.0
+	f, rn, d := 1.0, 0.0, 0.0
+
+	for {
+		rn += 1
+		f *= rn
+		rn += 1
+		f *= rn
+		x2 *= x
+		y2 *= y
+		t := y2 + x2
+		t /= f
+		d += t
+
+		rn += 1
+		f *= rn
+		rn += 1
+		f *= rn
+		x2 *= x
+		y2 *= y
+		t = y2 - x2
+		t /= f
+		d += t
+		if !(abs(t/d) > MACH_EPSILON) { // don't use <=, because of floating point nonsense and NaN
+			break
+		}
+	}
+	return d
+}
+
+// _reduce_pi_f64 reduces the input argument x to the range (-PI/2, PI/2].
+// x must be greater than or equal to 0. For small arguments it
+// uses Cody-Waite reduction in 3 f64 parts based on:
+// "Elementary Function Evaluation:  Algorithms and Implementation"
+// Jean-Michel Muller, 1997.
+// For very large arguments it uses Payne-Hanek range reduction based on:
+// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit"
+@(private)
+_reduce_pi_f64 :: proc "contextless" (x: f64) -> f64 #no_bounds_check {
+	x := x
+
+	// REDUCE_THRESHOLD is the maximum value of x where the reduction using
+	// Cody-Waite reduction still gives accurate results. This threshold
+	// is set by t*PIn being representable as a f64 without error
+	// where t is given by t = floor(x * (1 / PI)) and PIn are the leading partial
+	// terms of PI. Since the leading terms, PI1 and PI2 below, have 30 and 32
+	// trailing zero bits respectively, t should have less than 30 significant bits.
+	//	t < 1<<30  -> floor(x*(1/PI)+0.5) < 1<<30 -> x < (1<<30-1) * PI - 0.5
+	// So, conservatively we can take x < 1<<30.
+	REDUCE_THRESHOLD :: f64(1 << 30)
+
+	if abs(x) < REDUCE_THRESHOLD {
+		// Use Cody-Waite reduction in three parts.
+		// PI1, PI2 and PI3 comprise an extended precision value of PI
+		// such that PI ~= PI1 + PI2 + PI3. The parts are chosen so
+		// that PI1 and PI2 have an approximately equal number of trailing
+		// zero bits. This ensures that t*PI1 and t*PI2 are exact for
+		// large integer values of t. The full precision PI3 ensures the
+		// approximation of PI is accurate to 102 bits to handle cancellation
+		// during subtraction.
+		PI1 :: 0h400921fb40000000 // 3.141592502593994
+		PI2 :: 0h3e84442d00000000 // 1.5099578831723193e-07
+		PI3 :: 0h3d08469898cc5170 // 1.0780605716316238e-14
+
+		t := x / math.PI
+		t += 0.5
+		t = f64(i64(t)) // i64(t) = the multiple
+		return ((x - t*PI1) - t*PI2) - t*PI3
+	}
+	// Must apply Payne-Hanek range reduction
+	MASK      :: 0x7FF
+	SHIFT     :: 64 - 11 - 1
+	BIAS      :: 1023
+	FRAC_MASK :: 1<<SHIFT - 1
+
+	// Extract out the integer and exponent such that,
+	// x = ix * 2 ** exp.
+	ix := transmute(u64)(x)
+	exp := int(ix>>SHIFT&MASK) - BIAS - SHIFT
+	ix &= FRAC_MASK
+	ix |= 1 << SHIFT
+
+	// bdpi is the binary digits of 1/PI as a u64 array,
+	// that is, 1/PI = SUM bdpi[i]*2^(-64*i).
+	// 19 64-bit digits give 1216 bits of precision
+	// to handle the largest possible f64 exponent.
+	@static bdpi := [?]u64{
+		0x0000000000000000,
+		0x517cc1b727220a94,
+		0xfe13abe8fa9a6ee0,
+		0x6db14acc9e21c820,
+		0xff28b1d5ef5de2b0,
+		0xdb92371d2126e970,
+		0x0324977504e8c90e,
+		0x7f0ef58e5894d39f,
+		0x74411afa975da242,
+		0x74ce38135a2fbf20,
+		0x9cc8eb1cc1a99cfa,
+		0x4e422fc5defc941d,
+		0x8ffc4bffef02cc07,
+		0xf79788c5ad05368f,
+		0xb69b3f6793e584db,
+		0xa7a31fb34f2ff516,
+		0xba93dd63f5f2f8bd,
+		0x9e839cfbc5294975,
+		0x35fdafd88fc6ae84,
+		0x2b0198237e3db5d5,
+	}
+
+	// Use the exponent to extract the 3 appropriate u64 digits from bdpi,
+	// B ~ (z0, z1, z2), such that the product leading digit has the exponent -64.
+	// Note, exp >= 50 since x >= REDUCE_THRESHOLD and exp < 971 for maximum f64.
+	digit, bitshift := uint(exp+64)/64, uint(exp+64)%64
+	z0 := (bdpi[digit] << bitshift) | (bdpi[digit+1] >> (64 - bitshift))
+	z1 := (bdpi[digit+1] << bitshift) | (bdpi[digit+2] >> (64 - bitshift))
+	z2 := (bdpi[digit+2] << bitshift) | (bdpi[digit+3] >> (64 - bitshift))
+
+	// Multiply mantissa by the digits and extract the upper two digits (hi, lo).
+	z2hi, _    := bits.mul(z2, ix)
+	z1hi, z1lo := bits.mul(z1, ix)
+	z0lo  := z0 * ix
+	lo, c := bits.add(z1lo, z2hi, 0)
+	hi, _ := bits.add(z0lo, z1hi, c)
+
+	// Find the magnitude of the fraction.
+	lz := uint(bits.leading_zeros(hi))
+	e  := u64(BIAS - (lz + 1))
+
+	// Clear implicit mantissa bit and shift into place.
+	hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1)))
+	hi >>= 64 - SHIFT
+
+	// Include the exponent and convert to a float.
+	hi |= e << SHIFT
+	x = transmute(f64)(hi)
+
+	// map to (-PI/2, PI/2]
+	if x > 0.5 {
+		x -= 1
+	}
+	return math.PI * x
+}
+

+ 1 - 1
core/math/ease/ease.odin

@@ -450,7 +450,7 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i
 flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
 	clear(&flux.keys_to_be_deleted)
 
-	for key, tween in &flux.values {
+	for key, &tween in flux.values {
 		delay_remainder := f64(0)
 
 		// Update delay if necessary.

+ 74 - 0
core/math/math.odin

@@ -2158,6 +2158,80 @@ signbit :: proc{
 }
 
 
+@(require_results)
+hypot_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
+	p, q := abs(x), abs(y)
+	switch {
+	case is_inf(p, 1) || is_inf(q, 1):
+		return inf_f16(1)
+	case is_nan(p) || is_nan(q):
+		return nan_f16()
+	}
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * sqrt(1+q*q)
+}
+@(require_results)
+hypot_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
+	p, q := abs(x), abs(y)
+	switch {
+	case is_inf(p, 1) || is_inf(q, 1):
+		return inf_f32(1)
+	case is_nan(p) || is_nan(q):
+		return nan_f32()
+	}
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * sqrt(1+q*q)
+}
+@(require_results)
+hypot_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
+	p, q := abs(x), abs(y)
+	switch {
+	case is_inf(p, 1) || is_inf(q, 1):
+		return inf_f64(1)
+	case is_nan(p) || is_nan(q):
+		return nan_f64()
+	}
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * sqrt(1+q*q)
+}
+@(require_results) hypot_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(hypot_f16(f16(x), f16(y))) }
+@(require_results) hypot_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(hypot_f16(f16(x), f16(y))) }
+@(require_results) hypot_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(hypot_f32(f32(x), f32(y))) }
+@(require_results) hypot_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(hypot_f32(f32(x), f32(y))) }
+@(require_results) hypot_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(hypot_f64(f64(x), f64(y))) }
+@(require_results) hypot_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(hypot_f64(f64(x), f64(y))) }
+
+// hypot returns Sqrt(p*p + q*q), taking care to avoid unnecessary overflow and underflow.
+//
+// Special cases:
+//	hypot(±Inf, q) = +Inf
+//	hypot(p, ±Inf) = +Inf
+//	hypot(NaN, q) = NaN
+//	hypot(p, NaN) = NaN
+hypot :: proc{
+	hypot_f16, hypot_f16le, hypot_f16be,
+	hypot_f32, hypot_f32le, hypot_f32be,
+	hypot_f64, hypot_f64le, hypot_f64be,
+}
+
 F16_DIG        :: 3
 F16_EPSILON    :: 0.00097656
 F16_GUARD      :: 0

+ 308 - 0
core/math/math_sincos.odin

@@ -0,0 +1,308 @@
+package math
+
+import "core:math/bits"
+
+// The original C code, the long comment, and the constants
+// below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
+// available from http://www.netlib.org/cephes/cmath.tgz.
+// The go code is a simplified version of the original C.
+//
+//      sin.c
+//
+//      Circular sine
+//
+// SYNOPSIS:
+//
+// double x, y, sin();
+// y = sin( x );
+//
+// DESCRIPTION:
+//
+// Range reduction is into intervals of pi/4.  The reduction error is nearly
+// eliminated by contriving an extended precision modular arithmetic.
+//
+// Two polynomial approximating functions are employed.
+// Between 0 and pi/4 the sine is approximated by
+//      x  +  x**3 P(x**2).
+// Between pi/4 and pi/2 the cosine is represented as
+//      1  -  x**2 Q(x**2).
+//
+// ACCURACY:
+//
+//                      Relative error:
+// arithmetic   domain      # trials      peak         rms
+//    DEC       0, 10       150000       3.0e-17     7.8e-18
+//    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
+//
+// Partial loss of accuracy begins to occur at x = 2**30 = 1.074e9.  The loss
+// is not gradual, but jumps suddenly to about 1 part in 10e7.  Results may
+// be meaningless for x > 2**49 = 5.6e14.
+//
+//      cos.c
+//
+//      Circular cosine
+//
+// SYNOPSIS:
+//
+// double x, y, cos();
+// y = cos( x );
+//
+// DESCRIPTION:
+//
+// Range reduction is into intervals of pi/4.  The reduction error is nearly
+// eliminated by contriving an extended precision modular arithmetic.
+//
+// Two polynomial approximating functions are employed.
+// Between 0 and pi/4 the cosine is approximated by
+//      1  -  x**2 Q(x**2).
+// Between pi/4 and pi/2 the sine is represented as
+//      x  +  x**3 P(x**2).
+//
+// ACCURACY:
+//
+//                      Relative error:
+// arithmetic   domain      # trials      peak         rms
+//    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
+//    DEC        0,+1.07e9   17000       3.0e-17     7.2e-18
+//
+// Cephes Math Library Release 2.8:  June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+//    Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+//   The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+//   Stephen L. Moshier
+//   [email protected]
+
+sincos :: proc{
+	sincos_f16, sincos_f16le, sincos_f16be,
+	sincos_f32, sincos_f32le, sincos_f32be,
+	sincos_f64, sincos_f64le, sincos_f64be,
+}
+
+sincos_f16 :: proc "contextless" (x: f16) -> (sin, cos: f16) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f16(s), f16(c)
+}
+sincos_f16le :: proc "contextless" (x: f16le) -> (sin, cos: f16le) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f16le(s), f16le(c)
+}
+sincos_f16be :: proc "contextless" (x: f16be) -> (sin, cos: f16be) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f16be(s), f16be(c)
+}
+
+sincos_f32 :: proc "contextless" (x: f32) -> (sin, cos: f32) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f32(s), f32(c)
+}
+sincos_f32le :: proc "contextless" (x: f32le) -> (sin, cos: f32le) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f32le(s), f32le(c)
+}
+sincos_f32be :: proc "contextless" (x: f32be) -> (sin, cos: f32be) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f32be(s), f32be(c)
+}
+
+sincos_f64le :: proc "contextless" (x: f64le) -> (sin, cos: f64le) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f64le(s), f64le(c)
+}
+sincos_f64be :: proc "contextless" (x: f64be) -> (sin, cos: f64be) #no_bounds_check {
+	s, c := sincos_f64(f64(x))
+	return f64be(s), f64be(c)
+}
+
+sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check {
+	x := x
+
+	PI4A :: 0h3fe921fb40000000 // 7.85398125648498535156e-1  PI/4 split into three parts
+	PI4B :: 0h3e64442d00000000 // 3.77489470793079817668e-8
+	PI4C :: 0h3ce8469898cc5170 // 2.69515142907905952645e-15
+
+	// special cases
+	switch {
+	case x == 0:
+		return x, 1 // return ±0.0, 1.0
+	case is_nan(x) || is_inf(x, 0):
+		return nan_f64(), nan_f64()
+	}
+
+	// make argument positive
+	sin_sign, cos_sign := false, false
+	if x < 0 {
+		x = -x
+		sin_sign = true
+	}
+
+	j: u64
+	y, z: f64
+	if x >= REDUCE_THRESHOLD {
+		j, z = _trig_reduce_f64(x)
+	} else {
+		j = u64(x * (4 / PI)) // integer part of x/(PI/4), as integer for tests on the phase angle
+		y = f64(j)           // integer part of x/(PI/4), as float
+
+		if j&1 == 1 { // map zeros to origin
+			j += 1
+			y += 1
+		}
+		j &= 7                               // octant modulo TAU radians (360 degrees)
+		z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic
+	}
+	if j > 3 { // reflect in x axis
+		j -= 4
+		sin_sign, cos_sign = !sin_sign, !cos_sign
+	}
+	if j > 1 {
+		cos_sign = !cos_sign
+	}
+
+	zz := z * z
+
+	cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5])
+	sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5])
+
+	if j == 1 || j == 2 {
+		sin, cos = cos, sin
+	}
+	if cos_sign {
+		cos = -cos
+	}
+	if sin_sign {
+		sin = -sin
+	}
+	return
+}
+
+// sin coefficients
+@(private="file")
+_sin := [?]f64{
+	 0h3de5d8fd1fd19ccd, //  1.58962301576546568060e-10
+	 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
+	 0h3ec71de3567d48a1, //  2.75573136213857245213e-6
+	 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
+	 0h3f8111111110f7d0, //  8.33333333332211858878e-3
+	 0hbfc5555555555548, // -1.66666666666666307295e-1
+}
+
+// cos coefficients
+@(private="file")
+_cos := [?]f64{
+	0hbda8fa49a0861a9b, // -1.13585365213876817300e-11,
+	0h3e21ee9d7b4e3f05, //  2.08757008419747316778e-9,
+	0hbe927e4f7eac4bc6, // -2.75573141792967388112e-7,
+	0h3efa01a019c844f5, //  2.48015872888517045348e-5,
+	0hbf56c16c16c14f91, // -1.38888888888730564116e-3,
+	0h3fa555555555554b, //  4.16666666666665929218e-2,
+}
+
+// REDUCE_THRESHOLD is the maximum value of x where the reduction using Pi/4
+// in 3 f64 parts still gives accurate results. This threshold
+// is set by y*C being representable as a f64 without error
+// where y is given by y = floor(x * (4 / Pi)) and C is the leading partial
+// terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30
+// and 32 trailing zero bits, y should have less than 30 significant bits.
+//
+//	y < 1<<30  -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4
+//
+// So, conservatively we can take x < 1<<29.
+// Above this threshold Payne-Hanek range reduction must be used.
+@(private="file")
+REDUCE_THRESHOLD :: 1 << 29
+
+// _trig_reduce_f64 implements Payne-Hanek range reduction by Pi/4
+// for x > 0. It returns the integer part mod 8 (j) and
+// the fractional part (z) of x / (Pi/4).
+// The implementation is based on:
+// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit"
+// K. C. Ng et al, March 24, 1992
+// The simulated multi-precision calculation of x*B uses 64-bit integer arithmetic.
+_trig_reduce_f64 :: proc "contextless" (x: f64) -> (j: u64, z: f64) #no_bounds_check {
+	// bd_pi4 is the binary digits of 4/pi as a u64 array,
+	// that is, 4/pi = Sum bd_pi4[i]*2^(-64*i)
+	// 19 64-bit digits and the leading one bit give 1217 bits
+	// of precision to handle the largest possible f64 exponent.
+	@static bd_pi4 := [?]u64{
+		0x0000000000000001,
+		0x45f306dc9c882a53,
+		0xf84eafa3ea69bb81,
+		0xb6c52b3278872083,
+		0xfca2c757bd778ac3,
+		0x6e48dc74849ba5c0,
+		0x0c925dd413a32439,
+		0xfc3bd63962534e7d,
+		0xd1046bea5d768909,
+		0xd338e04d68befc82,
+		0x7323ac7306a673e9,
+		0x3908bf177bf25076,
+		0x3ff12fffbc0b301f,
+		0xde5e2316b414da3e,
+		0xda6cfd9e4f96136e,
+		0x9e8c7ecd3cbfd45a,
+		0xea4f758fd7cbe2f6,
+		0x7a0e73ef14a525d4,
+		0xd7f6bf623f1aba10,
+		0xac06608df8f6d757,
+	}
+
+	PI4 :: PI / 4
+	if x < PI4 {
+		return 0, x
+	}
+
+	MASK  :: 0x7FF
+	SHIFT :: 64 - 11 - 1
+	BIAS  :: 1023
+
+	// Extract out the integer and exponent such that,
+	// x = ix * 2 ** exp.
+	ix := transmute(u64)x
+	exp := int(ix>>SHIFT&MASK) - BIAS - SHIFT
+	ix &~= MASK << SHIFT
+	ix |= 1 << SHIFT
+	// Use the exponent to extract the 3 appropriate u64 digits from bd_pi4,
+	// B ~ (z0, z1, z2), such that the product leading digit has the exponent -61.
+	// Note, exp >= -53 since x >= PI4 and exp < 971 for maximum f64.
+	digit, bitshift := uint(exp+61)/64, uint(exp+61)%64
+	z0 := (bd_pi4[digit] << bitshift) | (bd_pi4[digit+1] >> (64 - bitshift))
+	z1 := (bd_pi4[digit+1] << bitshift) | (bd_pi4[digit+2] >> (64 - bitshift))
+	z2 := (bd_pi4[digit+2] << bitshift) | (bd_pi4[digit+3] >> (64 - bitshift))
+	// Multiply mantissa by the digits and extract the upper two digits (hi, lo).
+	z2hi, _ := bits.mul(z2, ix)
+	z1hi, z1lo := bits.mul(z1, ix)
+	z0lo := z0 * ix
+	lo, c := bits.add(z1lo, z2hi, 0)
+	hi, _ := bits.add(z0lo, z1hi, c)
+	// The top 3 bits are j.
+	j = hi >> 61
+	// Extract the fraction and find its magnitude.
+	hi = hi<<3 | lo>>61
+	lz := uint(bits.leading_zeros(hi))
+	e := u64(BIAS - (lz + 1))
+	// Clear implicit mantissa bit and shift into place.
+	hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1)))
+	hi >>= 64 - SHIFT
+	// Include the exponent and convert to a float.
+	hi |= e << SHIFT
+	z = transmute(f64)hi
+	// Map zeros to origin.
+	if j&1 == 1 {
+		j += 1
+		j &= 7
+		z -= 1
+	}
+	// Multiply the fractional part by pi/4.
+	return j, z * PI4
+}

+ 6 - 6
core/mem/allocators.odin

@@ -813,22 +813,22 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	switch mode {
 	case .Alloc:
 		if size > 0 {
-			panic("mem: panic allocator, .Alloc called")
+			panic("mem: panic allocator, .Alloc called", loc=loc)
 		}
 	case .Alloc_Non_Zeroed:
 		if size > 0 {
-			panic("mem: panic allocator, .Alloc_Non_Zeroed called")
+			panic("mem: panic allocator, .Alloc_Non_Zeroed called", loc=loc)
 		}
 	case .Resize:
 		if size > 0 {
-			panic("mem: panic allocator, .Resize called")
+			panic("mem: panic allocator, .Resize called", loc=loc)
 		}
 	case .Free:
 		if old_memory != nil {
-			panic("mem: panic allocator, .Free called")
+			panic("mem: panic allocator, .Free called", loc=loc)
 		}
 	case .Free_All:
-		panic("mem: panic allocator, .Free_All called")
+		panic("mem: panic allocator, .Free_All called", loc=loc)
 
 	case .Query_Features:
 		set := (^Allocator_Mode_Set)(old_memory)
@@ -838,7 +838,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		return nil, nil
 
 	case .Query_Info:
-		panic("mem: panic allocator, .Query_Info called")
+		panic("mem: panic allocator, .Query_Info called", loc=loc)
 	}
 
 	return nil, nil

+ 1 - 1
core/mem/virtual/arena.odin

@@ -120,7 +120,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
 			if arena.minimum_block_size == 0 {
 				arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
 			}
-			arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
+			arena_init_static(arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
 		}
 		fallthrough
 	case .Buffer:

+ 6 - 2
core/os/os2/errors_windows.odin

@@ -37,14 +37,18 @@ _get_platform_error :: proc() -> Error {
 	case win32.ERROR_NOT_SUPPORTED:
 		return .Unsupported
 
+	case win32.ERROR_HANDLE_EOF:
+		return .EOF
+
+	case win32.ERROR_INVALID_HANDLE:
+		return .Invalid_File
+
 	case
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
-		win32.ERROR_INVALID_HANDLE,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_LOCK_VIOLATION,
-		win32.ERROR_HANDLE_EOF,
 		win32.ERROR_BROKEN_PIPE,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_INSUFFICIENT_BUFFER,

+ 38 - 30
core/os/os2/file.odin

@@ -8,12 +8,6 @@ File :: struct {
 	impl: _File,
 }
 
-Seek_From :: enum {
-	Start   = 0, // seek relative to the origin of the file
-	Current = 1, // seek relative to the current offset
-	End     = 2, // seek relative to the end
-}
-
 File_Mode :: distinct u32
 File_Mode_Dir         :: File_Mode(1<<16)
 File_Mode_Named_Pipe  :: File_Mode(1<<17)
@@ -72,56 +66,70 @@ fd :: proc(f: ^File) -> uintptr {
 	return _fd(f)
 }
 
-
-close :: proc(f: ^File) -> Error {
-	return _close(f)
-}
-
 name :: proc(f: ^File) -> string {
 	return _name(f)
 }
 
-seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
-	return _seek(f, offset, whence)
+close :: proc(f: ^File) -> Error {
+	if f != nil {
+		return io.close(f.impl.stream)
+	}
+	return nil
 }
 
-read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
-	return _read(f, p)
+seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
+	if f != nil {
+		return io.seek(f.impl.stream, offset, whence)
+	}
+	return 0, .Invalid_File
 }
 
-read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
-	return _read_at(f, p, offset)
+read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+	if f != nil {
+		return io.read(f.impl.stream, p)
+	}
+	return 0, .Invalid_File
 }
 
-read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
-	return _read_from(f, r)
+read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+	if f != nil {
+		return io.read_at(f.impl.stream, p, offset)
+	}
+	return 0, .Invalid_File
 }
 
 write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
-	return _write(f, p)
+	if f != nil {
+		return io.write(f.impl.stream, p)
+	}
+	return 0, .Invalid_File
 }
 
 write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
-	return _write_at(f, p, offset)
-}
-
-write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
-	return _write_to(f, w)
+	if f != nil {
+		return io.write_at(f.impl.stream, p, offset)
+	}
+	return 0, .Invalid_File
 }
 
 file_size :: proc(f: ^File) -> (n: i64, err: Error) {
-	return _file_size(f)
+	if f != nil {
+		return io.size(f.impl.stream)
+	}
+	return 0, .Invalid_File
 }
 
+flush :: proc(f: ^File) -> Error {
+	if f != nil {
+		return io.flush(f.impl.stream)
+	}
+	return nil
+}
 
 sync :: proc(f: ^File) -> Error {
 	return _sync(f)
 }
 
-flush :: proc(f: ^File) -> Error {
-	return _flush(f)
-}
-
 truncate :: proc(f: ^File, size: i64) -> Error {
 	return _truncate(f, size)
 }

+ 63 - 21
core/os/os2/file_linux.odin

@@ -33,6 +33,8 @@ _File :: struct {
 	name: string,
 	fd: int,
 	allocator: runtime.Allocator,
+
+	stream: io.Stream,
 }
 
 _file_allocator :: proc() -> runtime.Allocator {
@@ -73,6 +75,10 @@ _new_file :: proc(fd: uintptr, _: string) -> ^File {
 	file.impl.fd = int(fd)
 	file.impl.allocator = _file_allocator()
 	file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
+	file.impl.stream = {
+		data = file,
+		procedure = _file_stream_proc,
+	}
 	return file
 }
 
@@ -102,7 +108,7 @@ _name :: proc(f: ^File) -> string {
 	return f.impl.name if f != nil else ""
 }
 
-_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
 	res := unix.sys_lseek(f.impl.fd, offset, int(whence))
 	if res < 0 {
 		return -1, _get_platform_error(int(res))
@@ -110,18 +116,18 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
 	return res, nil
 }
 
-_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n = unix.sys_read(f.impl.fd, &p[0], len(p))
+	n := unix.sys_read(f.impl.fd, &p[0], len(p))
 	if n < 0 {
 		return -1, _get_platform_error(n)
 	}
-	return n, nil
+	return i64(n), nil
 }
 
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
@@ -132,30 +138,25 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 		if m < 0 {
 			return -1, _get_platform_error(m)
 		}
-		n += m
+		n += i64(m)
 		b = b[m:]
 		offset += i64(m)
 	}
 	return
 }
 
-_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
-	//TODO
-	return
-}
-
-_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
+	n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
 	if n < 0 {
 		return -1, _get_platform_error(n)
 	}
-	return int(n), nil
+	return i64(n), nil
 }
 
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
@@ -166,18 +167,13 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 		if m < 0 {
 			return -1, _get_platform_error(m)
 		}
-		n += m
+		n += i64(m)
 		b = b[m:]
 		offset += i64(m)
 	}
 	return
 }
 
-_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
-	//TODO
-	return
-}
-
 _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
 	s: _Stat = ---
 	res := unix.sys_fstat(f.impl.fd, &s)
@@ -366,3 +362,49 @@ _is_dir_fd :: proc(fd: int) -> bool {
 _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) {
 	return strings.clone_to_cstring(name, context.temp_allocator)
 }
+
+
+@(private="package")
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	f := (^File)(stream_data)
+	ferr: Error
+	i: int
+	switch mode {
+	case .Read:
+		n, ferr = _read(f, p)
+		err = error_to_io_error(ferr)
+		return
+	case .Read_At:
+		n, ferr = _read_at(f, p, offset)
+		err = error_to_io_error(ferr)
+		return
+	case .Write:
+		n, ferr = _write(f, p)
+		err = error_to_io_error(ferr)
+		return
+	case .Write_At:
+		n, ferr = _write_at(f, p, offset)
+		err = error_to_io_error(ferr)
+		return
+	case .Seek:
+		n, ferr = _seek(f, offset, whence)
+		err = error_to_io_error(ferr)
+		return
+	case .Size:
+		n, ferr = _file_size(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Flush:
+		ferr = _flush(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Close, .Destroy:
+		ferr = _close(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Query:
+		return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
+	}
+	return 0, .Empty
+}
+

+ 6 - 76
core/os/os2/file_stream.odin

@@ -3,17 +3,15 @@ package os2
 import "core:io"
 
 to_stream :: proc(f: ^File) -> (s: io.Stream) {
-	s.stream_data = f
-	s.stream_vtable = &_file_stream_vtable
+	if f != nil {
+		assert(f.impl.stream.procedure != nil)
+		s = f.impl.stream
+	}
 	return
 }
 
-to_writer :: proc(f: ^File) -> (s: io.Writer) {
-	return {to_stream(f)}
-}
-to_reader :: proc(f: ^File) -> (s: io.Reader) {
-	return {to_stream(f)}
-}
+to_writer :: to_stream
+to_reader :: to_stream
 
 
 @(private)
@@ -23,71 +21,3 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
 	}
 	return ferr.(io.Error) or_else .Unknown
 }
-
-
-@(private)
-_file_stream_vtable := io.Stream_VTable{
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = read(f, p)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = read_at(f, p, offset)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = write_to(f, w)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = write(f, p)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = write_at(f, p, offset)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
-		f := (^File)(s.stream_data)
-		ferr: Error
-		n, ferr = read_from(f, r)
-		err = error_to_io_error(ferr)
-		return
-	},
-	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
-		f := (^File)(s.stream_data)
-		n, ferr := seek(f, offset, Seek_From(whence))
-		err := error_to_io_error(ferr)
-		return n, err
-	},
-	impl_size = proc(s: io.Stream) -> i64 {
-		f := (^File)(s.stream_data)
-		sz, _ := file_size(f)
-		return sz
-	},
-	impl_flush = proc(s: io.Stream) -> io.Error {
-		f := (^File)(s.stream_data)
-		ferr := flush(f)
-		return error_to_io_error(ferr)
-	},
-	impl_close = proc(s: io.Stream) -> io.Error {
-		f := (^File)(s.stream_data)
-		ferr := close(f)
-		return error_to_io_error(ferr)
-	},
-}

+ 68 - 23
core/os/os2/file_windows.odin

@@ -38,6 +38,8 @@ _File :: struct {
 	wname: win32.wstring,
 	kind: _File_Kind,
 
+	stream: io.Stream,
+
 	allocator: runtime.Allocator,
 
 	rw_mutex: sync.RW_Mutex, // read write calls
@@ -144,6 +146,11 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
 	}
 	f.impl.kind = kind
 
+	f.impl.stream = {
+		data = f,
+		procedure = _file_stream_proc,
+	}
+
 	return f
 }
 
@@ -181,7 +188,7 @@ _name :: proc(f: ^File) -> string {
 	return f.impl.name if f != nil else ""
 }
 
-_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
 	handle := _handle(f)
 	if handle == win32.INVALID_HANDLE {
 		return 0, .Invalid_File
@@ -208,7 +215,7 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
 	return i64(hi)<<32 + i64(dw_ptr), nil
 }
 
-_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 	read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
 		if len(b) == 0 {
 			return 0, nil
@@ -274,7 +281,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
 			n, err := read_console(handle, p[total_read:][:to_read])
 			total_read += n
 			if err != nil {
-				return int(total_read), err
+				return i64(total_read), err
 			}
 		} else {
 			ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
@@ -287,11 +294,11 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
 		}
 	}
 
-	return int(total_read), err
+	return i64(total_read), err
 }
 
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
-	pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
+	pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
 		buf := data
 		if len(buf) > MAX_RW {
 			buf = buf[:MAX_RW]
@@ -313,7 +320,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 			err = _get_platform_error()
 			done = 0
 		}
-		n = int(done)
+		n = i64(done)
 		return
 	}
 
@@ -329,12 +336,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 	return
 }
 
-_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
-	// TODO(bill)
-	return
-}
-
-_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 	if len(p) == 0 {
 		return
 	}
@@ -352,17 +354,17 @@ _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
 
 		e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
 		if single_write_length <= 0 || !e {
-			n = int(total_write)
+			n = i64(total_write)
 			err = _get_platform_error()
 			return
 		}
 		total_write += i64(single_write_length)
 	}
-	return int(total_write), nil
+	return i64(total_write), nil
 }
 
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
-	pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
+	pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
 		buf := data
 		if len(buf) > MAX_RW {
 			buf = buf[:MAX_RW]
@@ -382,7 +384,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 			err = _get_platform_error()
 			done = 0
 		}
-		n = int(done)
+		n = i64(done)
 		return
 	}
 
@@ -397,11 +399,6 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
 	return
 }
 
-_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
-	// TODO(bill)
-	return
-}
-
 _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
 	length: win32.LARGE_INTEGER
 	handle := _handle(f)
@@ -727,3 +724,51 @@ _is_dir :: proc(path: string) -> bool {
 	}
 	return false
 }
+
+
+@(private="package")
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	f := (^File)(stream_data)
+	ferr: Error
+	i: int
+	switch mode {
+	case .Read:
+		n, ferr = _read(f, p)
+		err = error_to_io_error(ferr)
+		return
+	case .Read_At:
+		n, ferr = _read_at(f, p, offset)
+		err = error_to_io_error(ferr)
+		return
+	case .Write:
+		n, ferr = _write(f, p)
+		err = error_to_io_error(ferr)
+		return
+	case .Write_At:
+		n, ferr = _write_at(f, p, offset)
+		err = error_to_io_error(ferr)
+		return
+	case .Seek:
+		n, ferr = _seek(f, offset, whence)
+		err = error_to_io_error(ferr)
+		return
+	case .Size:
+		n, ferr = _file_size(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Flush:
+		ferr = _flush(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Close:
+		ferr = _close(f)
+		err = error_to_io_error(ferr)
+		return
+	case .Query:
+		return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
+	case .Destroy:
+		return 0, .Empty
+	}
+	return 0, .Empty
+}
+

+ 5 - 4
core/os/os_darwin.odin

@@ -314,15 +314,16 @@ Dirent :: struct {
 
 Dir :: distinct rawptr // DIR*
 
+ADDRESS_FAMILY :: c.char
 SOCKADDR :: struct #packed {
 	len: c.char,
-	family: c.char,
+	family: ADDRESS_FAMILY,
 	sa_data: [14]c.char,
 }
 
 SOCKADDR_STORAGE_LH :: struct #packed {
 	len: c.char,
-	family: c.char,
+	family: ADDRESS_FAMILY,
 	__ss_pad1: [6]c.char,
 	__ss_align: i64,
 	__ss_pad2: [112]c.char,
@@ -330,7 +331,7 @@ SOCKADDR_STORAGE_LH :: struct #packed {
 
 sockaddr_in :: struct #packed {
 	sin_len: c.char,
-	sin_family: c.char,
+	sin_family: ADDRESS_FAMILY,
 	sin_port: u16be,
 	sin_addr: in_addr,
 	sin_zero: [8]c.char,
@@ -338,7 +339,7 @@ sockaddr_in :: struct #packed {
 
 sockaddr_in6 :: struct #packed {
 	sin6_len: c.char,
-	sin6_family: c.char,
+	sin6_family: ADDRESS_FAMILY,
 	sin6_port: u16be,
 	sin6_flowinfo: c.uint,
 	sin6_addr: in6_addr,

+ 29 - 2
core/os/os_linux.odin

@@ -432,6 +432,14 @@ AT_FDCWD            :: ~uintptr(99)	/* -100 */
 AT_REMOVEDIR        :: uintptr(0x200)
 AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
 
+pollfd :: struct {
+	fd:      c.int,
+	events:  c.short,
+	revents: c.short,
+}
+
+sigset_t :: distinct u64
+
 foreign libc {
 	@(link_name="__errno_location") __errno_location    :: proc() -> ^int ---
 
@@ -450,6 +458,7 @@ foreign libc {
 	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> int ---
 	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
 	@(link_name="putenv")           _unix_putenv        :: proc(cstring) -> c.int ---
+	@(link_name="setenv")           _unix_setenv        :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
 	@(link_name="realpath")         _unix_realpath      :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
 
 	@(link_name="exit")             _unix_exit          :: proc(status: c.int) -> ! ---
@@ -885,8 +894,10 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string)
 
 set_env :: proc(key, value: string) -> Errno {
 	runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
-	s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
-	res := _unix_putenv(strings.unsafe_string_to_cstring(s))
+	key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
+	value_cstring := strings.clone_to_cstring(value, context.temp_allocator)
+	// NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly
+	res := _unix_setenv(key_cstring, value_cstring, 1)
 	if res < 0 {
 		return Errno(get_last_error())
 	}
@@ -1087,3 +1098,19 @@ fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) {
 	}
 	return result, ERROR_NONE
 }
+
+poll :: proc(fds: []pollfd, timeout: int) -> (int, Errno) {
+	result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout)
+	if result < 0 {
+		return 0, _get_errno(result)
+	}
+	return result, ERROR_NONE
+}
+
+ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) {
+	result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t))
+	if result < 0 {
+		return 0, _get_errno(result)
+	}
+	return result, ERROR_NONE
+}

+ 46 - 52
core/os/stream.odin

@@ -4,66 +4,60 @@ import "core:io"
 
 stream_from_handle :: proc(fd: Handle) -> io.Stream {
 	s: io.Stream
-	s.stream_data = rawptr(uintptr(fd))
-	s.stream_vtable = &_file_stream_vtable
+	s.data = rawptr(uintptr(fd))
+	s.procedure = _file_stream_proc
 	return s
 }
 
 
 @(private)
-_file_stream_vtable := io.Stream_VTable{
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		fd := Handle(uintptr(s.stream_data))
-		os_err: Errno
-		n, os_err = read(fd, p)
-		return
-	},
-	impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		when ODIN_OS == .Windows || ODIN_OS == .WASI {
-			fd := Handle(uintptr(s.stream_data))
-			os_err: Errno
-			n, os_err = read_at(fd, p, offset)
-		}
-		return
-	},
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		fd := Handle(uintptr(s.stream_data))
-		os_err: Errno
-		n, os_err = write(fd, p)
-		return
-	},
-	impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
-		when ODIN_OS == .Windows || ODIN_OS == .WASI {
-			fd := Handle(uintptr(s.stream_data))
-			os_err: Errno
-			n, os_err = write_at(fd, p, offset)
-			_ = os_err
-		}
-		return
-	},
-	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
-		fd := Handle(uintptr(s.stream_data))
-		n, os_err := seek(fd, offset, int(whence))
-		_ = os_err
-		return n, nil
-	},
-	impl_size = proc(s: io.Stream) -> i64 {
-		fd := Handle(uintptr(s.stream_data))
-		sz, _ := file_size(fd)
-		return sz
-	},
-	impl_flush = proc(s: io.Stream) -> io.Error {
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	fd := Handle(uintptr(stream_data))
+	n_int: int
+	os_err: Errno
+	switch mode {
+	case .Close:
+		close(fd)
+	case .Flush:
 		when ODIN_OS == .Windows {
-			fd := Handle(uintptr(s.stream_data))
 			flush(fd)
 		} else {
 			// TOOD(bill): other operating systems
 		}
-		return nil
-	},
-	impl_close = proc(s: io.Stream) -> io.Error {
-		fd := Handle(uintptr(s.stream_data))
-		close(fd)
-		return nil
-	},
+	case .Read:
+		n_int, os_err = read(fd, p)
+		n = i64(n_int)
+		if os_err != 0 {
+			err = .Unknown
+		}
+	case .Read_At:
+		when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+			n_int, os_err = read_at(fd, p, offset)
+			n = i64(n_int)
+		}
+	case .Write:
+		n_int, os_err = write(fd, p)
+		n = i64(n_int)
+	case .Write_At:
+		when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+			n_int, os_err = write_at(fd, p, offset)
+			n = i64(n_int)
+		}
+	case .Seek:
+		n, os_err = seek(fd, offset, int(whence))
+	case .Size:
+		n, os_err = file_size(fd)
+	case .Destroy:
+		err = .Empty
+	case .Query:
+		when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
+			return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query})
+		} else {
+			return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
+		}
+	}
+	if err == nil && os_err != 0 {
+		err = .Unknown
+	}
+	return
 }

+ 2 - 2
core/reflect/reflect.odin

@@ -781,7 +781,7 @@ set_union_variant_raw_tag :: proc(a: any, tag: i64) {
 		tag_ptr := uintptr(a.data) + info.tag_offset
 		tag_any := any{rawptr(tag_ptr), info.tag_type.id}
 
-		switch i in &tag_any {
+		switch &i in tag_any {
 		case u8:   i = u8(tag)
 		case i8:   i = i8(tag)
 		case u16:  i = u16(tag)
@@ -1312,7 +1312,7 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid)
 
 	ptr_any := any{data, base_integer_id}
 	ptr: rawptr
-	switch i in &ptr_any {
+	switch &i in ptr_any {
 	case u8:    ptr = _handle(&i)
 	case u16:   ptr = _handle(&i)
 	case u32:   ptr = _handle(&i)

+ 6 - 6
core/runtime/core_builtin.odin

@@ -112,7 +112,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
 // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
 @builtin
 pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
-	assert(len(array) > 0, "", loc)
+	assert(len(array) > 0, loc=loc)
 	res = array[len(array)-1]
 	(^Raw_Dynamic_Array)(array).len -= 1
 	return res
@@ -136,7 +136,7 @@ pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check
 // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
 @builtin
 pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
-	assert(len(array) > 0, "", loc)
+	assert(len(array) > 0, loc=loc)
 	res = array[0]
 	if len(array) > 1 {
 		copy(array[0:], array[1:])
@@ -424,7 +424,7 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
 			a := (^Raw_Dynamic_Array)(array)
 			when size_of(E) != 0 {
 				data := ([^]E)(a.data)
-				assert(condition=data != nil, loc=loc)
+				assert(data != nil, loc=loc)
 				data[a.len] = arg
 			}
 			a.len += 1
@@ -459,7 +459,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
 			a := (^Raw_Dynamic_Array)(array)
 			when size_of(E) != 0 {
 				data := ([^]E)(a.data)
-				assert(condition=data != nil, loc=loc)
+				assert(data != nil, loc=loc)
 				intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
 			}
 			a.len += arg_len
@@ -472,7 +472,7 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
 @builtin
 append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
 	args := transmute([]E)arg
-	return append_elems(array=array, args=args, loc=loc)
+	return append_elems(array, ..args, loc=loc)
 }
 
 
@@ -481,7 +481,7 @@ append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #ca
 append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
 	n_arg: int
 	for arg in args {
-		n_arg, err = append(array = array, args = transmute([]E)(arg), loc = loc)
+		n_arg, err = append(array, ..transmute([]E)(arg), loc=loc)
 		n += n_arg
 		if err != nil {
 			return

+ 2 - 2
core/runtime/default_allocators_js.odin

@@ -1,5 +1,5 @@
 //+build js
 package runtime
 
-default_allocator_proc :: nil_allocator_proc
-default_allocator :: nil_allocator
+default_allocator_proc :: panic_allocator_proc
+default_allocator :: panic_allocator

+ 49 - 1
core/runtime/default_allocators_nil.odin

@@ -35,4 +35,52 @@ nil_allocator :: proc() -> Allocator {
 when ODIN_OS == .Freestanding {
 	default_allocator_proc :: nil_allocator_proc
 	default_allocator :: nil_allocator
-} 
+} 
+
+
+
+panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
+                             size, alignment: int,
+                             old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	switch mode {
+	case .Alloc:
+		if size > 0 {
+			panic("panic allocator, .Alloc called", loc=loc)
+		}
+	case .Alloc_Non_Zeroed:
+		if size > 0 {
+			panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
+		}
+	case .Resize:
+		if size > 0 {
+			panic("panic allocator, .Resize called", loc=loc)
+		}
+	case .Free:
+		if old_memory != nil {
+			panic("panic allocator, .Free called", loc=loc)
+		}
+	case .Free_All:
+		panic("panic allocator, .Free_All called", loc=loc)
+
+	case .Query_Features:
+		set := (^Allocator_Mode_Set)(old_memory)
+		if set != nil {
+			set^ = {.Query_Features}
+		}
+		return nil, nil
+
+	case .Query_Info:
+		panic("panic allocator, .Query_Info called", loc=loc)
+	}
+
+	return nil, nil
+}
+
+panic_allocator :: proc() -> Allocator {
+	return Allocator{
+		procedure = nil_allocator_proc,
+		data = nil,
+	}
+}
+
+

+ 2 - 2
core/runtime/default_allocators_wasi.odin

@@ -1,5 +1,5 @@
 //+build wasi
 package runtime
 
-default_allocator_proc :: nil_allocator_proc
-default_allocator :: nil_allocator
+default_allocator_proc :: panic_allocator_proc
+default_allocator :: panic_allocator

+ 10 - 10
core/runtime/error_checks.odin

@@ -22,7 +22,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
+	handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
 		print_caller_location(Source_Code_Location{file, line, column, ""})
 		print_string(" Index ")
 		print_i64(i64(index))
@@ -83,7 +83,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
+	handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
 		print_caller_location(Source_Code_Location{file, line, column, ""})
 		print_string(" Invalid dynamic array indices ")
 		print_i64(i64(low))
@@ -104,7 +104,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
+	handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
 		print_caller_location(Source_Code_Location{file, line, column, ""})
 		print_string(" Matrix indices [")
 		print_i64(i64(row_index))
@@ -128,7 +128,7 @@ when ODIN_NO_RTTI {
 			return
 		}
 		@(cold)
-		handle_error :: proc "contextless" (file: string, line, column: i32) {
+		handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
 			print_caller_location(Source_Code_Location{file, line, column, ""})
 			print_string(" Invalid type assertion\n")
 			type_assertion_trap()
@@ -141,7 +141,7 @@ when ODIN_NO_RTTI {
 			return
 		}
 		@(cold)
-		handle_error :: proc "contextless" (file: string, line, column: i32) {
+		handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
 			print_caller_location(Source_Code_Location{file, line, column, ""})
 			print_string(" Invalid type assertion\n")
 			type_assertion_trap()
@@ -154,7 +154,7 @@ when ODIN_NO_RTTI {
 			return
 		}
 		@(cold)
-		handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) {
+		handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
 			print_caller_location(Source_Code_Location{file, line, column, ""})
 			print_string(" Invalid type assertion from ")
 			print_typeid(from)
@@ -199,7 +199,7 @@ when ODIN_NO_RTTI {
 		}
 
 		@(cold)
-		handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+		handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
 
 			actual := variant_type(from, from_data)
 
@@ -225,7 +225,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
+	handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
 		print_caller_location(loc)
 		print_string(" Invalid slice length for make: ")
 		print_i64(i64(len))
@@ -240,7 +240,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
+	handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int)  -> ! {
 		print_caller_location(loc)
 		print_string(" Invalid dynamic array parameters for make: ")
 		print_i64(i64(len))
@@ -257,7 +257,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
 		return
 	}
 	@(cold)
-	handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
+	handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int)  -> ! {
 		print_caller_location(loc)
 		print_string(" Invalid map capacity for make: ")
 		print_i64(i64(cap))

+ 27 - 6
core/runtime/internal.odin

@@ -566,16 +566,37 @@ max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
 }
 
 abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
-	r, i := real(x), imag(x)
-	return f16(intrinsics.sqrt(f32(r*r + i*i)))
+	p, q := abs(real(x)), abs(imag(x))
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * f16(intrinsics.sqrt(f32(1 + q*q)))
 }
 abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 {
-	r, i := real(x), imag(x)
-	return intrinsics.sqrt(r*r + i*i)
+	p, q := abs(real(x)), abs(imag(x))
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * intrinsics.sqrt(1 + q*q)
 }
 abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 {
-	r, i := real(x), imag(x)
-	return intrinsics.sqrt(r*r + i*i)
+	p, q := abs(real(x)), abs(imag(x))
+	if p < q {
+		p, q = q, p
+	}
+	if p == 0 {
+		return 0
+	}
+	q = q / p
+	return p * intrinsics.sqrt(1 + q*q)
 }
 abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 {
 	r, i, j, k := real(x), imag(x), jmag(x), kmag(x)

+ 10 - 10
core/slice/ptr.odin

@@ -1,7 +1,7 @@
 package slice
 
 import "core:builtin"
-import "core:mem"
+import "core:runtime"
 
 ptr_add :: proc(p: $P/^$T, x: int) -> ^T {
 	return ([^]T)(p)[x:]
@@ -27,9 +27,9 @@ ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) {
 		a := rawptr(uintptr(x) + uintptr(i))
 		b := rawptr(uintptr(y) + uintptr(i))
 
-		mem.copy(t, a, BLOCK_SIZE)
-		mem.copy(a, b, BLOCK_SIZE)
-		mem.copy(b, t, BLOCK_SIZE)
+		runtime.mem_copy(t, a, BLOCK_SIZE)
+		runtime.mem_copy(a, b, BLOCK_SIZE)
+		runtime.mem_copy(b, t, BLOCK_SIZE)
 	}
 
 	if i < len {
@@ -38,9 +38,9 @@ ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) {
 		a := rawptr(uintptr(x) + uintptr(i))
 		b := rawptr(uintptr(y) + uintptr(i))
 
-		mem.copy(t, a, rem)
-		mem.copy(a, b, rem)
-		mem.copy(b, t, rem)
+		runtime.mem_copy(t, a, rem)
+		runtime.mem_copy(a, b, rem)
+		runtime.mem_copy(b, t, rem)
 	}
 }
 
@@ -59,9 +59,9 @@ ptr_swap_overlapping :: proc(x, y: rawptr, len: int) {
 	
 	for n := len; n > 0; n -= N {
 		m := builtin.min(n, N)
-		mem.copy(&buffer, a, m)
-		mem.copy(a, b, m)
-		mem.copy(b, &buffer, m)
+		runtime.mem_copy(&buffer, a, m)
+		runtime.mem_copy(a, b, m)
+		runtime.mem_copy(b, &buffer, m)
 		
 		a, b = a[N:], b[N:]
 	}

+ 18 - 12
core/slice/slice.odin

@@ -3,12 +3,12 @@ package slice
 import "core:intrinsics"
 import "core:builtin"
 import "core:math/bits"
-import "core:mem"
+import "core:runtime"
 
 _ :: intrinsics
 _ :: builtin
 _ :: bits
-_ :: mem
+_ :: runtime
 
 /*
 	Turn a pointer and a length into a slice.
@@ -164,7 +164,7 @@ equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) {
 		return false
 	}
 	when intrinsics.type_is_simple_compare(E) {
-		return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
+		return runtime.memory_compare(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
 	} else {
 		for i in 0..<len(a) {
 			if a[i] != b[i] {
@@ -180,7 +180,7 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp
 	if len(a) != len(b) {
 		return false
 	}
-	return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
+	return runtime.memory_compare(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
 }
 
 /*
@@ -220,6 +220,12 @@ has_suffix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_c
 	return false
 }
 
+zero :: proc(array: $T/[]$E) #no_bounds_check {
+	if len(array) > 0 {
+		intrinsics.mem_zero(raw_data(array), size_of(E)*len(array))
+	}
+}
+
 fill :: proc(array: $T/[]$E, value: E) #no_bounds_check {
 	if len(array) <= 0 {
 		return
@@ -250,7 +256,7 @@ swap_with_slice :: proc(a, b: $T/[]$E, loc := #caller_location) {
 }
 
 @(require_results)
-concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, err: mem.Allocator_Error) #optional_allocator_error {
+concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, err: runtime.Allocator_Error) #optional_allocator_error {
 	if len(a) == 0 {
 		return
 	}
@@ -268,7 +274,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, er
 
 // copies a slice into a new slice
 @(require_results)
-clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, mem.Allocator_Error) #optional_allocator_error {
+clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, runtime.Allocator_Error) #optional_allocator_error {
 	d, err := make([]E, len(a), allocator)
 	copy(d[:], a)
 	return d, err
@@ -276,7 +282,7 @@ clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, mem.Allocator
 
 
 // copies slice into a new dynamic array
-clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> ([dynamic]E, mem.Allocator_Error) #optional_allocator_error {
+clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> ([dynamic]E, runtime.Allocator_Error) #optional_allocator_error {
 	d, err := make([dynamic]E, len(a), allocator)
 	copy(d[:], a)
 	return d, err
@@ -286,12 +292,12 @@ to_dynamic :: clone_to_dynamic
 // Converts slice into a dynamic array without cloning or allocating memory
 @(require_results)
 into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
-	s := transmute(mem.Raw_Slice)a
-	d := mem.Raw_Dynamic_Array{
+	s := transmute(runtime.Raw_Slice)a
+	d := runtime.Raw_Dynamic_Array{
 		data = s.data,
 		len  = 0,
 		cap  = s.len,
-		allocator = mem.nil_allocator(),
+		allocator = runtime.nil_allocator(),
 	}
 	return transmute([dynamic]E)d
 }
@@ -373,7 +379,7 @@ as_ptr :: proc(array: $T/[]$E) -> [^]E {
 
 
 @(require_results)
-mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> (r: []V, err: mem.Allocator_Error) #optional_allocator_error {
+mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> (r: []V, err: runtime.Allocator_Error) #optional_allocator_error {
 	r = make([]V, len(s), allocator) or_return
 	for v, i in s {
 		r[i] = f(v)
@@ -402,7 +408,7 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
 }
 
 @(require_results)
-scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> (res: []V, err: mem.Allocator_Error) #optional_allocator_error {
+scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> (res: []V, err: runtime.Allocator_Error) #optional_allocator_error {
 	if len(s) == 0 { return }
 
 	res = make([]V, len(s), allocator) or_return

+ 16 - 25
core/strings/builder.odin

@@ -164,36 +164,27 @@ builder_init :: proc{
 	builder_init_len_cap,
 }
 @(private)
-_builder_stream_vtable_obj := io.Stream_VTable{
-	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		b := (^Builder)(s.stream_data)
-		n = write_bytes(b, p)
-		if n < len(p) {
+_builder_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	b := (^Builder)(stream_data)
+	#partial switch mode {
+	case .Write:
+		n = i64(write_bytes(b, p))
+		if n < i64(len(p)) {
 			err = .EOF
 		}
 		return
-	},
-	impl_write_byte = proc(s: io.Stream, c: byte) -> (err: io.Error) {
-		b := (^Builder)(s.stream_data)
-		n := write_byte(b, c)
-		if n == 0 {
-			err = .EOF
-		}
+	case .Size:
+		n = i64(len(b.buf))
 		return
-	},
-	impl_size = proc(s: io.Stream) -> i64 {
-		b := (^Builder)(s.stream_data)
-		return i64(len(b.buf))
-	},
-	impl_destroy = proc(s: io.Stream) -> io.Error {
-		b := (^Builder)(s.stream_data)
+	case .Destroy:
 		builder_destroy(b)
-		return .None
-	},
+		return
+	case .Query:
+		return io.query_utility({.Write, .Size, .Destroy, .Query})
+	}
+	return 0, .Empty
 }
-// NOTE(dweiler): Work around a miscompilation bug on Linux still.
-@(private)
-_builder_stream_vtable := &_builder_stream_vtable_obj
+
 /*
 Returns an io.Stream from a Builder
 
@@ -204,7 +195,7 @@ Returns:
 - res: the io.Stream
 */
 to_stream :: proc(b: ^Builder) -> (res: io.Stream) {
-	return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
+	return io.Stream{procedure=_builder_stream_proc, data=b}
 }
 /*
 Returns an io.Writer from a Builder

+ 19 - 39
core/strings/reader.odin

@@ -35,8 +35,8 @@ Returns:
 - s: An io.Stream for the given Reader
 */
 reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
-	s.stream_data = r
-	s.stream_vtable = &_reader_vtable
+	s.data = r
+	s.procedure = _reader_proc
 	return
 }
 /*
@@ -294,41 +294,21 @@ This VTable is used by the Reader struct to provide its functionality
 as an `io.Stream`.
 */
 @(private)
-_reader_vtable := io.Stream_VTable{
-	impl_size = proc(s: io.Stream) -> i64 {
-		r := (^Reader)(s.stream_data)
-		return reader_size(r)
-	},
-	impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read(r, p)
-	},
-	impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_at(r, p, off)
-	},
-	impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_byte(r)
-	},
-	impl_unread_byte = proc(s: io.Stream) -> io.Error {
-		r := (^Reader)(s.stream_data)
-		return reader_unread_byte(r)
-	},
-	impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_read_rune(r)
-	},
-	impl_unread_rune = proc(s: io.Stream) -> io.Error {
-		r := (^Reader)(s.stream_data)
-		return reader_unread_rune(r)
-	},
-	impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_seek(r, offset, whence)
-	},
-	impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
-		r := (^Reader)(s.stream_data)
-		return reader_write_to(r, w)
-	},
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+	r := (^Reader)(stream_data)
+	#partial switch mode {
+	case .Size:
+		n = reader_size(r)
+		return
+	case .Read:
+		return io._i64_err(reader_read(r, p))
+	case .Read_At:
+		return io._i64_err(reader_read_at(r, p, offset))
+	case .Seek:
+		n, err = reader_seek(r, offset, whence)
+		return
+	case .Query:
+		return io.query_utility({.Size, .Read, .Read_At, .Seek, .Query})
+	}
+	return 0, .Empty
 }

+ 4 - 4
core/strings/strings.odin

@@ -1194,7 +1194,7 @@ Output:
 split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	sep :: "\n"
 	lines := _split(s, sep, 0, -1, allocator) or_return
-	for line in &lines {
+	for &line in lines {
 		line = _trim_cr(line)
 	}
 	return lines, nil
@@ -1234,7 +1234,7 @@ Output:
 split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	sep :: "\n"
 	lines := _split(s, sep, 0, n, allocator) or_return
-	for line in &lines {
+	for &line in lines {
 		line = _trim_cr(line)
 	}
 	return lines, nil
@@ -1273,7 +1273,7 @@ Output:
 split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	sep :: "\n"
 	lines := _split(s, sep, len(sep), -1, allocator) or_return
-	for line in &lines {
+	for &line in lines {
 		line = _trim_cr(line)
 	}
 	return lines, nil
@@ -1314,7 +1314,7 @@ Output:
 split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	sep :: "\n"
 	lines := _split(s, sep, len(sep), n, allocator) or_return
-	for line in &lines {
+	for &line in lines {
 		line = _trim_cr(line)
 	}
 	return lines, nil

+ 34 - 0
core/sys/unix/syscalls_linux.odin

@@ -1567,6 +1567,23 @@ MADV_HWPOISON    :: 100
 // pipe2 flags
 O_CLOEXEC :: 0o2000000
 
+// poll events
+POLLIN         :: 0x0001
+POLLPRI        :: 0x0002
+POLLOUT        :: 0x0004
+POLLERR        :: 0x0008
+POLLHUP        :: 0x0010
+POLLNVAL       :: 0x0020
+POLLRDNORM     :: 0x0040
+POLLRDBAND     :: 0x0080
+POLLWRNORM     :: 0x0100
+POLLWRBAND     :: 0x0200
+POLLMSG        :: 0x0400
+POLLREMOVE     :: 0x1000
+POLLRDHUP      :: 0x2000
+POLLFREE       :: 0x4000
+POLL_BUSY_LOOP :: 0x8000
+
 // perf event data
 Perf_Sample :: struct #raw_union {
 	period:    u64,
@@ -2057,6 +2074,23 @@ sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int {
 	return int(intrinsics.syscall(SYS_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg)))
 }
 
+sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int {
+	// NOTE: specialcased here because `arm64` does not have `poll`
+	when ODIN_ARCH == .arm64 {
+		seconds := i64(timeout / 1_000)
+		nanoseconds := i64((timeout % 1000) * 1_000_000)
+		timeout_spec := timespec{seconds, nanoseconds}
+		
+		return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(&timeout_spec), uintptr(0), uintptr(8)))
+	} else {
+		return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout)))
+	}
+}
+
+sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int {
+	return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(timeout), uintptr(sigmask), uintptr(sigsetsize)))
+}
+
 get_errno :: proc "contextless" (res: int) -> i32 {
 	if res < 0 && res > -4096 {
 		return i32(-res)

+ 1 - 1
core/testing/runner_windows.odin

@@ -191,7 +191,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) {
 		global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc)
 		
 		context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
-			errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc)
+			errorf(global_current_t, "%s %s", prefix, message, loc=loc)
 			intrinsics.trap()
 		}
 		

+ 5 - 5
core/testing/testing.odin

@@ -46,15 +46,15 @@ errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
 }
 
 fail :: proc(t: ^T, loc := #caller_location) {
-	error(t=t, args={"FAIL"}, loc=loc)
+	error(t, "FAIL", loc=loc)
 	t.error_count += 1
 }
 
 fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
 	if msg != "" {
-		error(t=t, args={"FAIL:", msg}, loc=loc)
+		error(t, "FAIL:", msg, loc=loc)
 	} else {
-		error(t=t, args={"FAIL"}, loc=loc)
+		error(t, "FAIL", loc=loc)
 	}
 	t.error_count += 1
 	if t._fail_now != nil {
@@ -84,14 +84,14 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
 
 expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
 	if !ok {
-		error(t=t, args={msg}, loc=loc)
+		error(t, msg, loc=loc)
 	}
 	return ok
 }
 expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
 	ok := value == expected
 	if !ok {
-		errorf(t=t, format="expected %v, got %v", args={expected, value}, loc=loc)
+		errorf(t, "expected %v, got %v", expected, value, loc=loc)
 	}
 	return ok
 }

+ 2 - 2
core/text/i18n/i18n.odin

@@ -170,8 +170,8 @@ destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator)
 		return
 	}
 
-	for section in &catalog.k_v {
-		for key in &catalog.k_v[section] {
+	for section in catalog.k_v {
+		for key in catalog.k_v[section] {
 			delete(catalog.k_v[section][key])
 		}
 		delete(catalog.k_v[section])

+ 1 - 1
core/text/table/table.odin

@@ -114,7 +114,7 @@ set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment
 
 format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string {
 	context.allocator = tbl.format_allocator
-	return fmt.aprintf(fmt = _fmt, args = args)
+	return fmt.aprintf(_fmt, ..args)
 }
 
 header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {

+ 33 - 100
core/thread/thread.odin

@@ -10,8 +10,16 @@ Thread_Proc :: #type proc(^Thread)
 
 MAX_USER_ARGUMENTS :: 8
 
+Thread_State :: enum u8 {
+	Started,
+	Joined,
+	Done,
+	Self_Cleanup,
+}
+
 Thread :: struct {
 	using specific: Thread_Os_Specific,
+	flags: bit_set[Thread_State; u8],
 	id:             int,
 	procedure:      Thread_Proc,
 
@@ -38,7 +46,7 @@ Thread :: struct {
 
 		IMPORTANT:
 		By default, the thread proc will get the same context as `main()` gets.
-		In this sitation, the thread will get a new temporary allocator which will be cleaned up when the thread dies.
+		In this situation, the thread will get a new temporary allocator which will be cleaned up when the thread dies.
 		***This does NOT happen when you set `init_context`.***
 		This means that if you set `init_context`, but still have the `temp_allocator` field set to the default temp allocator,
 		then you'll need to call `runtime.default_temp_allocator_destroy(auto_cast the_thread.init_context.temp_allocator.data)` manually,
@@ -47,7 +55,6 @@ Thread :: struct {
 	*/
 	init_context: Maybe(runtime.Context),
 
-
 	creation_allocator: mem.Allocator,
 }
 
@@ -101,125 +108,46 @@ yield :: proc() {
 
 
 run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
-	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc())t.data
-		fn()
-		destroy(t)
-	}
-	t := create(thread_proc, priority)
-	t.data = rawptr(fn)
-	t.init_context = init_context
-	start(t)
+	create_and_start(fn, init_context, priority, true)
 }
 
 run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
-	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc(rawptr))t.data
-		assert(t.user_index >= 1)
-		data := t.user_args[0]
-		fn(data)
-		destroy(t)
-	}
-	t := create(thread_proc, priority)
-	t.data = rawptr(fn)
-	t.user_index = 1
-	t.user_args = data
-	t.init_context = init_context
-	start(t)
+	create_and_start_with_data(data, fn, init_context, priority, true)
 }
 
 run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T) <= size_of(rawptr) {
-	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc(T))t.data
-		assert(t.user_index >= 1)
-		data := (^T)(&t.user_args[0])^
-		fn(data)
-		destroy(t)
-	}
-	t := create(thread_proc, priority)
-	t.data = rawptr(fn)
-	t.user_index = 1
-	data := data
-	mem.copy(&t.user_args[0], &data, size_of(data))
-	t.init_context = init_context
-	start(t)
+	create_and_start_with_poly_data(data, fn, init_context, priority, true)
 }
 
 run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr) {
-	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc(T1, T2))t.data
-		assert(t.user_index >= 2)
-		arg1 := (^T1)(&t.user_args[0])^
-		arg2 := (^T2)(&t.user_args[1])^
-		fn(arg1, arg2)
-		destroy(t)
-	}
-	t := create(thread_proc, priority)
-	t.data = rawptr(fn)
-	t.user_index = 2
-	arg1, arg2 := arg1, arg2
-	mem.copy(&t.user_args[0], &arg1, size_of(arg1))
-	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
-	t.init_context = init_context
-	start(t)
+	create_and_start_with_poly_data2(arg1, arg2, fn, init_context, priority, true)
 }
 
 run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr),
 	      size_of(T3) <= size_of(rawptr) {
-	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc(T1, T2, T3))t.data
-		assert(t.user_index >= 3)
-		arg1 := (^T1)(&t.user_args[0])^
-		arg2 := (^T2)(&t.user_args[1])^
-		arg3 := (^T3)(&t.user_args[2])^
-		fn(arg1, arg2, arg3)
-		destroy(t)
-	}
-	t := create(thread_proc, priority)
-	t.data = rawptr(fn)
-	t.user_index = 3
-	arg1, arg2, arg3 := arg1, arg2, arg3
-	mem.copy(&t.user_args[0], &arg1, size_of(arg1))
-	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
-	mem.copy(&t.user_args[2], &arg3, size_of(arg3))
-	t.init_context = init_context
-	start(t)
+	create_and_start_with_poly_data3(arg1, arg2, arg3, fn, init_context, priority, true)
 }
 run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr),
 	      size_of(T3) <= size_of(rawptr) {
+	create_and_start_with_poly_data4(arg1, arg2, arg3, arg4, fn, init_context, priority, true)
+}
+
+
+create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 	thread_proc :: proc(t: ^Thread) {
-		fn := cast(proc(T1, T2, T3, T4))t.data
-		assert(t.user_index >= 4)
-		arg1 := (^T1)(&t.user_args[0])^
-		arg2 := (^T2)(&t.user_args[1])^
-		arg3 := (^T3)(&t.user_args[2])^
-		arg4 := (^T4)(&t.user_args[3])^
-		fn(arg1, arg2, arg3, arg4)
-		destroy(t)
+		fn := cast(proc())t.data
+		fn()
 	}
 	t := create(thread_proc, priority)
 	t.data = rawptr(fn)
-	t.user_index = 4
-	arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4
-	mem.copy(&t.user_args[0], &arg1, size_of(arg1))
-	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
-	mem.copy(&t.user_args[2], &arg3, size_of(arg3))
-	mem.copy(&t.user_args[3], &arg4, size_of(arg4))
-	t.init_context = init_context
-	start(t)
-}
-
-
-
-create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
-	t := create(fn, priority)
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
@@ -228,7 +156,7 @@ create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) =
 
 
 
-create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
+create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
 	thread_proc :: proc(t: ^Thread) {
 		fn := cast(proc(rawptr))t.data
 		assert(t.user_index >= 1)
@@ -239,12 +167,13 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
 	t.data = rawptr(fn)
 	t.user_index = 1
 	t.user_args = data
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
 }
 
-create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread 
+create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T) <= size_of(rawptr) {
 	thread_proc :: proc(t: ^Thread) {
 		fn := cast(proc(T))t.data
@@ -257,12 +186,13 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
 	t.user_index = 1
 	data := data
 	mem.copy(&t.user_args[0], &data, size_of(data))
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
 }
 
-create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread 
+create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr) {
 	thread_proc :: proc(t: ^Thread) {
@@ -278,12 +208,13 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
 	arg1, arg2 := arg1, arg2
 	mem.copy(&t.user_args[0], &arg1, size_of(arg1))
 	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
 }
 
-create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread 
+create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr),
 	      size_of(T3) <= size_of(rawptr) {
@@ -302,11 +233,12 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
 	mem.copy(&t.user_args[0], &arg1, size_of(arg1))
 	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
 	mem.copy(&t.user_args[2], &arg3, size_of(arg3))
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
 }
-create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread 
+create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
 	where size_of(T1) <= size_of(rawptr),
 	      size_of(T2) <= size_of(rawptr),
 	      size_of(T3) <= size_of(rawptr) {
@@ -327,6 +259,7 @@ create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4:
 	mem.copy(&t.user_args[1], &arg2, size_of(arg2))
 	mem.copy(&t.user_args[2], &arg3, size_of(arg3))
 	mem.copy(&t.user_args[3], &arg4, size_of(arg4))
+	if self_cleanup do t.flags += {.Self_Cleanup}
 	t.init_context = init_context
 	start(t)
 	return t
@@ -360,4 +293,4 @@ _maybe_destroy_default_temp_allocator :: proc(init_context: Maybe(runtime.Contex
 	if context.temp_allocator.procedure == runtime.default_temp_allocator_proc {
 		runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
 	}
-}
+}

+ 1 - 9
core/thread/thread_js.odin

@@ -5,15 +5,7 @@ import "core:intrinsics"
 import "core:sync"
 import "core:mem"
 
-Thread_State :: enum u8 {
-	Started,
-	Joined,
-	Done,
-}
-
-Thread_Os_Specific :: struct {
-	flags:       bit_set[Thread_State; u8],
-}
+Thread_Os_Specific :: struct {}
 
 _thread_priority_map := [Thread_Priority]i32{
 	.Normal = 0,

+ 1 - 1
core/thread/thread_pool.odin

@@ -81,7 +81,7 @@ pool_destroy :: proc(pool: ^Pool) {
 	delete(pool.tasks)
 	delete(pool.tasks_done)
 
-	for t in &pool.threads {
+	for &t in pool.threads {
 		destroy(t)
 	}
 

+ 7 - 7
core/thread/thread_unix.odin

@@ -8,19 +8,12 @@ import "core:sys/unix"
 
 CAS :: intrinsics.atomic_compare_exchange_strong
 
-Thread_State :: enum u8 {
-	Started,
-	Joined,
-	Done,
-}
-
 // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
 // Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
 Thread_Os_Specific :: struct #align 16 {
 	unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
 	cond:        sync.Cond,
 	mutex:       sync.Mutex,
-	flags:       bit_set[Thread_State; u8],
 }
 //
 // Creates a thread which will run the given procedure.
@@ -67,6 +60,13 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 
 		sync.unlock(&t.mutex)
 
+		if .Self_Cleanup in t.flags {
+			t.unix_thread = {}
+			// NOTE(ftphikari): It doesn't matter which context 'free' received, right?
+			context = {}
+			free(t, t.creation_allocator)
+		}
+
 		return nil
 	}
 

+ 8 - 7
core/thread/thread_windows.odin

@@ -6,17 +6,10 @@ import "core:intrinsics"
 import "core:sync"
 import win32 "core:sys/windows"
 
-Thread_State :: enum u8 {
-	Started,
-	Joined,
-	Done,
-}
-
 Thread_Os_Specific :: struct {
 	win32_thread:    win32.HANDLE,
 	win32_thread_id: win32.DWORD,
 	mutex:           sync.Mutex,
-	flags:           bit_set[Thread_State; u8],
 }
 
 _thread_priority_map := [Thread_Priority]i32{
@@ -47,6 +40,14 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 
 		intrinsics.atomic_store(&t.flags, t.flags + {.Done})
 
+		if .Self_Cleanup in t.flags {
+			win32.CloseHandle(t.win32_thread)
+			t.win32_thread = win32.INVALID_HANDLE
+			// NOTE(ftphikari): It doesn't matter which context 'free' received, right?
+			context = {}
+			free(t, t.creation_allocator)
+		}
+
 		return 0
 	}
 

+ 1 - 1
core/unicode/tools/generate_entity_table.odin

@@ -221,7 +221,7 @@ named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) {
 
 	delete(entity_map)
 	delete(names)
-	for name in &names {
+	for &name in names {
 		free(&name)
 	}
 }

+ 0 - 2
examples/all/all_experimental.odin

@@ -3,8 +3,6 @@ package all
 
 import c_tokenizer    "core:c/frontend/tokenizer"
 import c_preprocessor "core:c/frontend/preprocessor"
-import virtual "core:mem/virtual"
 
 _ :: c_tokenizer
 _ :: c_preprocessor
-_ :: virtual

+ 2 - 0
examples/all/all_main.odin

@@ -87,6 +87,7 @@ import hlm              "core:math/linalg/hlsl"
 import noise            "core:math/noise"
 import rand             "core:math/rand"
 import ease             "core:math/ease"
+import cmplx            "core:math/cmplx"
 
 import mem              "core:mem"
 import virtual          "core:mem/virtual"
@@ -206,6 +207,7 @@ _ :: hlm
 _ :: noise
 _ :: rand
 _ :: ease
+_ :: cmplx
 _ :: mem
 _ :: virtual
 _ :: ast

+ 9 - 1
examples/all/all_vendor.odin

@@ -47,6 +47,10 @@ import CA         "vendor:darwin/QuartzCore"
 // NOTE(bill): only one can be checked at a time
 import lua_5_4    "vendor:lua/5.4"
 
+import nvg       "vendor:nanovg"
+import nvg_gl    "vendor:nanovg/gl"
+import fontstash "vendor:fontstash"
+
 _ :: botan_bindings
 _ :: botan_blake2b
 _ :: gost
@@ -92,4 +96,8 @@ _ :: MTL
 _ :: MTK
 _ :: CA
 
-_ :: lua_5_4
+_ :: lua_5_4
+
+_ :: nvg
+_ :: nvg_gl
+_ :: fontstash

+ 3 - 3
examples/demo/demo.odin

@@ -352,7 +352,7 @@ control_flow :: proc() {
 
 		if false {
 			f, err := os.open("my_file.txt")
-			if err != 0 {
+			if err != os.ERROR_NONE {
 				// handle error
 			}
 			defer os.close(f)
@@ -1175,13 +1175,13 @@ threading_example :: proc() {
 		N :: 3
 
 		pool: thread.Pool
-		thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator)
+		thread.pool_init(&pool, allocator=context.allocator, thread_count=N)
 		defer thread.pool_destroy(&pool)
 
 
 		for i in 0..<30 {
 			// be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use 
-			thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator)
+			thread.pool_add_task(&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i)
 		}
 
 		thread.pool_start(&pool)

+ 2 - 0
misc/shell.bat

@@ -7,5 +7,7 @@ rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxil
 rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
 set _NO_DEBUG_HEAP=1
 
+set ODIN_IGNORE_MSVC_CHECK=1
+
 rem set path=w:\Odin\misc;%path%
 cls

+ 3 - 1
src/array.cpp

@@ -80,7 +80,9 @@ gb_internal Slice<T> slice_make(gbAllocator const &allocator, isize count) {
 	GB_ASSERT(count >= 0);
 	Slice<T> s = {};
 	s.data = gb_alloc_array(allocator, T, count);
-	GB_ASSERT(s.data != nullptr);
+	if (count > 0) {
+		GB_ASSERT(s.data != nullptr);
+	}
 	s.count = count;
 	return s;
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 410 - 400
src/check_expr.cpp


+ 53 - 8
src/check_stmt.cpp

@@ -417,6 +417,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
 		return nullptr;
 
 	case Addressing_Variable:
+		check_old_for_or_switch_value_usage(lhs->expr);
 		break;
 
 	case Addressing_MapIndex: {
@@ -1141,8 +1142,14 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
 		syntax_error(as_token, "Expected 1 expression after 'in'");
 		return;
 	}
+	bool is_addressed = false;
+
 	Ast *lhs = as->lhs[0];
 	Ast *rhs = as->rhs[0];
+	if (lhs->kind == Ast_UnaryExpr && lhs->UnaryExpr.op.kind == Token_And) {
+		is_addressed = true;
+		lhs = lhs->UnaryExpr.expr;
+	}
 
 	check_expr(ctx, &x, rhs);
 	check_assignment(ctx, &x, nullptr, str_lit("type switch expression"));
@@ -1281,12 +1288,15 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
 			}
 		}
 
-		bool is_reference = false;
+		bool is_reference = is_addressed;
+		bool old_style = false;
 
-		if (is_ptr &&
+		if (!is_reference &&
+		    is_ptr &&
 		    cc->list.count == 1 &&
 		    case_type != nullptr) {
 			is_reference = true;
+			old_style = true;
 		}
 
 		if (cc->list.count > 1 || saw_nil) {
@@ -1305,9 +1315,12 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
 		{
 			Entity *tag_var = alloc_entity_variable(ctx->scope, lhs->Ident.token, case_type, EntityState_Resolved);
 			tag_var->flags |= EntityFlag_Used;
+			tag_var->flags |= EntityFlag_SwitchValue;
 			if (!is_reference) {
 				tag_var->flags |= EntityFlag_Value;
-				tag_var->flags |= EntityFlag_SwitchValue;
+			}
+			if (old_style) {
+				tag_var->flags |= EntityFlag_OldForOrSwitchValue;
 			}
 			add_entity(ctx, ctx->scope, lhs, tag_var);
 			add_entity_use(ctx, lhs, tag_var);
@@ -1469,12 +1482,15 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 
 	Ast *expr = unparen_expr(rs->expr);
 
+	bool is_possibly_addressable = true;
 	isize max_val_count = 2;
 	if (is_ast_range(expr)) {
 		ast_node(ie, BinaryExpr, expr);
 		Operand x = {};
 		Operand y = {};
 
+		is_possibly_addressable = false;
+
 		bool ok = check_range(ctx, expr, true, &x, &y, nullptr);
 		if (!ok) {
 			goto skip_expr_range_stmt;
@@ -1497,6 +1513,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 				gb_string_free(t);
 				goto skip_expr_range_stmt;
 			} else {
+				is_possibly_addressable = false;
+
 				if (is_reverse) {
 					error(node, "#reverse for is not supported for enum types");
 				}
@@ -1510,7 +1528,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 			Type *t = base_type(type_deref(operand.type));
 			switch (t->kind) {
 			case Type_Basic:
-				if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
+				if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+					is_possibly_addressable = false;
 					array_add(&vals, t_rune);
 					array_add(&vals, t_int);
 					if (is_reverse) {
@@ -1529,6 +1548,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 
 			case Type_Array:
 				if (is_ptr) use_by_reference_for_value = true;
+				if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable;
 				array_add(&vals, t->Array.elem);
 				array_add(&vals, t_int);
 				break;
@@ -1575,6 +1595,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 						array_add(&vals, e->type);
 					}
 
+					is_possibly_addressable = false;
+
 					if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
 						gbString s = type_to_string(t);
 						error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
@@ -1644,8 +1666,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 		}
 		Ast * name = lhs[i];
 		Type *type = rhs[i];
-
 		Entity *entity = nullptr;
+
+		bool is_addressed = false;
+		if (name->kind == Ast_UnaryExpr && name->UnaryExpr.op.kind == Token_And) {
+			is_addressed = true;
+			name = name->UnaryExpr.expr;
+		}
 		if (name->kind == Ast_Ident) {
 			Token token = name->Ident.token;
 			String str = token.string;
@@ -1659,7 +1686,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 				entity->flags |= EntityFlag_ForValue;
 				entity->flags |= EntityFlag_Value;
 				entity->identifier = name;
-				if (i == addressable_index && use_by_reference_for_value) {
+				entity->Variable.for_loop_parent_type = type_of_expr(expr);
+
+				if (is_addressed) {
+					if (is_possibly_addressable && i == addressable_index) {
+						entity->flags &= ~EntityFlag_Value;
+					} else {
+						char const *idx_name = is_map ? "key" : "index";
+						error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
+					}
+				} else if (i == addressable_index && use_by_reference_for_value) {
+					entity->flags |= EntityFlag_OldForOrSwitchValue;
 					entity->flags &= ~EntityFlag_Value;
 				}
 				if (is_soa) {
@@ -1678,7 +1715,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
 				entity = found;
 			}
 		} else {
-			error(name, "A variable declaration must be an identifier");
+			gbString s = expr_to_string(lhs[i]);
+			error(name, "A variable declaration must be an identifier, got %s", s);
+			gb_string_free(s);
 		}
 
 		if (entity == nullptr) {
@@ -2207,7 +2246,13 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
 	} else if (operands.count != result_count) {
 		// Ignore error message as it has most likely already been reported
 		if (all_operands_valid(operands)) {
-			error(node, "Expected %td return values, got %td", result_count, operands.count);
+			if (operands.count == 1) {
+				gbString t = type_to_string(operands[0].type);
+				error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t);
+				gb_string_free(t);
+			} else {
+				error(node, "Expected %td return values, got %td", result_count, operands.count);
+			}
 		}
 	} else {
 		for (isize i = 0; i < result_count; i++) {

+ 8 - 3
src/check_type.cpp

@@ -729,6 +729,12 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
 	union_type->Union.kind = ut->kind;
 	switch (ut->kind) {
 	case UnionType_no_nil:
+		if (union_type->Union.is_polymorphic && poly_operands == nullptr) {
+			GB_ASSERT(variants.count == 0);
+			if (ut->variants.count != 1) {
+				break;
+			}
+		}
 		if (variants.count < 2) {
 			error(ut->align, "A union with #no_nil must have at least 2 variants");
 		}
@@ -1410,7 +1416,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
 }
 
 
-gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> *operands) {
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> const *operands) {
 	if (_params == nullptr) {
 		return nullptr;
 	}
@@ -1658,7 +1664,6 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 				ExactValue poly_const = {};
 
 				if (operands != nullptr && variables.count < operands->count) {
-
 					Operand op = (*operands)[variables.count];
 					if (op.expr == nullptr) {
 						// NOTE(bill): 2019-03-30
@@ -1961,7 +1966,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
 
 
 // NOTE(bill): 'operands' is for generating non generic procedure type
-gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> *operands) {
+gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> const *operands) {
 	ast_node(pt, ProcType, proc_type_node);
 
 	if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) {

+ 5 - 1
src/entity.cpp

@@ -84,7 +84,9 @@ enum EntityFlag : u64 {
 	EntityFlag_CustomLinkage_LinkOnce = 1ull<<44,
 	
 	EntityFlag_Require = 1ull<<50,
-	EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
+	EntityFlag_ByPtr   = 1ull<<51, // enforce parameter is passed by pointer
+
+	EntityFlag_OldForOrSwitchValue = 1ull<<52,
 
 	EntityFlag_Overridden    = 1ull<<63,
 };
@@ -209,6 +211,8 @@ struct Entity {
 
 			ParameterValue param_value;
 
+			Type *for_loop_parent_type;
+
 			String     thread_local_model;
 			Entity *   foreign_library;
 			Ast *      foreign_library_ident;

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů