drastically improved the io.Reader code
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 1 Dec 2021 16:47:18 +0000 (10:47 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 1 Dec 2021 16:47:18 +0000 (10:47 -0600)
core/io/file.onyx
core/io/io.onyx
core/io/reader.onyx
core/io/stream.onyx
core/memory.onyx
modules/wasm_utils/parser.onyx
tests/aoc-2020/day24.onyx
tests/aoc-2021/day01 [new file with mode: 0644]
tests/aoc-2021/day01.onyx [new file with mode: 0644]
tests/aoc-2021/input/day01.txt [new file with mode: 0644]

index 08b7f052d37eb00caf23db63d2ee3924975a650d..953289e90b8edd07420d414225bed468f26e8732 100644 (file)
@@ -236,10 +236,6 @@ file_stream_vtable := Stream_Vtable.{
         return .None, byte;
     },
 
-    unread_byte = (use fs: ^FileStream) -> Error {
-        return .NotImplemented;
-    },
-
     write = (use fs: ^FileStream, buffer: [] u8) -> (Error, u32) {
         bytes_written : wasi.Size;
         vec   := IOVec.{ buf = cast(u32) buffer.data, len = buffer.count };
index a43d3cac2da5448cf5dce3e78460e97fbcbed808..71739d8983d4d3a1ef4e63cd1a2d53150691e970 100644 (file)
@@ -20,4 +20,13 @@ Error :: enum {
 
     // The file or stream was not initialized correctly.
     BadFile        :: 0x06;
+
+    // Some underlying buffer used in stream processing is full.
+    BufferFull     :: 0x07;
+
+    // While trying to read from a stream, multiple reads failed in a row.
+    NoProgress     :: 0x08;
+
+    // Not possible to unread.
+    InvalidUnread  :: 0x09;
 }
\ No newline at end of file
index bbb13f43254d7017b8c0361c2a81e8d7d7bd1b57..3f14df9dc608f0bd77d23730660dbbf8525d4c15 100644 (file)
@@ -1,15 +1,45 @@
 package core.io
 
 memory :: package core.memory
+math   :: package core.math
 
 Reader :: struct {
     stream : ^Stream;
+
+    buffer: [] u8;
+    buffer_allocator: Allocator;
+
+    start, end: u32; // The start and ending positions of the edges of the buffer "window".    
+
+    last_byte: i32;
+
+    error: Error;
+
+    done : bool; // If an .EOF was reached.
 }
 
-reader_make :: (s: ^Stream) -> Reader {
+reader_make :: (s: ^Stream, buffer_size := 4096, allocator := context.allocator) -> Reader {
     assert(s.vtable != null, "Stream vtable was not setup correctly.");
-    
-    return Reader.{ s };
+
+    reader: Reader;
+    reader.stream = s;
+
+    memory.alloc_slice(^reader.buffer, buffer_size, allocator);
+    reader.buffer_allocator = allocator;
+
+    reader_reset(^reader);
+
+    return reader;
+}
+
+reader_reset :: (use reader: ^Reader) {
+    start, end = 0, 0;
+    last_byte = #char "\0";
+    done = false;
+}
+
+reader_free :: (use reader: ^Reader) {
+    memory.free_slice(^buffer, buffer_allocator);
 }
 
 // You need to free the StringStream from the context.allocator when you use this. For example,
@@ -19,52 +49,107 @@ reader_from_string :: (s: str) -> (Reader, ^StringStream) {
     stream_ptr := new(StringStream);
     *stream_ptr = string_stream_make(s);
 
-    return Reader.{ stream_ptr }, stream_ptr;
+    return reader_make(stream_ptr), stream_ptr;
+}
+
+reader_get_buffered :: (use reader: ^Reader) -> i32 {
+    return end - start;
 }
 
 reader_empty :: (use reader: ^Reader) -> bool {
-    return stream_end_of_file(stream);
+    return done && reader_get_buffered(reader) == 0;
 }
 
 read_byte :: (use reader: ^Reader) -> u8 {
-    err, byte := stream_read_byte(stream);
+    while start == end {
+        if error != .None {
+            reader_consume_error(reader);
+            return 0;
+        }
+
+        if err := reader_read_next_chunk(reader); err != .None {
+            return 0;
+        }
+    }
+
+    byte := buffer[start];
+    start += 1;
+    last_byte = cast(i32) byte;
     return byte;
 }
 
-read_bytes :: #match {
-    (use reader: ^Reader, bytes := 1, allocator := context.allocator) -> str {
-        buffer := memory.make_slice(u8, bytes, allocator);
-        stream_read(stream, buffer);
-        return buffer;
-    },
+unread_byte :: (use reader: ^Reader) -> Error {
+    if last_byte < 0 || (start == 0 && end > 0) {
+        return .InvalidUnread;
+    }
 
-    (use reader: ^Reader, bytes: [] u8) -> str {
-        err, bytes_read := stream_read(stream, bytes);
-        return bytes;
+    if start > 0 {
+        start -= 1;
+    } else {
+        end = 1;
     }
+
+    buffer[start] = cast(u8) last_byte;
+    last_byte = -1;
+    return .None;
 }
 
+read_bytes :: (use reader: ^Reader, bytes: [] u8) -> (i32, Error) {
+    n := bytes.count;
+    if n == 0 {
+        if reader_get_buffered(reader) > 0 do return 0, .None;
+        return 0, reader_consume_error(reader);
+    }
+
+    if start == end {
+        if error != .None do return 0, reader_consume_error(reader);
+
+        if n >= buffer.count {
+            error, n = stream_read(stream, bytes);
+
+            if n > 0 do last_byte = cast(i32) bytes[n - 1];
+
+            return n, reader_consume_error(reader);
+        }
+
+        start, end = 0, 0;
+        error, n = stream_read(stream, buffer);
+        if n == 0 do return 0, reader_consume_error(reader);
+        end += n;
+    }
+
+    memory.copy(bytes.data, buffer.data + start, n);
+    start += n;
+    last_byte = cast(i32) buffer[start - 1];
+    return n, .None;
+}
+
+read_string :: (use reader: ^Reader, bytes := 1, allocator := context.allocator) -> str {
+    buf := memory.make_slice(u8, bytes, allocator);
+    bytes_read, err := read_bytes(reader, buf);
+    return buf[0 .. bytes_read];
+};
+
 read_i32 :: (use reader: ^Reader) -> i32 {
     n: i32 = 0;
 
     skip_whitespace(reader);
 
     is_negative := false;
-    err, curr := stream_peek_byte(stream);
+    curr, err := peek_byte(reader);
     if curr == #char "-" {
         is_negative = true;
-        err, curr = stream_read_byte(stream);
-        err, curr = stream_peek_byte(stream);
+        start += 1;
     }
 
+    curr, err = peek_byte(reader);
     while curr >= #char "0" && curr <= #char "9" {
-        err, curr = stream_read_byte(stream);
-
         n *= 10;
         n += cast(u32) (curr - #char "0"); 
 
-        err, curr = stream_peek_byte(stream);
-        if err == Error.EOF do break;
+        start += 1;
+        curr, err = peek_byte(reader);
+        if err != .None do break;
     }
 
     if is_negative do n = -n;
@@ -77,21 +162,20 @@ read_i64 :: (use reader: ^Reader) -> i64 {
     skip_whitespace(reader);
 
     is_negative := false;
-    err, curr := stream_peek_byte(stream);
+    curr, err := peek_byte(reader);
     if curr == #char "-" {
         is_negative = true;
-        err, curr = stream_read_byte(stream);
-        err, curr = stream_peek_byte(stream);
+        start += 1;
     }
 
+    curr, err = peek_byte(reader);
     while curr >= #char "0" && curr <= #char "9" {
-        err, curr = stream_read_byte(stream);
-
         n *= 10;
         n += cast(u64) (curr - #char "0"); 
 
-        err, curr = stream_peek_byte(stream);
-        if err == Error.EOF do break;
+        start += 1;
+        curr, err = peek_byte(reader);
+        if err != .None do break;
     }
 
     if is_negative do n = -n;
@@ -103,15 +187,14 @@ read_u32 :: (use reader: ^Reader) -> u32 {
 
     skip_whitespace(reader);
 
-    err, curr := stream_peek_byte(stream);
+    curr, err := peek_byte(reader);
     while curr >= #char "0" && curr <= #char "9" {
-        err, curr = stream_read_byte(stream);
-
         n *= 10;
         n += cast(u32) (curr - #char "0"); 
 
-        err, curr = stream_peek_byte(stream);
-        if err == Error.EOF do break;
+        start += 1;
+        curr, err = peek_byte(reader);
+        if err != .None do break;
     }
 
     return n;
@@ -122,54 +205,48 @@ read_u64 :: (use reader: ^Reader) -> u64 {
 
     skip_whitespace(reader);
 
-    err, curr := stream_peek_byte(stream);
+    curr, err := peek_byte(reader);
     while curr >= #char "0" && curr <= #char "9" {
-        err, curr = stream_read_byte(stream);
-
         n *= 10;
         n += cast(u64) (curr - #char "0"); 
 
-        err, curr = stream_peek_byte(stream);
-        if err == Error.EOF do break;
+        start += 1;
+        curr, err = peek_byte(reader);
+        if err != .None do break;
     }
 
     return n;
 }
 
 read_line :: (use reader: ^Reader, consume_newline := true, allocator := context.allocator) -> str {
-    _, curr_pos := stream_tell(stream);
+    reader_read_next_chunk(reader);
 
-    count := 0;
-    err, curr := stream_read_byte(stream);
-    while curr != #char "\n" {
+    count := start;
+    while count < end && buffer[count] != #char "\n" {
         count += 1;
-
-        err, curr = stream_read_byte(stream);
-        if err != Error.None do break;
     }
 
-    if consume_newline do count += 1;
-
-    stream_seek(stream, curr_pos, SeekFrom.Start);
+    if consume_newline && count < end {
+        count += 1;
+    }
 
     out := str.{
         data  = raw_alloc(allocator, count * sizeof(u8)),
-        count = count,
+        count = count - start,
     };
 
-    stream_read(stream, out);
+    memory.copy(out.data, buffer.data + start, count - start);
+    start = count;
     return out;
 }
 
 read_word :: (use reader: ^Reader, numeric_allowed := false, allocator := context.allocator) -> str {
     skip_whitespace(reader);
+    reader_read_next_chunk(reader);
 
-    _, curr_pos := stream_tell(stream);
-
-    count := 0;
-    err, curr := stream_read_byte(stream);
-
-    while true {
+    count := start;
+    while count < end {
+        curr := buffer[count];
         if     (curr >= #char "a" && curr <= #char "z")
             || (curr >= #char "A" && curr <= #char "Z")
             || curr == #char "_" {
@@ -179,78 +256,80 @@ read_word :: (use reader: ^Reader, numeric_allowed := false, allocator := contex
         } else {
             break;
         }
-
-        err, curr = stream_read_byte(stream);
-        if err != Error.None do break;
     }
 
-    stream_seek(stream, curr_pos, SeekFrom.Start);
-
     out := str.{
         data  = raw_alloc(allocator, count * sizeof(u8)),
-        count = count,
+        count = count - start,
     };
 
-    stream_read(stream, out);
+    memory.copy(out.data, buffer.data + start, count - start);
+    start = count;
     return out;
 }
 
 read_until :: (use reader: ^Reader, until: u8, skip: u32 = 0, allocator := context.allocator, consume_end := false) -> str {
-    _, curr_pos := stream_tell(stream);
-
-    count := 0;
-    err, curr := stream_read_byte(stream);
+    reader_read_next_chunk(reader);
 
-    while true {
+    count := start;
+    while count < end {
+        curr := buffer[count];
         if curr != until {
             count += 1;
         } else {
             if skip == 0 do break;
             else do skip -= 1;
         }
-
-        err, curr = stream_read_byte(stream);
-        if err != Error.None do break;
     }
 
-    if consume_end do count += 1;
-
-    stream_seek(stream, curr_pos, SeekFrom.Start);
+    if consume_end && count < end do count += 1;
 
     out := str.{
         data  = raw_alloc(allocator, count * sizeof(u8)),
-        count = count,
+        count = count - start,
     };
 
-    stream_read(stream, out);
+    memory.copy(out.data, buffer.data + start, count - start);
+    start = count;
     return out;
 }
 
-peek_byte :: (use reader: ^Reader) -> u8 {
-    err, byte := stream_peek_byte(stream);
-    return byte;
+peek_byte :: (use reader: ^Reader) -> (u8, Error) {
+    if start == end {
+        if fill_err := reader_read_next_chunk(reader); fill_err != .None {
+            return 0, fill_err;
+        }
+    }
+
+    return buffer[start], .None;
 }
 
 advance_line :: (use reader: ^Reader) {
-    err, curr := stream_read_byte(stream);
-    if err != Error.None do return;
-    
-    while curr != #char "\n" {
-        err, curr = stream_read_byte(stream);
-        if err != Error.None do return;
-    }
+    while true {
+        if start == end {
+            if fill_err := reader_read_next_chunk(reader); fill_err != .None {
+                return;
+            }
+        }
 
-    err, curr = stream_read_byte(stream);
+        defer start += 1;
+        if buffer[start] == #char "\n" {
+            return;
+        }
+    }
 }
 
 skip_whitespace :: (use reader: ^Reader) {
-    while true {
-        err, byte := stream_peek_byte(stream);
-        if err == Error.EOF do break;
+    while start < end {
+        if start == end {
+            if fill_err := reader_read_next_chunk(reader); fill_err != .None {
+                return;
+            }
+        }
 
-        switch byte {
+        switch buffer[start] {
             case #char " ", #char "\t", #char "\n", #char "\r" {
-                err, byte = stream_read_byte(stream);
+                start += 1;
             }
 
             case #default do return;
@@ -258,6 +337,71 @@ skip_whitespace :: (use reader: ^Reader) {
     }
 }
 
-skip_bytes :: (use reader: ^Reader, bytes: u32) {
-    for _: bytes do stream_read_byte(stream);
+skip_bytes :: (use reader: ^Reader, bytes: u32) -> (skipped: i32, err: Error) {
+    if bytes == 0 do return 0, .None;
+
+    remaining := bytes;
+    while true {
+        skip := reader_get_buffered(reader);
+        if skip == 0 {
+            if fill_err := reader_read_next_chunk(reader); fill_err != .None {
+                return 0, fill_err;
+            }
+
+            skip = reader_get_buffered(reader);
+        }
+
+        skip = math.min(skip, remaining);
+        start += skip;
+        remaining -= skip;
+
+        if remaining == 0 do return bytes, .None;
+
+        if error != .None {
+            return bytes - remaining, reader_consume_error(reader);
+        }
+    }
+
+    return 0, .None;
+}
+
+#local reader_consume_error :: (use reader: ^Reader) -> Error {
+    defer error = .None;
+    return error;
 }
+
+#local reader_read_next_chunk :: (use reader: ^Reader) -> Error {
+    if done do return .None;
+
+    if start > 0 {
+        // This assumes that memory.copy behaves like memmove, in that the
+        // buffer may overlap, but it should do the right thing.
+        memory.copy(buffer.data, buffer.data + start, end - start);
+        end -= start;
+        start = 0;
+    }
+
+    if end >= buffer.count {
+        return .BufferFull;
+    }
+
+    // Try to re-read multiple times
+    for 4 {
+        err, n := stream_read(stream, buffer[end .. buffer.count]);
+        end += n;
+        if err != .None {
+            if err == .EOF do done = true;
+
+            error = err;
+            return .None;
+        }
+
+        if n > 0 {
+            return .None;
+        }
+    }
+
+    done = true;
+    error = .NoProgress;
+    return .None;
+}
\ No newline at end of file
index 486ce7be6e82a5fdb2e1b4906c4ca9e672b5864d..cc926621f1006dd2e97a711d8f3d74eacbeb76da 100644 (file)
@@ -8,22 +8,21 @@ Stream :: struct {
 
 // #package
 Stream_Vtable :: struct {
-    seek         : (s: ^Stream, to: i32, whence: SeekFrom) -> Error;
-    tell         : (s: ^Stream) -> (Error, u32);
+    seek         : (s: ^Stream, to: i32, whence: SeekFrom) -> Error            = null_proc;
+    tell         : (s: ^Stream) -> (Error, u32)                                = null_proc;
 
-    read         : (s: ^Stream, buffer: [] u8) -> (Error, u32);
-    read_at      : (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32);
-    read_byte    : (s: ^Stream) -> (Error, u8);
-    unread_byte  : (s: ^Stream) -> Error;
+    read         : (s: ^Stream, buffer: [] u8) -> (Error, u32)                 = null_proc;
+    read_at      : (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32)        = null_proc;
+    read_byte    : (s: ^Stream) -> (Error, u8)                                 = null_proc;
 
-    write        : (s: ^Stream, buffer: [] u8) -> (Error, u32);
-    write_at     : (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32);
-    write_byte   : (s: ^Stream, byte: u8) -> Error;
+    write        : (s: ^Stream, buffer: [] u8) -> (Error, u32)                 = null_proc;
+    write_at     : (s: ^Stream, at: u32, buffer: [] u8) -> (Error, u32)        = null_proc;
+    write_byte   : (s: ^Stream, byte: u8) -> Error                             = null_proc;
 
-    close        : (s: ^Stream) -> Error;
-    flush        : (s: ^Stream) -> Error;
+    close        : (s: ^Stream) -> Error                                       = null_proc;
+    flush        : (s: ^Stream) -> Error                                       = null_proc;
 
-    size         : (s: ^Stream) -> i32;
+    size         : (s: ^Stream) -> i32                                         = null_proc;
 }
 
 SeekFrom :: enum {
@@ -67,13 +66,6 @@ stream_read_byte :: (use s: ^Stream) -> (Error, u8) {
     return vtable.read_byte(s);
 }
 
-stream_unread_byte :: (use s: ^Stream) -> Error {
-    if vtable == null do return .NoVtable;
-    if vtable.unread_byte == null_proc do return .NotImplemented;
-    
-    return vtable.unread_byte(s);
-}
-
 stream_write :: (use s: ^Stream, buffer: [] u8) -> (Error, u32) {
     if vtable == null do return .NoVtable, 0;
     if vtable.write == null_proc do return .NotImplemented, 0;
@@ -116,22 +108,6 @@ stream_size :: (use s: ^Stream) -> i32 {
     return vtable.size(s);
 }
 
-stream_peek_byte :: (use s: ^Stream) -> (Error, u8) {
-    err, out := stream_read_byte(s);
-    if err != .None do return err, 0;
-
-    err = stream_unread_byte(s);
-    if err != .None do return err, 0;
-
-    return .None, out;
-}
-
-stream_end_of_file :: (use s: ^Stream) -> bool {
-    err, _ := stream_peek_byte(s);
-    return err == .EOF;
-}
-
-
 
 //
 // StringStream
@@ -201,13 +177,6 @@ string_stream_vtable := Stream_Vtable.{
         return .None, data[curr_pos];
     },
 
-    unread_byte = (use ss: ^StringStream) -> Error {
-        if curr_pos <= 0 do return .OutOfBounds;
-
-        curr_pos -= 1;
-        return .None;
-    },
-
     write = (use ss: ^StringStream, buffer: [] u8) -> (Error, u32) {
         if curr_pos >= data.count do return .EOF, 0;
 
@@ -238,10 +207,7 @@ string_stream_vtable := Stream_Vtable.{
 
     size = (use ss: ^StringStream) -> i32 {
         return data.count;
-    },
-
-    close = null_proc,
-    flush = null_proc,
+    }
 }
 
 
@@ -325,13 +291,6 @@ dynamic_string_stream_vtable := Stream_Vtable.{
         return .None, data[curr_pos];
     },
 
-    unread_byte = (use dss: ^DynamicStringStream) -> Error {
-        if curr_pos <= 0 do return .OutOfBounds;
-
-        curr_pos -= 1;
-        return .None;
-    },
-
     write = (use dss: ^DynamicStringStream, buffer: [] u8) -> (Error, u32) {
         if curr_pos + buffer.count >= data.capacity {
             if !array.ensure_capacity(^data, curr_pos + buffer.count) do return .EOF, 0;
@@ -375,6 +334,4 @@ dynamic_string_stream_vtable := Stream_Vtable.{
 
         return .None;
     },
-
-    close = null_proc,
 }
index 7fcbc969cb6154f94d14ca473c7800c7c778fe6d..de6a7c6f284d6be6ce402062abe13f61cf61d7b9 100644 (file)
@@ -1,22 +1,7 @@
 package core.memory
 
-// This will be replaced / augmented with a memory_copy intrinsic
-// when that is added to the compiler. There would be an easy flag
-// to control if this implementation is used, or the intrinsic. The
-// only case where this implementation should be used is in legacy
-// systems where the memory_copy instruction is not available.
-// However, it might be worth switching to an intrinsic completely
-// and having the intrinsic know whether or not to output the instruction
-// or an inlined version of this procedure.
 use package core.intrinsics.wasm { memory_copy, memory_fill }
 
-// Even though I thought this would work, this method of aliases functions
-// does not currently work. There needs to be a second level of indirection
-// on entities to allow for this case to work, which may not be hard to change
-// but still needs to happen.
-// copy :: memory_copy
-// set  :: memory_fill
-
 copy :: (dest: rawptr, src: rawptr, count: i32) do memory_copy(dest, src, count);
 set  :: (dest: rawptr, byte: u8, count: i32)    do memory_fill(dest, byte, count);
 
index 03fcba5127087d60bb3dc900ddc6227ba32281fb..4a7270637efd65b1896d2ce27737fb584dbf0003 100644 (file)
@@ -383,11 +383,13 @@ parse_section_locations :: (use bin: ^WasmBinary) -> bool {
         magic_buffer: [4] u8;
 
         @Bug // If these are string literals, then the null byte messes up the compiler and it thinks its a 0-character string.
-        if io.read_bytes(^reader, magic_buffer) != u8.[ 0, #char "a", #char "s", #char "m" ] do return false;
-        if io.read_bytes(^reader, magic_buffer) != u8.[ 1, 0, 0, 0 ] do return false; // This may not be necessary
+        io.read_bytes(^reader, magic_buffer);
+        if magic_buffer[0 .. 4] != u8.[ 0, #char "a", #char "s", #char "m" ] do return false;
+        io.read_bytes(^reader, magic_buffer);
+        if magic_buffer[0 .. 4] != u8.[ 1, 0, 0, 0 ] do return false; // This may not be necessary
     }
  
-    while !io.stream_end_of_file(^stream) {
+    while !io.reader_empty(^reader) {
         section_number := cast(WasmSection) io.read_byte(^reader);
         section_size   := read_uleb128(^reader);
         _, pos := io.stream_tell(^stream);
index 60969163948323dc9c1c9dc310594634f27d72b6..ab94da59485dc52cfb80eb55fa1a957eef187f94 100644 (file)
@@ -34,7 +34,7 @@ main :: (args: [] cstr) {
        grid := map.make(Vec2, Cell, .{}); // `true` is black
        defer map.free(^grid);
 
-       while !io.stream_end_of_file(^file_stream) {
+       while !io.reader_empty(^file) {
                line := io.read_line(^file);
 
                loc := Vec2.{ 0, 0 };
diff --git a/tests/aoc-2021/day01 b/tests/aoc-2021/day01
new file mode 100644 (file)
index 0000000..6ba2402
--- /dev/null
@@ -0,0 +1 @@
+Part 2: 1611
diff --git a/tests/aoc-2021/day01.onyx b/tests/aoc-2021/day01.onyx
new file mode 100644 (file)
index 0000000..9fe6f54
--- /dev/null
@@ -0,0 +1,52 @@
+PART :: 2
+
+#load "core/std"
+
+use package core
+
+count_increasing :: (arr: [] $T) -> u32 {
+    increased_count := 0;
+    for 1 .. arr.count {
+        if arr[it - 1] < arr[it] {
+            increased_count += 1;
+        }
+    }
+
+    return increased_count;
+}
+
+main :: (args) => {
+    #if false {
+        contents := #file_contents "./input/day01.txt";
+        reader, stream := io.reader_from_string(contents);
+        defer cfree(stream);
+    }
+
+    err, file := io.open("./tests/aoc-2021/input/day01.txt");
+    assert(err == .None, "Error opening file");
+    reader := io.reader_make(^file);
+    defer io.close(^file);
+
+    nums := array.make(i32);
+    while !io.reader_empty(^reader) {
+        nums << io.read_u32(^reader);
+        io.skip_whitespace(^reader);
+    }
+
+    #if PART == 1 {
+        increased_count := count_increasing(nums);
+        printf("Part 1: {}\n", increased_count);
+    }
+
+    #if PART == 2 {
+        windows := array.make(i32);
+        for i: range.{ 0, nums.count - 2 } {
+            sum := 0;
+            for k: i .. i+3 do sum += nums[k];
+            windows << sum;
+        }
+
+        increased_count := count_increasing(windows);
+        printf("Part 2: {}\n", increased_count);
+    }
+}
\ No newline at end of file
diff --git a/tests/aoc-2021/input/day01.txt b/tests/aoc-2021/input/day01.txt
new file mode 100644 (file)
index 0000000..d08a508
--- /dev/null
@@ -0,0 +1,2000 @@
+151
+152
+153
+158
+159
+163
+164
+162
+161
+167
+169
+168
+169
+170
+164
+175
+176
+178
+187
+186
+193
+188
+192
+203
+206
+218
+215
+239
+243
+244
+238
+239
+240
+229
+245
+247
+255
+244
+250
+256
+257
+263
+265
+267
+266
+267
+268
+271
+277
+278
+275
+276
+278
+280
+281
+282
+274
+307
+310
+317
+323
+325
+345
+346
+366
+364
+363
+364
+368
+370
+372
+373
+403
+398
+399
+401
+406
+407
+415
+416
+421
+423
+421
+428
+430
+431
+420
+421
+418
+420
+422
+415
+416
+417
+416
+406
+405
+403
+413
+423
+456
+459
+460
+462
+463
+477
+482
+483
+486
+489
+524
+519
+522
+523
+525
+522
+523
+531
+530
+531
+535
+546
+558
+554
+546
+553
+559
+567
+575
+576
+598
+623
+624
+626
+629
+626
+631
+633
+634
+635
+638
+639
+647
+645
+647
+648
+650
+665
+675
+677
+680
+682
+688
+674
+675
+686
+678
+679
+682
+693
+703
+714
+718
+720
+709
+718
+714
+715
+717
+741
+742
+762
+764
+773
+774
+771
+747
+749
+755
+763
+765
+771
+773
+760
+767
+798
+799
+804
+822
+824
+845
+852
+851
+848
+846
+875
+882
+881
+882
+881
+883
+888
+897
+921
+923
+913
+921
+928
+945
+947
+944
+948
+950
+965
+966
+968
+965
+987
+999
+1001
+996
+998
+999
+1001
+1011
+1015
+1019
+1021
+1036
+1060
+1070
+1080
+1081
+1046
+1044
+1048
+1054
+1055
+1056
+1045
+1046
+1049
+1044
+1047
+1046
+1044
+1040
+1039
+1047
+1073
+1074
+1065
+1071
+1075
+1056
+1066
+1069
+1070
+1075
+1076
+1082
+1075
+1086
+1092
+1097
+1105
+1116
+1141
+1146
+1149
+1155
+1156
+1172
+1195
+1193
+1210
+1221
+1227
+1228
+1243
+1274
+1285
+1287
+1297
+1305
+1319
+1317
+1313
+1314
+1312
+1315
+1319
+1318
+1309
+1312
+1313
+1298
+1299
+1303
+1318
+1333
+1336
+1330
+1331
+1321
+1320
+1312
+1311
+1313
+1314
+1325
+1344
+1342
+1334
+1335
+1324
+1337
+1339
+1342
+1351
+1377
+1378
+1379
+1391
+1368
+1366
+1357
+1340
+1336
+1342
+1344
+1347
+1381
+1376
+1377
+1382
+1389
+1390
+1406
+1414
+1440
+1445
+1455
+1458
+1459
+1460
+1465
+1467
+1487
+1490
+1491
+1486
+1492
+1490
+1474
+1484
+1488
+1502
+1496
+1503
+1504
+1502
+1493
+1479
+1483
+1506
+1507
+1513
+1515
+1516
+1517
+1519
+1526
+1529
+1530
+1532
+1531
+1534
+1533
+1548
+1553
+1552
+1555
+1554
+1558
+1563
+1575
+1569
+1571
+1605
+1607
+1588
+1600
+1604
+1605
+1620
+1621
+1614
+1615
+1623
+1656
+1657
+1677
+1676
+1678
+1686
+1653
+1661
+1668
+1669
+1667
+1672
+1661
+1659
+1669
+1656
+1662
+1668
+1669
+1678
+1675
+1679
+1683
+1691
+1728
+1705
+1700
+1706
+1707
+1703
+1705
+1694
+1700
+1704
+1706
+1707
+1713
+1756
+1764
+1774
+1773
+1774
+1793
+1794
+1793
+1795
+1815
+1837
+1867
+1876
+1893
+1914
+1924
+1942
+1944
+1952
+1959
+1956
+1952
+1951
+1953
+1973
+1974
+1961
+1963
+1985
+1982
+2004
+2022
+2026
+2032
+2046
+2034
+2032
+2033
+2026
+2055
+2056
+2066
+2067
+2065
+2071
+2074
+2084
+2085
+2101
+2079
+2065
+2067
+2068
+2077
+2087
+2116
+2117
+2120
+2121
+2131
+2144
+2143
+2151
+2154
+2158
+2177
+2182
+2192
+2198
+2195
+2197
+2233
+2234
+2233
+2235
+2238
+2244
+2239
+2240
+2257
+2267
+2270
+2271
+2284
+2293
+2294
+2300
+2305
+2312
+2325
+2326
+2332
+2350
+2355
+2354
+2350
+2347
+2348
+2321
+2343
+2340
+2341
+2336
+2331
+2353
+2354
+2353
+2370
+2371
+2372
+2383
+2382
+2376
+2378
+2386
+2392
+2393
+2405
+2409
+2412
+2415
+2430
+2431
+2450
+2454
+2456
+2465
+2479
+2480
+2483
+2475
+2479
+2482
+2483
+2492
+2489
+2498
+2490
+2501
+2505
+2509
+2530
+2531
+2526
+2529
+2554
+2557
+2558
+2564
+2557
+2558
+2559
+2560
+2561
+2562
+2561
+2562
+2550
+2553
+2573
+2592
+2591
+2585
+2587
+2580
+2582
+2567
+2568
+2579
+2580
+2588
+2589
+2590
+2591
+2592
+2589
+2593
+2599
+2605
+2610
+2623
+2625
+2629
+2635
+2634
+2638
+2641
+2605
+2606
+2597
+2608
+2616
+2619
+2624
+2628
+2631
+2636
+2657
+2660
+2662
+2663
+2667
+2670
+2673
+2676
+2678
+2681
+2682
+2705
+2714
+2716
+2726
+2764
+2768
+2770
+2749
+2753
+2758
+2749
+2761
+2767
+2771
+2769
+2771
+2773
+2780
+2782
+2798
+2809
+2821
+2812
+2823
+2830
+2833
+2838
+2858
+2859
+2876
+2877
+2879
+2851
+2852
+2859
+2844
+2858
+2848
+2852
+2850
+2854
+2883
+2888
+2889
+2891
+2895
+2905
+2913
+2920
+2941
+2939
+2940
+2938
+2952
+2944
+2928
+2934
+2932
+2928
+2942
+2931
+2947
+2948
+2952
+2949
+2946
+2951
+2944
+2947
+2950
+2951
+2948
+2944
+2942
+2941
+2939
+2950
+2949
+2956
+2967
+2969
+2968
+2977
+2984
+2997
+3028
+3025
+3027
+3037
+3042
+3039
+3043
+3044
+3065
+3066
+3069
+3066
+3074
+3076
+3085
+3061
+3062
+3061
+3060
+3061
+3062
+3063
+3064
+3067
+3068
+3074
+3086
+3088
+3096
+3106
+3109
+3114
+3115
+3119
+3113
+3109
+3137
+3129
+3110
+3099
+3098
+3099
+3090
+3120
+3121
+3122
+3160
+3165
+3181
+3186
+3191
+3200
+3199
+3196
+3198
+3199
+3200
+3204
+3208
+3210
+3213
+3215
+3225
+3226
+3218
+3224
+3234
+3243
+3249
+3241
+3238
+3239
+3240
+3241
+3242
+3243
+3248
+3236
+3263
+3265
+3268
+3271
+3270
+3271
+3279
+3275
+3271
+3276
+3275
+3286
+3285
+3286
+3290
+3292
+3293
+3314
+3307
+3302
+3310
+3311
+3306
+3307
+3321
+3322
+3323
+3313
+3316
+3317
+3318
+3319
+3323
+3325
+3328
+3326
+3327
+3340
+3341
+3340
+3341
+3347
+3358
+3370
+3372
+3404
+3411
+3414
+3417
+3424
+3428
+3434
+3443
+3444
+3451
+3457
+3460
+3442
+3443
+3454
+3464
+3465
+3461
+3457
+3458
+3481
+3492
+3497
+3501
+3521
+3528
+3533
+3534
+3537
+3538
+3550
+3556
+3557
+3550
+3557
+3556
+3555
+3556
+3557
+3572
+3576
+3577
+3579
+3580
+3581
+3582
+3590
+3593
+3611
+3618
+3630
+3631
+3623
+3615
+3610
+3616
+3617
+3623
+3624
+3625
+3642
+3643
+3653
+3654
+3638
+3639
+3640
+3645
+3649
+3668
+3681
+3675
+3683
+3692
+3690
+3726
+3699
+3702
+3703
+3704
+3702
+3703
+3724
+3729
+3730
+3724
+3733
+3734
+3740
+3742
+3749
+3746
+3748
+3750
+3749
+3748
+3737
+3734
+3729
+3739
+3764
+3775
+3788
+3801
+3811
+3815
+3816
+3820
+3818
+3805
+3804
+3810
+3814
+3813
+3816
+3815
+3825
+3840
+3861
+3867
+3883
+3884
+3882
+3883
+3884
+3889
+3885
+3887
+3890
+3900
+3903
+3909
+3912
+3913
+3917
+3916
+3931
+3948
+3947
+3951
+3971
+3996
+4010
+4011
+4010
+4012
+4018
+4022
+4034
+4042
+4048
+4051
+4055
+4051
+4062
+4073
+4080
+4086
+4093
+4095
+4100
+4101
+4108
+4094
+4100
+4101
+4106
+4107
+4110
+4111
+4112
+4116
+4143
+4148
+4159
+4162
+4163
+4174
+4173
+4175
+4176
+4185
+4186
+4208
+4206
+4214
+4211
+4210
+4204
+4194
+4197
+4198
+4215
+4221
+4219
+4222
+4224
+4225
+4238
+4239
+4251
+4252
+4254
+4250
+4252
+4257
+4261
+4252
+4256
+4289
+4305
+4304
+4308
+4298
+4300
+4302
+4303
+4304
+4299
+4320
+4329
+4344
+4351
+4343
+4344
+4345
+4344
+4343
+4344
+4347
+4340
+4342
+4361
+4369
+4371
+4374
+4375
+4378
+4399
+4404
+4408
+4402
+4403
+4406
+4404
+4411
+4419
+4421
+4426
+4421
+4434
+4438
+4446
+4447
+4448
+4445
+4452
+4469
+4470
+4469
+4473
+4482
+4496
+4497
+4491
+4493
+4496
+4494
+4495
+4496
+4503
+4505
+4504
+4469
+4474
+4476
+4481
+4496
+4495
+4499
+4500
+4501
+4503
+4504
+4508
+4520
+4521
+4540
+4541
+4542
+4543
+4545
+4561
+4563
+4601
+4608
+4611
+4617
+4616
+4617
+4619
+4618
+4639
+4642
+4665
+4669
+4673
+4680
+4692
+4697
+4707
+4710
+4717
+4732
+4734
+4735
+4752
+4753
+4754
+4761
+4760
+4758
+4770
+4777
+4776
+4782
+4780
+4783
+4793
+4798
+4799
+4800
+4821
+4822
+4823
+4821
+4832
+4838
+4841
+4845
+4847
+4846
+4852
+4879
+4882
+4880
+4885
+4904
+4908
+4911
+4914
+4915
+4942
+4948
+4938
+4956
+4955
+4954
+4955
+4957
+4985
+4971
+4980
+4983
+4982
+4988
+4978
+4989
+4993
+4995
+5013
+5011
+5013
+5009
+5018
+5042
+5043
+5044
+5045
+5044
+5046
+5048
+5040
+5061
+5056
+5045
+5051
+5077
+5103
+5106
+5107
+5108
+5111
+5112
+5125
+5126
+5131
+5133
+5147
+5152
+5134
+5135
+5137
+5150
+5165
+5174
+5191
+5212
+5213
+5228
+5229
+5230
+5233
+5224
+5228
+5230
+5232
+5235
+5253
+5261
+5285
+5286
+5278
+5274
+5275
+5279
+5286
+5296
+5297
+5299
+5303
+5302
+5303
+5304
+5311
+5312
+5321
+5325
+5336
+5350
+5351
+5354
+5361
+5365
+5373
+5374
+5377
+5370
+5381
+5384
+5387
+5383
+5385
+5388
+5399
+5400
+5401
+5402
+5405
+5428
+5433
+5434
+5432
+5433
+5434
+5437
+5441
+5466
+5465
+5466
+5469
+5470
+5472
+5474
+5478
+5489
+5491
+5492
+5500
+5503
+5507
+5510
+5512
+5530
+5529
+5530
+5534
+5535
+5537
+5540
+5542
+5543
+5539
+5556
+5560
+5590
+5592
+5599
+5598
+5605
+5602
+5605
+5606
+5607
+5608
+5609
+5643
+5647
+5646
+5650
+5663
+5669
+5677
+5688
+5699
+5701
+5700
+5703
+5714
+5716
+5719
+5720
+5763
+5768
+5778
+5818
+5821
+5831
+5841
+5845
+5846
+5866
+5869
+5888
+5902
+5906
+5907
+5910
+5924
+5905
+5906
+5909
+5926
+5947
+5948
+5938
+5939
+5940
+5941
+5942
+5943
+5944
+5951
+5950
+5947
+5936
+5937
+5928
+5942
+5934
+5943
+5949
+5948
+5966
+5967
+5961
+5958
+5964
+5967
+5929
+5930
+5939
+5941
+5951
+5949
+5944
+5976
+5963
+5957
+5973
+5974
+5969
+5975
+5976
+5978
+5985
+5986
+5987
+5988
+5993
+6002
+5999
+5998
+6001
+5993
+6009
+6004
+6010
+6014
+6030
+6039
+6046
+6062
+6074
+6073
+6079
+6080
+6081
+6085
+6086
+6084
+6082
+6090
+6105
+6117
+6120
+6127
+6125
+6126
+6125
+6138
+6154
+6155
+6161
+6168
+6171
+6159
+6161
+6162
+6163
+6165
+6160
+6156
+6165
+6163
+6162
+6164
+6165
+6166
+6165
+6166
+6161
+6171
+6183
+6186
+6179
+6180
+6171
+6212
+6211
+6213
+6210
+6215
+6216
+6230
+6235
+6236
+6265
+6281
+6301
+6303
+6305
+6304
+6305
+6306
+6307
+6322
+6338
+6339
+6341
+6344
+6348
+6350
+6360
+6364
+6369
+6371
+6379
+6404
+6409
+6433
+6434
+6435
+6445
+6449
+6453
+6454
+6451
+6455
+6448
+6450
+6455
+6457
+6458
+6477
+6480
+6485
+6496
+6495
+6497
+6498
+6499
+6501
+6502
+6496
+6493
+6494
+6497
+6498
+6501
+6498
+6500
+6505
+6519
+6526
+6530
+6534
+6544
+6552
+6555
+6562
+6573
+6564
+6566
+6567
+6581
+6587
+6590
+6601
+6606
+6605
+6606
+6591
+6590
+6604
+6606
+6619
+6627
+6626
+6627
+6624
+6625
+6627
+6633
+6632
+6633
+6634
+6632
+6631
+6637
+6642
+6643
+6617
+6591
+6593
+6595
+6607
+6608
+6612
+6614
+6617
+6619
+6628
+6632
+6638
+6636
+6637
+6639
+6640
+6644
+6649
+6650
+6651
+6656
+6650
+6653
+6667
+6670
+6673
+6677
+6683
+6702
+6713
+6714
+6716
+6726
+6727
+6740
+6736
+6742
+6741
+6774
+6772
+6778
+6761
+6760
+6763
+6772
+6768
+6770
+6778
+6779
+6790
+6792
+6800
+6801
+6807
+6803
+6807
+6808
+6811
+6813
+6814
+6817
+6819
+6829
+6848
+6852
+6853
+6854
+6860
+6867
+6864
+6887
+6890
+6900
+6921
+6937
+6938
+6950
+6968
+6992
+6993
+6989
+6990
+6988
+7004
+7011
+7009
+7001
+7015
+7019
+7036
+7054
+7063
+7072
+7063
+7065
+7063
+7073
+7076
+7077
+7083
+7086
+7087
+7088
+7089
+7093
+7107
+7109
+7123
+7126
+7127
+7129
+7131
+7134
+7135
+7154
+7177
+7180
+7193
+7195
+7197
+7228
+7236
+7237
+7238
+7240
+7244
+7246
+7250
+7279
+7272
+7282
+7283
+7303
+7302
+7301
+7302
+7303
+7304
+7307
+7310
+7309
+7295
+7294
+7299
+7319
+7347
+7356
+7355
+7360
+7361
+7358
+7384
+7388
+7387
+7392
+7396
+7402
+7397
+7387
+7390
+7393
+7388
+7390
+7391
+7393
+7412
+7417
+7418
+7417
+7414
+7415
+7419
+7418
+7419
+7425
+7426
+7435
+7436
+7431
+7437
+7438
+7439
+7453
+7454
+7452
+7454
+7458
+7459
+7460
+7473
+7475
+7472
+7455
+7461
+7459
+7467
+7472
+7474
+7468
+7470
+7473
+7482
+7478
+7479
+7476
+7471
+7484
+7492
+7501
+7502
+7507
+7513
+7514
+7512
+7513
+7520
+7539
+7543
+7547
+7546
+7536
+7535
+7536
+7537
+7538
+7537
+7532
+7518
+7510
+7532
+7548
+7549
+7576
+7577
+7579
+7586
+7587
+7598
+7599
+7606
+7607
+7606
+7607
+7605
+7602
+7603
+7606
+7609
+7614
+7613
+7597
+7596
+7607
+7608
+7609
+7629
+7628
+7631
+7632
+7650
+7658
+7635
+7646
+7647
+7663
+7660
+7665
+7677
+7667
+7671
+7675
+7673
+7676
+7677
+7675
+7685
+7695
+7702
+7703
+7702
+7698
+7701
+7676
+7695
+7704
+7726
+7722
+7727
+7728
+7757
+7762
+7764
+7765
+7768
+7769
+7768
+7770
+7784
+7785
+7796
+7799
+7801
+7803
+7808
+7811
+7822
+7824
+7828
+7795
+7796
+7806
+7807
+7802
+7804
+7802
+7825
+7832
+7834
+7830
+7831
+7835
+7836
+7843
+7849
+7856
+7855
+7879
+7878
+7879
+7885
+7888
+7887
+7901
+7908
+7911
+7914
+7924
+7934
+7935
+7936
+7937
+7938
+7939
+7941
+7951
+7956
+7972
+7973
+7976
+7977
+7983
+7982
+7995
+7994
+7995
+7996
+8003
+8005
+8000
+8011
+8009
+8004
+8006
+8024
+8034
+8027
+8039
+8043
+8022
+8025
+8022
+8018
+8020
+8024
+8036
+8044
+8047
+8045
+8046
+8050
+8045
+8046
+8053
+8068
+8070
+8072
+8067
+8056
+8064
+8075
+8078
+8081
+8112
+8127
\ No newline at end of file