From: Brendan Hansen Date: Wed, 24 Aug 2022 19:58:16 +0000 (-0500) Subject: StringStream and DynamicStringStream -> BufferStream X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=db2e13410b61d8650611d86f1a4524be4045793a;p=onyx.git StringStream and DynamicStringStream -> BufferStream --- diff --git a/core/builtin.onyx b/core/builtin.onyx index f13edc4b..0b6fe9c2 100644 --- a/core/builtin.onyx +++ b/core/builtin.onyx @@ -218,7 +218,7 @@ cfree :: (ptr: rawptr) do raw_free(context.allocator, ptr); delete :: #match { #precedence 1000 macro (x: ^$T, allocator := context.allocator) { - raw_free(allocator, x); + if x != null do raw_free(allocator, x); } } } diff --git a/core/container/map.onyx b/core/container/map.onyx index 72537f43..df66e8c1 100644 --- a/core/container/map.onyx +++ b/core/container/map.onyx @@ -76,8 +76,8 @@ init :: (use map: ^Map($K, $V), default := V.{}) { } free :: (use map: ^Map) { - if map.hashes.data != null do memory.free_slice(^hashes, allocator=allocator); - if map.entries.data != null do array.free(^entries); + if hashes.data != null do memory.free_slice(^hashes, allocator=allocator); + if entries.data != null do array.free(^entries); } #match (package builtin).delete (package core.map).free diff --git a/core/conv.onyx b/core/conv.onyx index 2b5ab564..f9642cd1 100644 --- a/core/conv.onyx +++ b/core/conv.onyx @@ -577,11 +577,11 @@ format_any :: (output: ^Format_Output, formatting: ^Format, v: any) { // This is a little gross but the only way to output the type name for a type_expr // is through a io.Writer. That should maybe be changed in the future? Also, I think // 256 bytes is enough for the name of a type but I'm not entirely sure... - stream := io.string_stream_make(~~buf); + stream := io.buffer_stream_make(~~buf, fixed=true); writer := io.writer_make(^stream); write_type_name(^writer, value); - output->write(io.string_stream_to_str(^stream)); + output->write(io.buffer_stream_to_str(^stream)); } case #default { diff --git a/core/io/reader.onyx b/core/io/reader.onyx index 56b40690..ab7fa0d1 100644 --- a/core/io/reader.onyx +++ b/core/io/reader.onyx @@ -67,9 +67,9 @@ reader_free :: (use reader: ^Reader) { // You need to free the StringStream from the context.allocator when you use this. For example, // reader, stream := reader_from_string(s); // defer cfree(stream); -reader_from_string :: (s: str) -> (Reader, ^StringStream) { - stream_ptr := new(StringStream); - *stream_ptr = string_stream_make(s); +reader_from_string :: (s: str) -> (Reader, ^BufferStream) { + stream_ptr := new(BufferStream); + *stream_ptr = buffer_stream_make(s, fixed=true, write_enabled=false); return reader_make(stream_ptr), stream_ptr; } diff --git a/core/io/stream.onyx b/core/io/stream.onyx index f88ab13e..bff7bfbe 100644 --- a/core/io/stream.onyx +++ b/core/io/stream.onyx @@ -110,164 +110,126 @@ stream_size :: (use s: ^Stream) -> i32 { // -// StringStream +// BufferStream // -StringStream :: struct { - use stream : Stream; +// In older times of the standard library, there were two string +// streams that existed for different purposes: StringStream and +// DynamicStringStream. I have since merged them into one stream +// called, BufferStream. Besides the names being confusing and +// long, I thought it was needlessly complicated to have two +// kinds of streams for what feels like the same task. +// +// When creating a BufferStream, there are two options: +// - You can pass a single integer, will which represent +// the initial size of the buffer. An initial buffer +// will be allocated using the allocator provided. +// This buffer will be write enabled, and not fixed, +// so it can grow to as (theoretically) large as +// necessary. +// +// - You can pass an existing slice. The behavior of this +// buffer depend on the other arguments. If you set +// write_enabled to false, then writing will be allowed +// into this buffer. This is good practice if you only +// plan on using the stream as a read-only output for +// io.Reader. If you enable writing, then you can +// decided if the buffer is fixed or not. If the buffer +// is fixed, the provided buffer will be used without +// allocating anything, and the data written will go +// directly into the provided slice. Obviously, when +// fixed is true, the buffer cannot grow and might result +// in filling the buffer and losing information. An .EOF +// error is returned in that case. If you pass fixed as +// false, the provided buffer will be copied into a new +// dynamic array. Note, the cursor still starts at index +// 0, so you have to use stream_tell to move the cursor +// to the correct spot (This behavior might change in the +// future). +// +// Remember to free the buffer using buffer_stream_free or delete. +// +// Note, if write_enabled is false, buffer_stream_to_str will return +// the null string, as convert a non-writable buffer to a string does +// not have a great natural definition. - curr_pos : i32; - data : str; -} -string_stream_make :: (s: str) -> StringStream { - return StringStream.{ - stream = Stream.{ - vtable = ^string_stream_vtable, - }, - data = s, - curr_pos = 0, - }; -} +BufferStream :: struct { + use stream : Stream; -string_stream_to_str :: (use ss: ^StringStream) -> str { - return .{ data.data, curr_pos }; -} + data: [..] u8; + curr_pos := 0; -#package -string_stream_vtable := Stream_Vtable.{ - seek = (use ss: ^StringStream, to: i32, whence: SeekFrom) -> Error { - if to >= data.count do return .OutOfBounds; + write_enabled := true; + fixed := false; +} - switch whence { - case SeekFrom.Start do curr_pos = to; - case SeekFrom.Current do curr_pos += to; - case SeekFrom.End do curr_pos = data.count - to; // CHECK: Off by one? +buffer_stream_make :: #match #locked { + (initial_data: [] u8, allocator := context.allocator, fixed := false, write_enabled := true) -> BufferStream { + if !write_enabled { + return .{ + .{ vtable = ^buffer_stream_vtable }, + data = .{ + data = initial_data.data, + count = initial_data.count + // This does not specify the allocator, + // but since write_enabled is false, + // this should never be resized. + }, + write_enabled = false, + fixed = true + }; } - return .None; - }, - - tell = (use ss: ^StringStream) -> (Error, u32) { - return .None, curr_pos; - }, - - read = (use ss: ^StringStream, buffer: [] u8) -> (Error, u32) { - if curr_pos >= data.count do return .EOF, 0; - - 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; - - return .None, bytes_to_read; - }, - - read_at = (use ss: ^StringStream, at: u32, buffer: [] u8) -> (Error, u32) { - if at >= data.count do return .EOF, 0; - - bytes_to_read := math.min(buffer.count, data.count - at); - memory.copy(buffer.data, ^data.data[at], bytes_to_read); - - return .None, bytes_to_read; - }, - - read_byte = (use ss: ^StringStream) -> (Error, u8) { - if curr_pos >= data.count do return .EOF, 0; - - defer curr_pos += 1; - return .None, data[curr_pos]; - }, - - write = (use ss: ^StringStream, buffer: [] u8) -> (Error, u32) { - if curr_pos >= data.count do return .EOF, 0; - - bytes_to_write := math.min(buffer.count, data.count - curr_pos); - memory.copy(^data.data[curr_pos], buffer.data, bytes_to_write); - curr_pos += bytes_to_write; - - return .None, bytes_to_write; - }, - - write_at = (use ss: ^StringStream, at: u32, buffer: [] u8) -> (Error, u32) { - if at >= data.count do return .EOF, 0; - - bytes_to_write := math.min(buffer.count, data.count - at); - memory.copy(^data.data[at], buffer.data, bytes_to_write); - - return .None, bytes_to_write; - }, - - write_byte = (use ss: ^StringStream, byte: u8) -> Error { - if curr_pos >= data.count do return .EOF; - - data[curr_pos] = byte; - curr_pos += 1; + // Make a copy of the data using the provided allocator. + data: [..] u8 = .{ initial_data.data, initial_data.count, initial_data.count }; + if !fixed { + data = array.make(initial_data, allocator); + } - return .None; + return .{ .{ vtable = ^buffer_stream_vtable }, data, fixed=fixed }; }, - size = (use ss: ^StringStream) -> i32 { - return data.count; + (initial_size := 1024, allocator := context.allocator) -> BufferStream { + return .{ + .{ vtable = ^buffer_stream_vtable }, + make([..] u8, 1024, allocator = allocator) + }; } } - -// -// DynamicStringStream -// -DynamicStringStream :: struct { - use stream : Stream; - - curr_pos : i32; - data : [..] u8; - - to_str :: dynamic_string_stream_to_str; +buffer_stream_free :: (use bs: ^BufferStream) { + if write_enabled && !fixed { + delete(^data); + } } -dynamic_string_stream_make :: (init_size := 128, a := context.allocator) -> DynamicStringStream { - data : [..] u8; - array.init(^data, init_size, allocator=a); - - return DynamicStringStream.{ - stream = Stream.{ - vtable = ^dynamic_string_stream_vtable, - }, - - curr_pos = 0, - data = data, - }; -} +#match builtin.delete buffer_stream_free -dynamic_string_stream_free :: (use dds: ^DynamicStringStream) { - array.free(^data); -} +buffer_stream_to_str :: (use bs: ^BufferStream) -> str { + if !write_enabled do return null_str; -dynamic_string_stream_to_str :: (use dds: ^DynamicStringStream) -> str { - return data.data[0 .. curr_pos]; + return data[0 .. curr_pos]; } #package -dynamic_string_stream_vtable := Stream_Vtable.{ - seek = (use dss: ^DynamicStringStream, to: i32, whence: SeekFrom) -> Error { - 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? - } +buffer_stream_vtable := Stream_Vtable.{ + seek = (use ss: ^BufferStream, to: i32, whence: SeekFrom) -> Error { + if to >= data.count do return .OutOfBounds; - if dest >= data.count { - if !array.ensure_capacity(^data, dest) do return .OutOfBounds; + switch whence { + case SeekFrom.Start do curr_pos = to; + case SeekFrom.Current do curr_pos += to; + case SeekFrom.End do curr_pos = data.count - to; // CHECK: Off by one? } - curr_pos = dest; return .None; }, - tell = (use dss: ^DynamicStringStream) -> (Error, u32) { + tell = (use ss: ^BufferStream) -> (Error, u32) { return .None, curr_pos; }, - read = (use dss: ^DynamicStringStream, buffer: [] u8) -> (Error, u32) { + read = (use ss: ^BufferStream, buffer: [] u8) -> (Error, u32) { if curr_pos >= data.count do return .EOF, 0; bytes_to_read := math.min(buffer.count, data.count - curr_pos); @@ -277,7 +239,7 @@ dynamic_string_stream_vtable := Stream_Vtable.{ return .None, bytes_to_read; }, - read_at = (use dss: ^DynamicStringStream, at: u32, buffer: [] u8) -> (Error, u32) { + read_at = (use ss: ^BufferStream, at: u32, buffer: [] u8) -> (Error, u32) { if at >= data.count do return .EOF, 0; bytes_to_read := math.min(buffer.count, data.count - at); @@ -286,27 +248,33 @@ dynamic_string_stream_vtable := Stream_Vtable.{ return .None, bytes_to_read; }, - read_byte = (use dss: ^DynamicStringStream) -> (Error, u8) { + read_byte = (use ss: ^BufferStream) -> (Error, u8) { if curr_pos >= data.count do return .EOF, 0; defer curr_pos += 1; return .None, data[curr_pos]; }, - write = (use dss: ^DynamicStringStream, buffer: [] u8) -> (Error, u32) { + write = (use dss: ^BufferStream, buffer: [] u8) -> (Error, u32) { + if !write_enabled do return .NotImplemented, 0; + if curr_pos + buffer.count >= data.capacity { + if fixed do return .EOF, 0; if !array.ensure_capacity(^data, curr_pos + buffer.count) do return .EOF, 0; } memory.copy(^data.data[curr_pos], buffer.data, buffer.count); curr_pos += buffer.count; - data.count += buffer.count; + data.count = math.max(data.count, curr_pos); return .None, buffer.count; }, - write_at = (use dss: ^DynamicStringStream, at: u32, buffer: [] u8) -> (Error, u32) { + write_at = (use dss: ^BufferStream, at: u32, buffer: [] u8) -> (Error, u32) { + if !write_enabled do return .NotImplemented, 0; + if at + buffer.count >= data.capacity { + if fixed do return .EOF, 0; if !array.ensure_capacity(^data, at + buffer.count) do return .EOF, 0; } @@ -316,24 +284,32 @@ dynamic_string_stream_vtable := Stream_Vtable.{ return .None, buffer.count; }, - write_byte = (use dss: ^DynamicStringStream, byte: u8) -> Error { - if !array.ensure_capacity(^data, data.count + 1) do return .EOF; + write_byte = (use dss: ^BufferStream, byte: u8) -> Error { + if !write_enabled do return .NotImplemented; + + if curr_pos + 1 >= data.capacity { + if fixed do return .EOF; + if !array.ensure_capacity(^data, data.count + 1) do return .EOF; + } data[curr_pos] = byte; curr_pos += 1; - data.count += 1; + data.count = math.max(data.count, curr_pos); return .None; }, - size = (use dss: ^DynamicStringStream) -> i32 { + size = (use dss: ^BufferStream) -> i32 { return data.count; }, - flush = (use dss: ^DynamicStringStream) -> Error { + flush = (use dss: ^BufferStream) -> Error { + if !write_enabled || fixed do return .NotImplemented; + curr_pos = 0; array.clear(^data); return .None; }, } + diff --git a/core/io/writer.onyx b/core/io/writer.onyx index ed466b7e..f545b9f0 100644 --- a/core/io/writer.onyx +++ b/core/io/writer.onyx @@ -16,9 +16,9 @@ writer_make :: (s: ^Stream) -> Writer { // Future-proofing the API writer_free :: (w: ^Writer) {} -string_builder :: (allocator := context.allocator) -> (Writer, ^DynamicStringStream) { - new_stream := new(DynamicStringStream, allocator=allocator); - *new_stream = dynamic_string_stream_make(); +string_builder :: (allocator := context.allocator) -> (Writer, ^BufferStream) { + new_stream := new(BufferStream, allocator=allocator); + *new_stream = buffer_stream_make(); return writer_make(new_stream), new_stream; } diff --git a/core/stdio.onyx b/core/stdio.onyx index 4d98e95c..20e2113f 100644 --- a/core/stdio.onyx +++ b/core/stdio.onyx @@ -114,12 +114,12 @@ byte_dump :: (ptr: rawptr, byte_count: u32, bytes_per_line := 8) { // #thread_local stdio : struct { - print_stream : io.DynamicStringStream; + print_stream : io.BufferStream; print_writer : io.Writer; } __stdio_init :: () { - stdio.print_stream = io.dynamic_string_stream_make(2048, context.allocator); + stdio.print_stream = io.buffer_stream_make(2048, context.allocator); stdio.print_writer = io.writer_make(^stdio.print_stream); // This shouldn't need to be here, but because ^stdin_vtable is not a compile-time @@ -132,7 +132,7 @@ __flush_stdio :: () { if stdio.print_stream.data.count == 0 do return; ^stdio.print_stream - |> io.dynamic_string_stream_to_str() + |> io.buffer_stream_to_str() |> runtime.__output_string(); ^stdio.print_stream |> io.stream_flush(); diff --git a/src/clone.c b/src/clone.c index b1a2a98d..828f6613 100644 --- a/src/clone.c +++ b/src/clone.c @@ -461,6 +461,7 @@ AstNode* ast_clone(bh_allocator a, void* n) { new_param.local = (AstLocal *) ast_clone(a, param->local); new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty; new_param.default_value = (AstTyped *) ast_clone(a, param->default_value); + new_param.use_processed = 0; dont_copy_structs = 0; new_param.vararg_kind = param->vararg_kind; @@ -605,6 +606,7 @@ AstFunction* clone_function_header(bh_allocator a, AstFunction* func) { new_param.local = (AstLocal *) ast_clone(a, param->local); new_param.local->flags &= ~Ast_Flag_Param_Symbol_Dirty; new_param.default_value = (AstTyped *) ast_clone(a, param->default_value); + new_param.use_processed = 0; dont_copy_structs = 0; new_param.vararg_kind = param->vararg_kind; diff --git a/src/symres.c b/src/symres.c index ca756ae8..f006c764 100644 --- a/src/symres.c +++ b/src/symres.c @@ -337,17 +337,19 @@ static SymresStatus symres_field_access(AstFieldAccess** fa) { char *closest = find_closest_symbol_in_node((AstNode *) expr, (*fa)->token->text); token_toggle_end((*fa)->token); + AstPackage *package = (AstPackage *) strip_aliases((AstNode *) (*fa)->expr); + if (closest) { onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Did you mean '%s'?", (*fa)->token->text, (*fa)->token->length, - ((AstPackage *) (*fa)->expr)->package->name, + package->package->name, closest); } else { onyx_report_error((*fa)->token->pos, Error_Critical, "'%b' was not found in package '%s'. Perhaps it is defined in a file that wasn't loaded?", (*fa)->token->text, (*fa)->token->length, - ((AstPackage *) (*fa)->expr)->package->name); + package->package->name); } return Symres_Error; diff --git a/src/types.c b/src/types.c index a3687131..0d9568a0 100644 --- a/src/types.c +++ b/src/types.c @@ -1112,8 +1112,10 @@ Type* type_get_contained_type(Type* type) { } static const StructMember slice_members[] = { - { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 }, - { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 }, + { 0, 0, NULL, "data", NULL, NULL, -1, 0, 0 }, + { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 }, + { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 }, + { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 }, }; static const StructMember array_members[] = { @@ -1121,6 +1123,8 @@ static const StructMember array_members[] = { { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "count", NULL, NULL, -1, 0, 0 }, { POINTER_SIZE + 4, 2, &basic_types[Basic_Kind_U32], "capacity", NULL, NULL, -1, 0, 0 }, { POINTER_SIZE + 8, 3, NULL, "allocator", NULL, NULL, -1, 0, 0 }, + { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "size", NULL, NULL, -1, 0, 0 }, + { POINTER_SIZE, 1, &basic_types[Basic_Kind_U32], "length", NULL, NULL, -1, 0, 0 }, }; b32 type_lookup_member(Type* type, char* member, StructMember* smem) { diff --git a/tests/aoc-2020/day22.onyx b/tests/aoc-2020/day22.onyx index b8913537..0289afc7 100644 --- a/tests/aoc-2020/day22.onyx +++ b/tests/aoc-2020/day22.onyx @@ -43,7 +43,7 @@ combat :: (player1: ^[..] u32, player2: ^[..] u32) -> u32 { // 4,5,2,8,|1,3,9,7, // Larger numbers are encoded in base 64. encode_hands :: (alloc: Allocator, p1: ^[..] u32, p2: ^[..] u32) -> str { - stream := io.dynamic_string_stream_make(256, alloc); + stream := io.buffer_stream_make(256, alloc); writer := io.writer_make(^stream); for n: *p1 { @@ -56,7 +56,7 @@ encode_hands :: (alloc: Allocator, p1: ^[..] u32, p2: ^[..] u32) -> str { io.write_str(^writer, ","); } - return io.dynamic_string_stream_to_str(^stream); + return io.buffer_stream_to_str(^stream); } recursive_combat :: (player1: ^[..] u32, player2: ^[..] u32) -> u32 { diff --git a/tests/aoc-2020/day24.onyx b/tests/aoc-2020/day24.onyx index 2111109c..47a534cb 100644 --- a/tests/aoc-2020/day24.onyx +++ b/tests/aoc-2020/day24.onyx @@ -28,7 +28,7 @@ Cell :: struct { main :: (args: [] cstr) { contents := #file_contents "./tests/aoc-2020/input/day24.txt"; - file_stream := io.string_stream_make(contents); + file_stream := io.buffer_stream_make(contents); file := io.reader_make(^file_stream); grid := map.make(Vec2, Cell, .{}); // `true` is black diff --git a/tests/string_stream_test.onyx b/tests/string_stream_test.onyx index b3eb204a..9f722810 100644 --- a/tests/string_stream_test.onyx +++ b/tests/string_stream_test.onyx @@ -5,7 +5,7 @@ use package core main :: (args: [] cstr) { some_string := "This is a test string that can be read.\n\n\n\n\n\n1234 4567"; - sstream := io.string_stream_make(some_string); + sstream := io.buffer_stream_make(some_string); sreader := io.reader_make(^sstream); for i: 0 .. 2 {