Browse Source

Merge pull request #2584 from odin-lang/new-io

New and Improved `io.Stream` interface
gingerBill 2 years ago
parent
commit
19ea090633
55 changed files with 720 additions and 1036 deletions
  1. 22 44
      core/bufio/read_writer.odin
  2. 13 45
      core/bufio/reader.odin
  3. 20 39
      core/bufio/writer.odin
  4. 22 60
      core/bytes/buffer.odin
  5. 20 39
      core/bytes/reader.odin
  6. 3 2
      core/compress/common.odin
  7. 4 4
      core/crypto/blake/blake.odin
  8. 1 1
      core/crypto/blake2b/blake2b.odin
  9. 1 1
      core/crypto/blake2s/blake2s.odin
  10. 1 1
      core/crypto/gost/gost.odin
  11. 4 4
      core/crypto/groestl/groestl.odin
  12. 15 15
      core/crypto/haval/haval.odin
  13. 4 4
      core/crypto/jh/jh.odin
  14. 4 4
      core/crypto/keccak/keccak.odin
  15. 1 1
      core/crypto/md2/md2.odin
  16. 1 1
      core/crypto/md4/md4.odin
  17. 1 1
      core/crypto/md5/md5.odin
  18. 4 4
      core/crypto/ripemd/ripemd.odin
  19. 1 1
      core/crypto/sha1/sha1.odin
  20. 4 4
      core/crypto/sha2/sha2.odin
  21. 4 4
      core/crypto/sha3/sha3.odin
  22. 2 2
      core/crypto/shake/shake.odin
  23. 1 1
      core/crypto/sm3/sm3.odin
  24. 2 2
      core/crypto/streebog/streebog.odin
  25. 3 3
      core/crypto/tiger/tiger.odin
  26. 3 3
      core/crypto/tiger2/tiger2.odin
  27. 1 1
      core/crypto/whirlpool/whirlpool.odin
  28. 5 5
      core/fmt/fmt_os.odin
  29. 30 74
      core/io/conv.odin
  30. 144 296
      core/io/io.odin
  31. 47 39
      core/io/multi.odin
  32. 43 36
      core/io/util.odin
  33. 6 2
      core/os/os2/errors_windows.odin
  34. 38 30
      core/os/os2/file.odin
  35. 63 21
      core/os/os2/file_linux.odin
  36. 6 76
      core/os/os2/file_stream.odin
  37. 68 23
      core/os/os2/file_windows.odin
  38. 46 52
      core/os/stream.odin
  39. 16 25
      core/strings/builder.odin
  40. 19 39
      core/strings/reader.odin
  41. 1 1
      vendor/botan/blake2b/blake2b.odin
  42. 1 1
      vendor/botan/gost/gost.odin
  43. 1 1
      vendor/botan/keccak/keccak.odin
  44. 1 1
      vendor/botan/md4/md4.odin
  45. 1 1
      vendor/botan/md5/md5.odin
  46. 1 1
      vendor/botan/ripemd/ripemd.odin
  47. 1 1
      vendor/botan/sha1/sha1.odin
  48. 4 4
      vendor/botan/sha2/sha2.odin
  49. 4 4
      vendor/botan/sha3/sha3.odin
  50. 2 2
      vendor/botan/shake/shake.odin
  51. 3 3
      vendor/botan/skein512/skein512.odin
  52. 1 1
      vendor/botan/sm3/sm3.odin
  53. 2 2
      vendor/botan/streebog/streebog.odin
  54. 3 3
      vendor/botan/tiger/tiger.odin
  55. 1 1
      vendor/botan/whirlpool/whirlpool.odin

+ 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
 }
+

+ 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
 	}

+ 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])
 	    } 

+ 5 - 5
core/fmt/fmt_os.odin

@@ -12,7 +12,7 @@ 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, ..args, sep=sep)
 }
@@ -23,7 +23,7 @@ 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, ..args, sep=sep)
@@ -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,7 +54,7 @@ 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)

+ 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
 
+}

+ 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
+}
+

+ 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
 }

+ 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
 }

+ 1 - 1
vendor/botan/blake2b/blake2b.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/gost/gost.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/keccak/keccak.odin

@@ -69,7 +69,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/md4/md4.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/md5/md5.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/ripemd/ripemd.odin

@@ -69,7 +69,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/sha1/sha1.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 4 - 4
vendor/botan/sha2/sha2.odin

@@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 4 - 4
vendor/botan/sha3/sha3.odin

@@ -72,7 +72,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -151,7 +151,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -230,7 +230,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -309,7 +309,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 2 - 2
vendor/botan/shake/shake.odin

@@ -70,7 +70,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 3 - 3
vendor/botan/skein512/skein512.odin

@@ -72,7 +72,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -151,7 +151,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -230,7 +230,7 @@ hash_stream_slice :: proc(s: io.Stream, bit_size: int, allocator := context.allo
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 1 - 1
vendor/botan/sm3/sm3.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 2 - 2
vendor/botan/streebog/streebog.odin

@@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 
@@ -149,7 +149,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         } 

+ 3 - 3
vendor/botan/tiger/tiger.odin

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

+ 1 - 1
vendor/botan/whirlpool/whirlpool.odin

@@ -69,7 +69,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
     defer delete(buf)
     i := 1
     for i > 0 {
-        i, _ = s->impl_read(buf)
+        i, _ = io.read(s, buf)
         if i > 0 {
             botan.hash_update(ctx, len(buf) == 0 ? nil : &buf[0], uint(i))
         }