Skip to content

Instantly share code, notes, and snippets.

@thebirk
Last active December 2, 2019 16:25
Show Gist options
  • Save thebirk/48bd37959a3d1132d5708577ef1c7947 to your computer and use it in GitHub Desktop.
Save thebirk/48bd37959a3d1132d5708577ef1c7947 to your computer and use it in GitHub Desktop.
package io
import "core:os"
import "core:mem"
import "core:log"
import "core:runtime"
Error :: enum {
Ok,
Could_Not_Create_File,
Could_Not_Open_File,
Could_Not_Read_From_File,
Not_Enough_Bytes,
End_Of_Stream,
}
/*
Another possible base structure could be:
Writer :: struct {
write_proc: proc(writer: ^Writer, data: []byte) -> (int, Error),
user_data: any,
}
*/
Writer :: struct {
// Could also include some more specific actions, like `close` and `flush`
write_proc: proc(writer: ^Writer, data: []byte) -> (int, Error),
}
write :: inline proc(using writer: ^Writer, data: []byte) -> (int, Error) {
return writer.write_proc(writer, data);
}
write_string :: inline proc(using writer: ^Writer, s: string) -> (int, Error) {
return writer.write_proc(writer, cast([]u8) s);
}
write_byte :: inline proc(using writer: ^Writer, b: byte) -> (int, Error) {
return writer.write_proc(writer, []u8{b});
}
write_any :: proc(using writer: ^Writer, data: any) -> (int, Error) {
// use type information to write structs, enums, unions, etc.
// have corresponding read_any.
tb := runtime.type_info_base(type_info_of(data.id));
switch ti in tb.variant {
case runtime.Type_Info_Struct:
bytes := mem.any_to_bytes(data);
return write(writer, bytes);
case runtime.Type_Info_String:
return write_string(writer, data.(string));
case:
fmt.panicf("write_any: type '%v' not supported", tb.id);
return 0, .Ok;
}
}
Reader :: struct {
read_proc: proc(reader: ^Reader, data: []byte) -> (int, Error),
}
read :: inline proc(using reader: ^Reader, data: []byte) -> (int, Error) {
return reader.read_proc(reader, data);
}
read_byte :: inline proc(using reader: ^Reader) -> (byte, Error) {
buffer: [1]u8;
n, err := reader.read_proc(reader, buffer[:]);
if err != .Ok {
return 0, err;
}
if n == 0 {
// What do we do here? Another error code? EOS error code?
return 0, .Ok;
}
return buffer[0], .Ok;
}
read_u32le :: inline proc(using reader: ^Reader) -> (u32le, Error) {
return read_typeid(reader, u32le);
}
read_typeid :: inline proc(using reader: ^Reader, $T: typeid) -> (T, Error) {
buffer: [size_of(T)]byte;
l, err := read(reader, buffer[:]);
if l != size_of(T) {
return {}, .Not_Enough_Bytes;
}
return transmute(T) buffer, .Ok;
}
nil_write_proc :: proc(writer: ^Writer, data: []byte) -> (int, Error) {
return len(data), .Ok;
}
@private
_nil_writer := Writer{nil_write_proc};
nil_writer :: proc() -> ^Writer {
return &_nil_writer;
}
//////////////////////////////
/// File Writer and Reader ///
//////////////////////////////
File_Writer :: struct {
using writer: Writer,
handle: os.Handle,
}
file_write_proc :: proc(using w: ^File_Writer, data: []byte) -> (int, Error) {
os.write(handle, data);
return len(data), .Ok;
}
create_file :: proc(filename: string) -> (File_Writer, Error) {
fw := File_Writer{};
fw.write_proc = cast(type_of(fw.write_proc)) file_write_proc;
handle, err := os.open(filename, os.O_CREATE|os.O_TRUNC);
if err != os.ERROR_NONE {
log.error("create_file failed to open file, errno: %v", err);
return {}, .Could_Not_Create_File;
}
fw.handle = handle;
return fw, .Ok;
}
open_file_writer :: proc(filename: string) -> (File_Writer, Error) {
fw := File_Writer{};
fw.write_proc = cast(type_of(fw.write_proc)) file_write_proc;
handle, err := os.open(filename, os.O_APPEND);
if err != os.ERROR_NONE {
log.error("open_file_writer failed to open file, errno: %v", err);
return {}, .Could_Not_Open_File;
}
fw.handle = handle;
return fw, .Ok;
}
close_file_writer :: proc(fw: ^File_Writer) {
os.close(fw.handle);
}
File_Reader :: struct {
using reader: Reader,
handle: os.Handle,
}
file_read_proc :: proc(using r: ^File_Reader, data: []byte) -> (int, Error) {
l, err := os.read(r.handle, data);
if err != os.ERROR_NONE {
return {}, .Could_Not_Read_From_File;
}
if l == 0 {
return 0, .End_Of_Stream;
}
return l, .Ok;
}
open_file_reader :: proc(filename: string) -> (File_Reader, Error) {
fr := File_Reader{};
fr.read_proc = cast(type_of(fr.read_proc)) file_read_proc;
handle, err := os.open(filename, os.O_RDONLY);
if err != os.ERROR_NONE {
log.error("open_file_reader failed to open file, errno: %v", err);
return {}, .Could_Not_Open_File;
}
fr.handle = handle;
return fr, .Ok;
}
close_file_reader :: proc(fr: ^File_Reader) {
os.close(fr.handle);
}
////////////////////
/// Multi Writer ///
////////////////////
// Implement a seperate error code for Multi_Writer that indicates that some error occured
// and then store that error next to the writer in a structur perhaps a little like this:
//
// writers: [dynamic]struct{^Writer, Error}
//
Multi_Writer :: struct {
using writer: Writer,
writers: [dynamic]^Writer,
}
multi_write_proc :: proc(using w: ^Multi_Writer, data: []byte) -> (int, Error) {
for w in writers {
l, err := w.write_proc(w, data);
if err != .Ok {
return l, err;
}
}
return len(data), .Ok;
}
make_multi_writer :: proc(writers: []^Writer) -> Multi_Writer {
mw := Multi_Writer{};
mw.write_proc = cast(type_of(mw.write_proc)) multi_write_proc;
for w in writers {
append(&mw.writers, w);
}
return mw;
}
delete_multi_writer :: proc(mw: ^Multi_Writer) {
delete(mw.writers);
}
////////////////////////////////
/// Socket Reader and Writer ///
////////////////////////////////
when false {
Socket_Writer :: struct {
using writer: Writer,
socket_handle: sock.Socket,
}
socket_write_proc :: proc(using w: ^Socket_Writer, data: []byte) -> (int, Error) {
n := sock.write(socket_handle, data, len(data));
if n < 0 {
// Handle errno
return 0, .Socked_Write_Error;
}
return n, .Ok;
}
make_socket_writer :: proc(sock: sock.Socket) -> Socket_Writer {
sw := Socket_Writer{};
sw.write_proc = cast(type_of(sw.write_proc)) socket_write_proc;
sw.socket_handle = sock;
return sw;
}
Socket_Reader :: struct {
using writer: Reader,
socket_handle: sock.Socket,
}
socket_read_proc :: proc(using w: ^Socket_Reader, data: []byte) -> (int, Error) {
n := sock.read(socket_handle, data, len(data));
if n < 0 {
// Handle errno
return 0, .Socked_Read_Error;
}
return n, .Ok;
}
make_socket_reader :: proc(sock: sock.Socket) -> Socket_Reader {
sw := Socket_Reader{};
sw.read_proc = cast(type_of(sw.read_proc)) socket_read_proc;
sw.socket_handle = sock;
return sw;
}
}
///////
import "core:fmt"
Lower_Case_Writer :: struct {
using writer: Writer,
input_writer: ^Writer,
}
lower_case_write_proc :: proc(using w: ^Lower_Case_Writer, data: []byte) -> (int, Error) {
wrote := 0;
for b in data {
b := b;
if b >= 0x41 && b <= 0x5A {
b += 0x20;
}
n, err := write_byte(w.input_writer, b);
wrote += n;
if err != .Ok {
return wrote, err;
}
}
return wrote, .Ok;
};
make_lower_case_writer :: proc(input_writer: ^Writer) -> Lower_Case_Writer {
lcw := Lower_Case_Writer{};
lcw.write_proc = cast(type_of(lcw.write_proc)) lower_case_write_proc;
lcw.input_writer = input_writer;
return lcw;
}
main :: proc() {
context.logger = log.create_console_logger();
fw, _ := create_file("test.txt");
defer close_file_writer(&fw);
// Example of a wrapping writer
lcw := make_lower_case_writer(&fw);
log_writer := Writer {
proc(writer: ^Writer, data: []byte) -> (int, Error) {
fmt.printf("wrote %d bytes\n", len(data));
return len(data), .Ok;
}
};
w := make_multi_writer([]^Writer{&lcw, &log_writer, nil_writer()});
defer delete_multi_writer(&w);
write(&w, cast([]byte) "This is some data");
write_byte(&w, '\n');
write_string(&w, "This is some more data\n");
write_any(&w, "This is a string\n");
write_any(&w, struct #packed {a: u8, b: u16, c: u32}{0x10, 0x2000, 0x40000000});
{ // Reading a file 8 bytes at a time, using File_Reader
fr, _ := open_file_reader("test.txt");
defer close_file_reader(&fr);
data: [8]byte;
for {
n, err := read(&fr, data[:]);
if err != .Ok {
if err == .End_Of_Stream {
break;
} else {
fmt.panicf("read err: %v\n", err);
}
}
for i in 0..<len(data) {
if i < n {
c := data[i];
switch c {
case '\n':
c = '$';
case ' ':
c = '.';
}
fmt.printf("%c", c);
} else {
fmt.printf("0");
}
}
fmt.println();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment