implemented #27
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 11 Jan 2021 21:50:37 +0000 (15:50 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 11 Jan 2021 21:50:37 +0000 (15:50 -0600)
core/io/stream.onyx
core/io/writer.onyx [new file with mode: 0644]
core/std/js.onyx
core/std/wasi.onyx
core/stdio.onyx
core/string/builder.onyx
core/sys/js.onyx
core/sys/wasi.onyx
tests/array_struct_robustness.onyx

index bd76c745faba97b03071c96b51e5e6aa82ebf954..d76d09e54bf3392a6e50660f23a90620636a8af4 100644 (file)
@@ -124,6 +124,9 @@ stream_peek_byte :: proc (use s: ^Stream, out: ^u8) -> Error {
 
 
 
+//
+// StringStream
+//
 StringStream :: struct {
     use stream : Stream;
 
@@ -185,11 +188,10 @@ string_stream_vtable := Stream_Vtable.{
         ss : ^StringStream = ~~s;
         use ss;
 
-        if curr_pos >= data.count do return Error.EOF;
+        if at >= data.count do return Error.EOF;
 
         bytes_to_read := math.min(buffer.count, data.count - at);
         memory.copy(buffer.data, ^data.data[at], bytes_to_read);
-        curr_pos += bytes_to_read;
 
         if number_read != null do *number_read = bytes_to_read;
         return Error.None;
@@ -235,11 +237,10 @@ string_stream_vtable := Stream_Vtable.{
         ss : ^StringStream = ~~s;
         use ss;
 
-        if curr_pos >= data.count do return Error.EOF;
+        if at >= data.count do return Error.EOF;
 
         bytes_to_write := math.min(buffer.count, data.count - at);
         memory.copy(^data.data[at], buffer.data, bytes_to_write);
-        curr_pos += bytes_to_write;
 
         if number_written != null do *number_written = bytes_to_write;
         return Error.None;
@@ -266,4 +267,189 @@ string_stream_vtable := Stream_Vtable.{
 
     close = null_proc,
     flush = null_proc,
-}
\ No newline at end of file
+}
+
+
+//
+// DynamicStringStream
+//
+DynamicStringStream :: struct {
+    use stream : Stream;
+
+    curr_pos : i32;
+    data  : [..] u8;
+    alloc : Allocator;
+}
+
+dynamic_string_stream_make :: proc (init_size := 128, a := context.allocator) -> DynamicStringStream {
+    data : [..] u8;
+    array.init(^data, init_size);
+
+    return DynamicStringStream.{
+        stream = Stream.{
+            vtable = ^dynamic_string_stream_vtable,
+        },
+
+        curr_pos = 0,
+        data = data,
+        alloc = a,
+    };
+}
+
+dynamic_string_stream_to_str :: proc (use dds: ^DynamicStringStream) -> str {
+    return data.data[0 .. curr_pos];
+}
+
+#private
+dynamic_string_stream_vtable := Stream_Vtable.{
+    seek = proc (s: ^Stream, to: i32, whence: SeekFrom) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        dest : i32;
+        switch whence {
+            case SeekFrom.Start   do dest = to;
+            case SeekFrom.Current do dest = curr_pos + to;
+            case SeekFrom.End     do dest = data.count - to; // CHECK: Off by one?
+        }
+
+        if dest >= data.count {
+            #context_scope {
+                context.allocator = alloc;
+                if !array.ensure_capacity(^data, dest) do return Error.OutOfBounds;
+            }
+        }
+
+        curr_pos = dest;
+        return Error.None;
+    },
+
+    tell = proc (s: ^Stream, out: ^i32) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if out != null do *out = curr_pos;
+        return Error.None;
+    },
+
+    read = proc (s: ^Stream, buffer: [] u8, number_read: ^u32) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if curr_pos >= data.count do return Error.EOF;
+
+        bytes_to_read := math.min(buffer.count, data.count - curr_pos);
+        memory.copy(buffer.data, ^data.data[curr_pos], bytes_to_read);
+        curr_pos += bytes_to_read;
+
+        if number_read != null do *number_read = bytes_to_read;
+        return Error.None;
+    },
+
+    read_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_read: ^u32) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if at >= data.count do return Error.EOF;
+
+        bytes_to_read := math.min(buffer.count, data.count - at);
+        memory.copy(buffer.data, ^data.data[at], bytes_to_read);
+
+        if number_read != null do *number_read = bytes_to_read;
+        return Error.None;
+    },
+
+    read_byte = proc (s: ^Stream, out: ^u8) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if curr_pos >= data.count do return Error.EOF;
+
+        if out != null do *out = data[curr_pos];
+
+        curr_pos += 1;
+        return Error.None;
+    },
+
+    unread_byte = proc (s: ^Stream) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if curr_pos <= 0 do return Error.OutOfBounds;
+
+        curr_pos -= 1;
+        return Error.None;
+    },
+
+    write = proc (s: ^Stream, buffer: [] u8, number_written: ^u32) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if curr_pos + buffer.count >= data.capacity {
+            #context_scope {
+                context.allocator = alloc;
+                if !array.ensure_capacity(^data, curr_pos + buffer.count) do return Error.EOF;
+            }
+        }
+
+        memory.copy(^data.data[curr_pos], buffer.data, buffer.count);
+        curr_pos   += buffer.count;
+        data.count += buffer.count;
+
+        if number_written != null do *number_written = buffer.count;
+        return Error.None;
+    },
+
+    write_at = proc (s: ^Stream, at: u32, buffer: [] u8, number_written: ^u32) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        if at + buffer.count >= data.capacity {
+            #context_scope {
+                context.allocator = alloc;
+                if !array.ensure_capacity(^data, at + buffer.count) do return Error.EOF;
+            }
+        }
+
+        memory.copy(^data.data[at], buffer.data, buffer.count);
+        data.count = math.max(data.count, at + buffer.count);
+
+        if number_written != null do *number_written = buffer.count;
+        return Error.None;
+    },
+
+    write_byte = proc (s: ^Stream, byte: u8) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        #context_scope {
+            context.allocator = alloc;
+            if !array.ensure_capacity(^data, data.count + 1) do return Error.EOF;
+        }
+
+        data[curr_pos] = byte;
+        curr_pos   += 1;
+        data.count += 1;
+
+        return Error.None;
+    },
+
+    size = proc (s: ^Stream) -> i32 {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        return data.count;
+    },
+
+    flush = proc (s: ^Stream) -> Error {
+        dss : ^DynamicStringStream = ~~s;
+        use dss;
+
+        curr_pos = 0;
+        array.clear(^data);
+
+        return Error.None;
+    },
+
+    close = null_proc,
+}
diff --git a/core/io/writer.onyx b/core/io/writer.onyx
new file mode 100644 (file)
index 0000000..9012551
--- /dev/null
@@ -0,0 +1,89 @@
+package core.io
+
+use package core.conv as conv
+
+Writer :: struct {
+    stream : ^Stream;
+}
+
+writer_make :: proc (s: ^Stream) -> Writer {
+    assert(s.vtable != null, "Stream vtable was not setup correctly.");
+
+    return Writer.{ s };
+}
+
+write_str :: proc (use writer: ^Writer, s: str) {
+    stream_write(stream, s);
+}
+
+write_cstr :: proc (use writer: ^Writer, cs: cstr) {
+    use package core
+
+    s := string.make(cs);
+    write_str(writer, s);
+}
+
+write_i32 :: proc (use writer: ^Writer, n: i32, base: u32 = 10) {
+    use package core
+
+    buf : [256] u8;
+    s := conv.i64_to_str(cast(i64) n, cast(u64) base, buf[0 .. 256]);
+    write_str(writer, s);
+}
+
+write_i64 :: proc (use writer: ^Writer, n: i64, base: u64 = 10) {
+    use package core
+
+    buf : [256] u8;
+    s := conv.i64_to_str(n, base, buf[0 .. 256]);
+    write_str(writer, s);
+}
+
+write_f32 :: proc (use writer: ^Writer, f: f32) {
+    use package core
+
+    buf : [256] u8;
+    s := conv.f64_to_str(cast(f64) f, buf[0 .. 256]);
+    write_str(writer, s);
+}
+
+write_f64 :: proc (use writer: ^Writer, f: f64) {
+    use package core
+
+    buf : [256] u8;
+    s := conv.f64_to_str(f, buf[0 .. 256]);
+    write_str(writer, s);
+}
+
+write_bool :: proc (use writer: ^Writer, b: bool) {
+    if b do write_str(writer, "true");
+    else do write_str(writer, "false");
+}
+
+write_ptr :: proc (use writer: ^Writer, p: ^void) {
+    write_i64(writer, cast(i64) p, 16);
+}
+
+write_range :: proc (use writer: ^Writer, r: range, sep := " ") {
+    for i: r {
+        write_i32(writer, i);
+        if i + r.step < r.high do write_str(writer, sep);
+    }
+}
+
+write_format :: proc (use writer: ^Writer, format: str, va: ...) {
+    // POTENTIAL BUG: this buffer will need to be bigger (or dynamic).
+    buffer: [2048] u8;
+    write_str(writer, conv.str_format_va(format, buffer[0 .. 2048], va));
+}
+
+write :: proc {
+    write_str, write_cstr, 
+    write_i32, write_f32,
+    write_i64, write_f64,
+    write_bool,
+    write_ptr,
+    write_range,
+
+    write_format,
+}
\ No newline at end of file
index 943027ed18ea537df1744ef5a51337c509f9cafe..71ef69e508a2384d264d22802293120a7622847c 100644 (file)
@@ -18,6 +18,7 @@ package core
 #load "core/io/io"
 #load "core/io/stream"
 #load "core/io/reader"
+#load "core/io/writer"
 
 #load "core/sys/js"
 
index 6ff0eed669a72e6f1f9d4949e42d2f751516ebae..7e5167c13919b94c4e8308984965d24c91fae52e 100644 (file)
@@ -20,6 +20,7 @@ package core
 #load "core/io/io"
 #load "core/io/stream"
 #load "core/io/reader"
+#load "core/io/writer"
 
 #load "core/sys/wasi"
 
index 8ebd94ca30000c5a13c8889d6fbd1e7b41039563..c3e67fda749518e5b9f8516770ea8731821eb8a9 100644 (file)
@@ -11,39 +11,22 @@ package core
 // of the system package
 use package system as system
 
-#private_file print_buffer : string.builder.Builder;
+#private_file print_stream : io.DynamicStringStream;
+#private_file print_writer : io.Writer;
 
 stdio_init :: proc () {
-    print_buffer = string.builder.make(2048);
-}
-
-print_str  :: proc (s: str) {
-    string.builder.append(^print_buffer, s);
-    if s.data[s.count - 1] == #char "\n" do print_buffer_flush();
-}
-
-print_cstr :: proc (s: cstr)                do string.builder.append(^print_buffer, s);
-print_i64  :: proc (n: i64, base: u64 = 10) do string.builder.append(^print_buffer, n, base);
-print_i32  :: proc (n: i32, base: u32 = 10) do string.builder.append(^print_buffer, cast(i64) n, cast(u64) base);
-print_f64  :: proc (n: f64)                 do string.builder.append(^print_buffer, n);
-print_f32  :: proc (n: f32)                 do string.builder.append(^print_buffer, cast(f64) n);
-print_bool :: proc (b: bool)                do string.builder.append(^print_buffer, b);
-print_ptr  :: proc (p: ^void)               do string.builder.append(^print_buffer, cast(i64) p, cast(u64) 16);
-
-print_range :: proc (r: range, sep := " ") {
-    for i: r {
-        print(i);
-        if i + r.step < r.high do print(sep);
-    }
-    print("\n");
+    print_stream = io.dynamic_string_stream_make(2048, context.allocator);
+    print_writer = io.writer_make(^print_stream);
 }
 
 print :: proc {
-    print_str,    print_cstr,
-    print_i64,    print_i32,
-    print_f64,    print_f32,
-    print_bool,   print_ptr,
-    print_range,
+    proc (x: str) {
+        io.write(^print_writer, x);
+        if x[x.count - 1] == #char "\n" do print_stream_flush();
+    },
+
+    proc (x: $T)        { io.write(^print_writer, x); },
+    proc (x: $T, y: $R) { io.write(^print_writer, x, y); },
 }
 
 println :: proc (x: $T) {
@@ -66,13 +49,13 @@ print_array :: proc (arr: $T, sep := " ") {
     print("\n");
 }
 
-print_buffer_flush :: proc () {
-    if print_buffer.data.count == 0 do return;
+print_stream_flush :: proc () {
+    if print_stream.data.count == 0 do return;
 
-    ^print_buffer
-        |> string.builder.to_str()
+    ^print_stream
+        |> io.dynamic_string_stream_to_str()
         |> system.output_str();
 
-    ^print_buffer |> string.builder.clear();
+    ^print_stream |> io.stream_flush();
 }
 
index 2280686fa7933372ab83ffcee10152b103524fbe..495e541e6d5a988391fb99cf3a1945d1be27c575 100644 (file)
@@ -1,5 +1,8 @@
 package core.string.builder
 
+// DEPRECATED: This package is deprecated in favor of using
+// an io.DynamicStringStream with an io.Writer.
+
 use package core.array as array
 use package core.string as string
 use package core.conv as conv
index 25b2fb9b724928e8bfff5eb95779b0540653da39..35957198b8f3fe91ca9065475ec059d45612d571 100644 (file)
@@ -35,5 +35,5 @@ proc () #export "_start" {
 
     main.main(args);
 
-    print_buffer_flush();
+    print_stream_flush();
 }
index a3aaf9059d0376b9fa62e5099289a83b9340079b..533ad186abb3776f2468961a83d257f3fa51e4b5 100644 (file)
@@ -51,5 +51,5 @@ proc () #export "_start" {
 
     main.main(argv[0 .. argc]);
 
-    print_buffer_flush();
+    print_stream_flush();
 }
index 8517edf1c48b5de6edc4d57d84ffedd24bf7697a..17dfeb464656f7d4789ed7dfff6ed1175e04144c 100644 (file)
@@ -5,8 +5,8 @@ use package core
 Vec2 :: struct { x: i32; y: i32; }
 
 // Overload print() to print Vec2's.
-proc (use v: Vec2) #add_overload print {
-    printf("Vec2(%i, %i)", x, y);
+proc (use writer: ^io.Writer, use v: Vec2) #add_overload io.write {
+    io.write_format(writer, "Vec2(%i, %i)", x, y);
 }
 
 EntityStore :: struct {