From 33c6f75a2ea8ba1b21c64767914d5faf4e2d22b0 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 10 May 2024 17:00:07 -0400 Subject: [PATCH 01/34] Fix joining non-`Started` threads from blocking main thread --- core/thread/thread_unix.odin | 9 +++++++++ core/thread/thread_windows.odin | 12 ++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 290f86eac..7c353fd33 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -36,6 +36,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { sync.wait(&t.cond, &t.mutex) } + if .Joined in t.flags { + return nil + } + when ODIN_OS != .Darwin { // Enable thread's cancelability. if can_set_thread_cancel_state { @@ -143,6 +147,11 @@ _join :: proc(t: ^Thread) { if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok { return } + // Prevent non-started threads from blocking main thread with initial wait + // condition. + if .Started not_in unjoined { + _start(t) + } unix.pthread_join(t.unix_thread, nil) } diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index e85b2b62a..4e5e8c07a 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -24,6 +24,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { __windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD { t := (^Thread)(t_) + if .Joined in t.flags { + return 0 + } + t.id = sync.current_thread_id() { @@ -93,11 +97,15 @@ _join :: proc(t: ^Thread) { return } + t.flags += {.Joined} + + if .Started not_in t.flags { + _start(t) + } + win32.WaitForSingleObject(t.win32_thread, win32.INFINITE) win32.CloseHandle(t.win32_thread) t.win32_thread = win32.INVALID_HANDLE - - t.flags += {.Joined} } _join_multiple :: proc(threads: ..^Thread) { From 1935811b2c6118e633dd5c4cef2e9e70f7b962e5 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 12 May 2024 19:51:19 -0400 Subject: [PATCH 02/34] Suggest `-all-packages` if testing empty directory --- src/parser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/parser.cpp b/src/parser.cpp index ee3c56daf..ad962f53e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5508,11 +5508,15 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const } } if (files_with_ext == 0 || files_to_reserve == 1) { + ERROR_BLOCK(); if (files_with_ext != 0) { syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path)); } else { syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path)); } + if (build_context.command_kind == Command_test) { + error_line("\tSuggestion: Make an .odin file that imports packages to test and use the `-all-packages` flag."); + } return nullptr; } From 971201182abab765bbb99a671eb331b6bedf4bec Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 12 May 2024 19:52:36 -0400 Subject: [PATCH 03/34] Fix `read_directory()` skipping directories on UNIX-likes --- src/path.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/path.cpp b/src/path.cpp index b07f20870..93f6f5000 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -400,16 +400,13 @@ gb_internal ReadDirectoryError read_directory(String path, Array *fi) continue; } - if (S_ISDIR(dir_stat.st_mode)) { - continue; - } - i64 size = dir_stat.st_size; FileInfo info = {}; info.name = copy_string(a, name); info.fullpath = path_to_full_path(a, filepath); info.size = size; + info.is_dir = S_ISDIR(dir_stat.st_mode); array_add(fi, info); } From facae34354dad8a00caddfb7d224774d9d768414 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 11:53:59 +0100 Subject: [PATCH 04/34] Fix #3584 --- src/check_expr.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 08f488642..4618bf969 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1441,6 +1441,13 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T // return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly); } return false; + + case Type_BitField: + if (source->kind == Type_BitField) { + return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type); + } + return false; + case Type_Tuple: GB_PANIC("This should never happen"); return false; From 3fb0d52a745ba7215201234e18995e7489cc1f24 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 11:57:04 +0100 Subject: [PATCH 05/34] Fix #3585 --- src/llvm_backend_expr.cpp | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index ad02ff7dd..4c5ff76bc 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -5194,6 +5194,54 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr)); return lb_addr(ptr); case_end; + + + case_ast_node(be, OrBranchExpr, expr); + lbBlock *block = nullptr; + + if (be->label != nullptr) { + lbBranchBlocks bb = lb_lookup_branch_blocks(p, be->label); + switch (be->token.kind) { + case Token_or_break: block = bb.break_; break; + case Token_or_continue: block = bb.continue_; break; + } + } else { + for (lbTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) { + if (t->is_block) { + continue; + } + + switch (be->token.kind) { + case Token_or_break: block = t->break_; break; + case Token_or_continue: block = t->continue_; break; + } + } + } + + GB_ASSERT(block != nullptr); + TypeAndValue tv = expr->tav; + + lbValue lhs = {}; + lbValue rhs = {}; + lb_emit_try_lhs_rhs(p, be->expr, tv, &lhs, &rhs); + Type *type = default_type(tv.type); + if (lhs.value) { + lhs = lb_emit_conv(p, lhs, type); + } else if (type != nullptr && type != t_invalid) { + lhs = lb_const_nil(p->module, type); + } + + lbBlock *then = lb_create_block(p, "or_branch.then"); + lbBlock *else_ = lb_create_block(p, "or_branch.else"); + + lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_); + lb_start_block(p, else_); + lb_emit_defer_stmts(p, lbDeferExit_Branch, block); + lb_emit_jump(p, block); + lb_start_block(p, then); + + return lb_addr(lb_address_from_load_or_generate_local(p, lhs)); + case_end; } TokenPos token_pos = ast_token(expr).pos; From 7905f0533f3fbbffec262a97b083d6aae450e46b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:01:16 +0100 Subject: [PATCH 06/34] Fix #3582 by disallowing it --- src/check_type.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 4df0c5d19..8d3c28f06 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -191,9 +191,10 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slicenames.count > 0) { Type *first_type = fields_array[fields_array.count-1]->type; + bool soa_ptr = is_type_soa_pointer(first_type); Type *t = base_type(type_deref(first_type)); - if (!does_field_type_allow_using(t) && + if ((soa_ptr || !does_field_type_allow_using(t)) && p->names.count >= 1 && p->names[0]->kind == Ast_Ident) { Token name_token = p->names[0]->Ident.token; From f8d235b6f572f4dd0ce3b0abd6eafbac55edc71e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:02:02 +0100 Subject: [PATCH 07/34] Fix #3581 due to typo --- src/llvm_backend_general.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 9caddfb51..3c1a7ee7f 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -1180,7 +1180,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { Type *t = addr.bitfield.type; if (do_mask) { - GB_ASSERT(addr.bitfield.bit_size < 8*type_size_of(ct)); + GB_ASSERT(addr.bitfield.bit_size <= 8*type_size_of(ct)); lbValue mask = lb_const_int(p->module, t, (1ull< Date: Mon, 13 May 2024 12:24:50 +0100 Subject: [PATCH 08/34] Minor clean up of `is_terminating` code This does not fix all known issues with it --- src/check_stmt.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c018077f9..3b836aa3c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -254,6 +254,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) } +String label_from_node(Ast *node, String default_label) { + if (node != nullptr && node->kind == Ast_Ident) { + return node->Ident.token.string; + } + return default_label; +} + // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly @@ -264,7 +271,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label); + return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); case_end; case_ast_node(es, ExprStmt, node); @@ -285,9 +292,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); + String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, label) && - check_is_terminating(is->else_stmt, label)) { + if (check_is_terminating(is->body, new_label) && + check_is_terminating(is->else_stmt, new_label)) { return true; } } @@ -320,7 +328,8 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + String new_label = label_from_node(fs->label, label); + if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { return true; } case_end; @@ -335,14 +344,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } @@ -351,14 +361,15 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; + String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, label) || - check_has_break_list(cc->stmts, label, true)) { + if (!check_is_terminating_list(cc->stmts, new_label) || + check_has_break_list(cc->stmts, new_label, true)) { return false; } } From d1217340f57168311cf26311f4d75a94fb26323b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:26:01 +0100 Subject: [PATCH 09/34] Fix #3573 --- src/check_expr.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4618bf969..1fc6eeb52 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -1794,6 +1794,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) { gb_string_free(str); return false; } + if (o->mode == Addressing_Type) { + gbString str = type_to_string(o->type); + error(o->expr, "Expected an expression for operator '%.*s', got type '%s'", LIT(op.string), str); + gb_string_free(str); + return false; + } + Type *type = base_type(core_array_type(o->type)); gbString str = nullptr; switch (op.kind) { From 34c8739b69e0778fe19f0593fecb7e7080363862 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:42:06 +0100 Subject: [PATCH 10/34] Fix #3578 --- src/check_stmt.cpp | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3b836aa3c..ad91838b9 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -253,15 +253,17 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return false; } - -String label_from_node(Ast *node, String default_label) { - if (node != nullptr && node->kind == Ast_Ident) { +String label_string(Ast *node) { + GB_ASSERT(node != nullptr); + if (node->kind == Ast_Ident) { return node->Ident.token.string; + } else if (node->kind == Ast_Label) { + return label_string(node->Label.name); } - return default_label; + GB_ASSERT("INVALID LABEL"); + return {}; } - // NOTE(bill): The last expression has to be a 'return' statement // TODO(bill): This is a mild hack and should be probably handled properly gb_internal bool check_is_terminating(Ast *node, String const &label) { @@ -271,7 +273,12 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(bs, BlockStmt, node); - return check_is_terminating_list(bs->stmts, label_from_node(bs->label, label)); + if (check_is_terminating_list(bs->stmts, label)) { + if (bs->label != nullptr) { + return check_is_terminating_list(bs->stmts, label_string(bs->label)); + } + return true; + } case_end; case_ast_node(es, ExprStmt, node); @@ -292,10 +299,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(is, IfStmt, node); - String new_label = label_from_node(is->label, label); if (is->else_stmt != nullptr) { - if (check_is_terminating(is->body, new_label) && - check_is_terminating(is->else_stmt, new_label)) { + if (check_is_terminating(is->body, label) && + check_is_terminating(is->else_stmt, label)) { return true; } } @@ -328,8 +334,10 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_end; case_ast_node(fs, ForStmt, node); - String new_label = label_from_node(fs->label, label); - if (fs->cond == nullptr && !check_has_break(fs->body, new_label, true)) { + if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) { + if (fs->label) { + return !check_has_break(fs->body, label_string(fs->label), false); + } return true; } case_end; @@ -344,15 +352,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, SwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } @@ -361,15 +368,14 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) { case_ast_node(ss, TypeSwitchStmt, node); bool has_default = false; - String new_label = label_from_node(ss->label, label); for_array(i, ss->body->BlockStmt.stmts) { Ast *clause = ss->body->BlockStmt.stmts[i]; ast_node(cc, CaseClause, clause); if (cc->list.count == 0) { has_default = true; } - if (!check_is_terminating_list(cc->stmts, new_label) || - check_has_break_list(cc->stmts, new_label, true)) { + if (!check_is_terminating_list(cc->stmts, label) || + check_has_break_list(cc->stmts, label, true)) { return false; } } From 8b4a8e4d806a9d8328c3aedf8175c681b42cb324 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:49:12 +0100 Subject: [PATCH 11/34] Fix #3569 --- src/check_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ad91838b9..719a0da15 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -751,7 +751,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, for (auto const &entry : scope->elements) { String name = entry.key; Entity *decl = entry.value; - if (!is_entity_exported(decl)) continue; + if (!is_entity_exported(decl, true)) continue; Entity *found = scope_insert_with_name(ctx->scope, name, decl); if (found != nullptr) { From 8d687a959d29c0ef0c631103b9775091e37c7e81 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:52:28 +0100 Subject: [PATCH 12/34] Fix #3516 --- src/check_expr.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1fc6eeb52..6a293a97e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -10263,6 +10263,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, case Type_Struct: if (is_type_soa_struct(t)) { valid = true; + if (t->Struct.soa_kind == StructSoa_Fixed) { + max_count = t->Struct.soa_count; + if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) { + gbString str = expr_to_string(node); + error(node, "Cannot slice #soa array '%s', value is not addressable", str); + gb_string_free(str); + o->mode = Addressing_Invalid; + o->expr = node; + return kind; + } + } o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); } break; From 6dc0ee3877d9a192a1e5483d3b80328b53b65bb7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:54:01 +0100 Subject: [PATCH 13/34] Fix #3577 --- core/net/common.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/net/common.odin b/core/net/common.odin index 2a6f44602..db969eab8 100644 --- a/core/net/common.odin +++ b/core/net/common.odin @@ -137,8 +137,8 @@ IP4_Address :: distinct [4]u8 IP6_Address :: distinct [8]u16be Address :: union {IP4_Address, IP6_Address} -IP4_Loopback := IP4_Address{127, 0, 0, 1} -IP6_Loopback := IP6_Address{0, 0, 0, 0, 0, 0, 0, 1} +IP4_Loopback :: IP4_Address{127, 0, 0, 1} +IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1} IP4_Any := IP4_Address{} IP6_Any := IP6_Address{} From 4bdc8548bd7ec557797985c548bd846c51ffc926 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:55:49 +0100 Subject: [PATCH 14/34] Fix #3554 --- src/llvm_backend_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 4c5ff76bc..899b74e56 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4141,7 +4141,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { if (se->high == nullptr) { lbValue offset = base; LLVMValueRef indices[1] = {low.value}; - offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, ""); + offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(offset.type)->MultiPointer.elem), offset.value, indices, 1, ""); lb_addr_store(p, res, offset); } else { low = lb_emit_conv(p, low, t_int); @@ -4150,7 +4150,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) { lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high); LLVMValueRef indices[1] = {low.value}; - LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, ""); + LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(base.type)->MultiPointer.elem), base.value, indices, 1, ""); LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, ""); LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value; From 54ebfa6179da22688239e218959320866e87a4f9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 12:58:18 +0100 Subject: [PATCH 15/34] Fix hanging on `thread.join` for windows where the thread had not been `start`ed --- core/thread/thread_windows.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index 4e5e8c07a..314ef5842 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -100,7 +100,8 @@ _join :: proc(t: ^Thread) { t.flags += {.Joined} if .Started not_in t.flags { - _start(t) + t.flags += {.Started} + win32.ResumeThread(t.win32_thread) } win32.WaitForSingleObject(t.win32_thread, win32.INFINITE) From 8808e5584a4a69e401010cbfe8800e0b2a430941 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 13:26:22 +0100 Subject: [PATCH 16/34] If only warnings exist on `print_all_errors`, next time it is called, clear the error list. This is mostly only syntax errors too --- src/error.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/error.cpp b/src/error.cpp index 688d1b34a..f5123b969 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -672,7 +672,20 @@ gb_internal int error_value_cmp(void const *a, void const *b) { return token_pos_cmp(x->pos, y->pos); } +gb_internal bool errors_already_printed = false; + gb_internal void print_all_errors(void) { + if (errors_already_printed) { + if (global_error_collector.warning_count.load() == global_error_collector.error_values.count) { + for (ErrorValue &ev : global_error_collector.error_values) { + array_free(&ev.msg); + } + array_clear(&global_error_collector.error_values); + errors_already_printed = false; + } + return; + } + auto const &escape_char = [](gbString res, u8 c) -> gbString { switch (c) { case '\n': res = gb_string_append_length(res, "\\n", 2); break; @@ -827,4 +840,6 @@ gb_internal void print_all_errors(void) { } gbFile *f = gb_file_get_standard(gbFileStandard_Error); gb_file_write(f, res, gb_string_length(res)); + + errors_already_printed = true; } \ No newline at end of file From 215ef3d985724fa0ef7092fa8f672cc502db87be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 13:26:47 +0100 Subject: [PATCH 17/34] Make `core:runtime` etc a warning, and an error with `-vet` --- src/build_settings.cpp | 5 ++++- src/parser.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c6ef33af2..ec7d03a84 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -730,10 +730,11 @@ enum VetFlags : u64 { VetFlag_Semicolon = 1u<<4, VetFlag_UnusedVariables = 1u<<5, VetFlag_UnusedImports = 1u<<6, + VetFlag_Deprecated = 1u<<7, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, - VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, + VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated, VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam, }; @@ -755,6 +756,8 @@ u64 get_vet_flag_from_name(String const &name) { return VetFlag_Style; } else if (name == "semicolon") { return VetFlag_Semicolon; + } else if (name == "deprecated") { + return VetFlag_Deprecated; } return VetFlag_NONE; } diff --git a/src/parser.cpp b/src/parser.cpp index ad962f53e..66e175765 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -11,6 +11,9 @@ gb_internal bool ast_file_vet_style(AstFile *f) { return (ast_file_vet_flags(f) & VetFlag_Style) != 0; } +gb_internal bool ast_file_vet_deprecated(AstFile *f) { + return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0; +} gb_internal bool file_allow_newline(AstFile *f) { bool is_strict = build_context.strict_style || ast_file_vet_style(f); @@ -5694,8 +5697,26 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node if (collection_name.len > 0) { // NOTE(bill): `base:runtime` == `core:runtime` - if (collection_name == "core" && string_starts_with(file_str, str_lit("runtime"))) { - collection_name = str_lit("base"); + if (collection_name == "core") { + bool replace_with_base = false; + if (string_starts_with(file_str, str_lit("runtime"))) { + replace_with_base = true; + } else if (string_starts_with(file_str, str_lit("intrinsics"))) { + replace_with_base = true; + } if (string_starts_with(file_str, str_lit("builtin"))) { + replace_with_base = true; + } + + if (replace_with_base) { + collection_name = str_lit("base"); + } + if (replace_with_base) { + if (ast_file_vet_deprecated(node->file())) { + syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + } else { + syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str)); + } + } } if (collection_name == "system") { From 1b593fc1cabb5dba048ef78439b2f6c42ce00be4 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 13:27:44 +0100 Subject: [PATCH 18/34] Correct `core:intrinsics` to `base:intrinsics` --- core/simd/x86/abm.odin | 2 +- core/simd/x86/cmpxchg16b.odin | 2 +- core/simd/x86/sse.odin | 2 +- core/simd/x86/sse2.odin | 2 +- core/simd/x86/sse3.odin | 2 +- core/simd/x86/ssse3.odin | 2 +- examples/demo/demo.odin | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/simd/x86/abm.odin b/core/simd/x86/abm.odin index 79b806242..9018a835a 100644 --- a/core/simd/x86/abm.odin +++ b/core/simd/x86/abm.odin @@ -1,7 +1,7 @@ //+build i386, amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" @(require_results, enable_target_feature="lzcnt") _lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 { diff --git a/core/simd/x86/cmpxchg16b.odin b/core/simd/x86/cmpxchg16b.odin index d575dd9df..1307a9cf2 100644 --- a/core/simd/x86/cmpxchg16b.odin +++ b/core/simd/x86/cmpxchg16b.odin @@ -1,7 +1,7 @@ //+build amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" cmpxchg16b :: #force_inline proc "c" (dst: ^u128, old, new: u128, $success, $failure: intrinsics.Atomic_Memory_Order) -> (val: u128) { return intrinsics.atomic_compare_exchange_strong_explicit(dst, old, new, success, failure) diff --git a/core/simd/x86/sse.odin b/core/simd/x86/sse.odin index 903a43dfd..4dac50234 100644 --- a/core/simd/x86/sse.odin +++ b/core/simd/x86/sse.odin @@ -1,7 +1,7 @@ //+build i386, amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" import "core:simd" // _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w) diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin index a597122f1..fc2fec300 100644 --- a/core/simd/x86/sse2.odin +++ b/core/simd/x86/sse2.odin @@ -1,7 +1,7 @@ //+build i386, amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" import "core:simd" @(enable_target_feature="sse2") diff --git a/core/simd/x86/sse3.odin b/core/simd/x86/sse3.odin index ca19c3954..a905a7726 100644 --- a/core/simd/x86/sse3.odin +++ b/core/simd/x86/sse3.odin @@ -1,7 +1,7 @@ //+build i386, amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" import "core:simd" @(require_results, enable_target_feature="sse3") diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin index 0264a1c93..2026c7f53 100644 --- a/core/simd/x86/ssse3.odin +++ b/core/simd/x86/ssse3.odin @@ -1,7 +1,7 @@ //+build i386, amd64 package simd_x86 -import "core:intrinsics" +import "base:intrinsics" import "core:simd" _ :: simd diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 39345e39c..b3c627808 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -8,7 +8,7 @@ import "core:thread" import "core:time" import "core:reflect" import "base:runtime" -import "core:intrinsics" +import "base:intrinsics" import "core:math/big" /* From 8fa20fb875849774942117af3a23cb676a3fd8d7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 14:44:53 +0100 Subject: [PATCH 19/34] Extra check for `field` being `nullptr` --- src/check_type.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 8d3c28f06..c209a8e09 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -19,10 +19,12 @@ gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstF } } else { Token tok = make_token_ident(name); - if (field->names.count > 0) { - tok.pos = ast_token(field->names[0]).pos; - } else { - tok.pos = ast_token(field->type).pos; + if (field) { + if (field->names.count > 0) { + tok.pos = ast_token(field->names[0]).pos; + } else { + tok.pos = ast_token(field->type).pos; + } } Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx); add_entity(ctx, ctx->scope, nullptr, f); From 0cf9dcd31441a56856a60e4f73db4b005a1407ee Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 13 May 2024 18:15:29 +0100 Subject: [PATCH 20/34] Make `..` ranges a complete error rather than a warning now. This should have been an error years ago. --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index 66e175765..e296e6935 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1572,7 +1572,7 @@ gb_internal Token expect_operator(AstFile *f) { LIT(p)); } if (prev.kind == Token_Ellipsis) { - syntax_warning(prev, "'..' for ranges has now been deprecated, prefer '..='"); + syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } From 20f8f9012d71fa9d95ff65a0badf844c9cbe4ddb Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:11:57 +0100 Subject: [PATCH 21/34] Attempt at fixing #3588 --- src/check_stmt.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 719a0da15..ee55ff0d7 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -776,6 +776,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, bool is_ptr = is_type_pointer(e->type); Type *t = base_type(type_deref(e->type)); if (t->kind == Type_Struct) { + wait_signal_until_available(&t->Struct.fields_wait_signal); + Scope *found = t->Struct.scope; GB_ASSERT(found != nullptr); for (auto const &entry : found->elements) { From 7734b12f9a494142c59487265e840bc1c8e605d3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:16:32 +0100 Subject: [PATCH 22/34] Fix #3587 --- src/check_stmt.cpp | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index ee55ff0d7..875503874 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -169,9 +169,16 @@ gb_internal bool check_has_break_list(Slice const &stmts, String const &l return false; } +gb_internal bool check_has_break_expr(Ast * expr, String const &label) { + if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + return true; + } + return false; +} + gb_internal bool check_has_break_expr_list(Slice const &exprs, String const &label) { for (Ast *expr : exprs) { - if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) { + if (check_has_break_expr(expr, label)) { return true; } } @@ -196,6 +203,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->BlockStmt.stmts, label, implicit); case Ast_IfStmt: + if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) { + return true; + } + if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) { + return true; + } + if (check_has_break(stmt->IfStmt.body, label, implicit) || (stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) { return true; @@ -206,6 +220,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) return check_has_break_list(stmt->CaseClause.stmts, label, implicit); case Ast_SwitchStmt: + if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) { + return true; + } if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) { return true; } @@ -218,6 +235,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit) break; case Ast_ForStmt: + if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) { + return true; + } + if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) { + return true; + } + if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) { + return true; + } + if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) { return true; } From e05315831fe0eda44343a17c38df03cca2be0d65 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 00:18:38 +0100 Subject: [PATCH 23/34] Fix #3586 --- src/llvm_backend_stmt.cpp | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 851433415..b18db4e45 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2157,6 +2157,16 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) { lb_open_scope(p, is->scope); // Scope #1 defer (lb_close_scope(p, lbDeferExit_Default, nullptr)); + lbBlock *then = lb_create_block(p, "if.then"); + lbBlock *done = lb_create_block(p, "if.done"); + lbBlock *else_ = done; + if (is->else_stmt != nullptr) { + else_ = lb_create_block(p, "if.else"); + } + if (is->label != nullptr) { + lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr); + tl->is_block = true; + } if (is->init != nullptr) { lbBlock *init = lb_create_block(p, "if.init"); lb_emit_jump(p, init); @@ -2164,22 +2174,12 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) { lb_build_stmt(p, is->init); } - lbBlock *then = lb_create_block(p, "if.then"); - lbBlock *done = lb_create_block(p, "if.done"); - lbBlock *else_ = done; - if (is->else_stmt != nullptr) { - else_ = lb_create_block(p, "if.else"); - } lbValue cond = lb_build_cond(p, is->cond, then, else_); // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()` // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()` // will target the correct (& only) branch statement - if (is->label != nullptr) { - lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr); - tl->is_block = true; - } if (cond.value && LLVMIsConstant(cond.value)) { // NOTE(bill): Do a compile time short circuit for when the condition is constantly known. @@ -2244,15 +2244,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) { if (p->debug_info != nullptr) { LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node)); } - - if (fs->init != nullptr) { - #if 1 - lbBlock *init = lb_create_block(p, "for.init"); - lb_emit_jump(p, init); - lb_start_block(p, init); - #endif - lb_build_stmt(p, fs->init); - } lbBlock *body = lb_create_block(p, "for.body"); lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later lbBlock *loop = body; @@ -2264,6 +2255,17 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) { post = lb_create_block(p, "for.post"); } + lb_push_target_list(p, fs->label, done, post, nullptr); + + if (fs->init != nullptr) { + #if 1 + lbBlock *init = lb_create_block(p, "for.init"); + lb_emit_jump(p, init); + lb_start_block(p, init); + #endif + lb_build_stmt(p, fs->init); + } + lb_emit_jump(p, loop); lb_start_block(p, loop); @@ -2276,7 +2278,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) { lb_start_block(p, body); } - lb_push_target_list(p, fs->label, done, post, nullptr); lb_build_stmt(p, fs->body); From ecd7846ec31f4e582a4bd0bb6fc0d8ce9ea258bf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 17:10:53 +0100 Subject: [PATCH 24/34] Clean up allocator stuff into `allocators.odin` --- core/os/os2/allocators.odin | 46 +++++++++++++++++++++++++++ core/os/os2/env_windows.odin | 8 ++--- core/os/os2/file.odin | 6 ++-- core/os/os2/file_linux.odin | 8 ++--- core/os/os2/file_windows.odin | 51 +++--------------------------- core/os/os2/path_windows.odin | 14 ++++---- core/os/os2/stat_windows.odin | 14 ++++---- core/os/os2/temp_file_windows.odin | 4 +-- 8 files changed, 75 insertions(+), 76 deletions(-) create mode 100644 core/os/os2/allocators.odin diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin new file mode 100644 index 000000000..c044e4522 --- /dev/null +++ b/core/os/os2/allocators.odin @@ -0,0 +1,46 @@ +//+private +package os2 + +import "base:runtime" + +file_allocator :: proc() -> runtime.Allocator { + return heap_allocator() +} + +temp_allocator_proc :: runtime.arena_allocator_proc + +@(private="file", thread_local) +global_default_temp_allocator_arena: runtime.Arena + +temp_allocator :: proc() -> runtime.Allocator { + return runtime.Allocator{ + procedure = temp_allocator_proc, + data = &global_default_temp_allocator_arena, + } +} + +@(require_results) +temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) { + temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc) + return +} + +temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_location) { + runtime.arena_temp_end(temp, loc) +} + +@(fini, private) +temp_allocator_fini :: proc() { + runtime.arena_destroy(&global_default_temp_allocator_arena) + global_default_temp_allocator_arena = {} +} + +@(deferred_out=temp_allocator_temp_end) +TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { + if ignore { + return {}, loc + } else { + return temp_allocator_temp_begin(loc), loc + } +} + diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index 774af9e8f..39694b821 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -19,9 +19,9 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string return "", true } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - b := make([]u16, n+1, _temp_allocator()) + b := make([]u16, n+1, temp_allocator()) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) if n == 0 { @@ -50,8 +50,8 @@ _unset_env :: proc(key: string) -> bool { } _clear_env :: proc() { - _TEMP_ALLOCATOR_GUARD() - envs := environ(_temp_allocator()) + TEMP_ALLOCATOR_GUARD() + envs := environ(temp_allocator()) for env in envs { for j in 1.. Error { return p, false, nil } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - dir, err := stat(path, _temp_allocator()) + dir, err := stat(path, temp_allocator()) if err == nil { if dir.is_directory { return nil @@ -54,14 +54,14 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { if j > 1 { new_path, allocated := fix_root_directory(path[:j-1]) or_return defer if allocated { - delete(new_path, _file_allocator()) + delete(new_path, file_allocator()) } mkdir_all(new_path, perm) or_return } err = mkdir(path, perm) if err != nil { - dir1, err1 := lstat(path, _temp_allocator()) + dir1, err1 := lstat(path, temp_allocator()) if err1 == nil && dir1.is_directory { return nil } @@ -127,10 +127,10 @@ _fix_long_path_internal :: proc(path: string) -> string { return path } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() PREFIX :: `\\?` - path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator()) + path_buf := make([]byte, len(PREFIX)+len(path)+1, temp_allocator()) copy(path_buf, PREFIX) n := len(path) r, w := 0, len(PREFIX) diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 154a5bbe3..03ad2052f 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -46,15 +46,15 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path if name == "" { name = "." } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - p := win32.utf8_to_utf16(name, _temp_allocator()) + p := win32.utf8_to_utf16(name, temp_allocator()) n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) if n == 0 { return "", _get_platform_error() } - buf := make([]u16, n+1, _temp_allocator()) + buf := make([]u16, n+1, temp_allocator()) n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) if n == 0 { return "", _get_platform_error() @@ -131,8 +131,8 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin if n == 0 { return "", _get_platform_error() } - _TEMP_ALLOCATOR_GUARD() - buf := make([]u16, max(n, 260)+1, _temp_allocator()) + TEMP_ALLOCATOR_GUARD() + buf := make([]u16, max(n, 260)+1, temp_allocator()) n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) return _cleanpath_from_buf(buf[:n], allocator) } @@ -147,8 +147,8 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { if n == 0 { return nil, _get_platform_error() } - _TEMP_ALLOCATOR_GUARD() - buf := make([]u16, max(n, 260)+1, _temp_allocator()) + TEMP_ALLOCATOR_GUARD() + buf := make([]u16, max(n, 260)+1, temp_allocator()) n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) return _cleanpath_strip_prefix(buf[:n]), nil } diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index c42da84f5..09b5675f2 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -17,9 +17,9 @@ _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Er if n == 0 { return "", nil } - _TEMP_ALLOCATOR_GUARD() + TEMP_ALLOCATOR_GUARD() - b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator()) + b := make([]u16, max(win32.MAX_PATH, n), temp_allocator()) n = win32.GetTempPathW(u32(len(b)), raw_data(b)) if n == 3 && b[1] == ':' && b[2] == '\\' { From 361be301fa1aec086de457ae01784c66304df4a9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 17:24:37 +0100 Subject: [PATCH 25/34] Use internal `temp_allocator()` --- core/os/os2/file_linux.odin | 59 ++++++++++++++++++++++------------- core/os/os2/file_windows.odin | 18 +++++++++-- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 13b5f2966..b1a6d84f5 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -36,8 +36,8 @@ _File :: struct { } _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) // Just default to using O_NOCTTY because needing to open a controlling // terminal would be incredibly rare. This has no effect on files while @@ -49,12 +49,12 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error case O_RDWR: flags_i = _O_RDWR } - flags_i |= (_O_APPEND * int(.Append in flags)) - flags_i |= (_O_CREAT * int(.Create in flags)) - flags_i |= (_O_EXCL * int(.Excl in flags)) - flags_i |= (_O_SYNC * int(.Sync in flags)) - flags_i |= (_O_TRUNC * int(.Trunc in flags)) - flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags)) + if .Append in flags { flags_i |= _O_APPEND } + if .Create in flags { flags_i |= _O_CREAT } + if .Excl in flags { flags_i |= _O_EXCL } + if .Sync in flags { flags_i |= _O_SYNC } + if .Trunc in flags { flags_i |= _O_TRUNC } + if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC } fd := unix.sys_open(name_cstr, flags_i, uint(perm)) if fd < 0 { @@ -87,8 +87,12 @@ _destroy :: proc(f: ^File) -> Error { _close :: proc(f: ^File) -> Error { - res := unix.sys_close(f.impl.fd) - return _ok_or_error(res) + if f != nil { + res := unix.sys_close(f.impl.fd) + _destroy(f) + return _ok_or_error(res) + } + return nil } _fd :: proc(f: ^File) -> uintptr { @@ -190,7 +194,8 @@ _truncate :: proc(f: ^File, size: i64) -> Error { } _remove :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) fd := unix.sys_open(name_cstr, int(File_Flags.Read)) if fd < 0 { @@ -244,17 +249,20 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st } _read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_unlink(name_cstr)) } _chdir :: proc(name: string) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chdir(name_cstr)) } @@ -263,7 +271,8 @@ _fchdir :: proc(f: ^File) -> Error { } _chmod :: proc(name: string, mode: File_Mode) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode))) } @@ -273,13 +282,15 @@ _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { // NOTE: will throw error without super user priviledges _chown :: proc(name: string, uid, gid: int) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_chown(name_cstr, uid, gid)) } // NOTE: will throw error without super user priviledges _lchown :: proc(name: string, uid, gid: int) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } @@ -289,7 +300,8 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error { } _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -306,12 +318,14 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { } _exists :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) return unix.sys_access(name_cstr, F_OK) == 0 } _is_file :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -330,7 +344,8 @@ _is_file_fd :: proc(fd: int) -> bool { } _is_dir :: proc(name: string) -> bool { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + name_cstr := _temp_name_to_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -354,7 +369,7 @@ _is_dir_fd :: proc(fd: int) -> bool { // buffer. Therefor, any large paths will use context.allocator. @(private="file") _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { - return strings.clone_to_cstring(name, context.temp_allocator) + return strings.clone_to_cstring(name, temp_allocator()) } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 4c0f62c7a..1984c9baf 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -36,6 +36,20 @@ _File :: struct { p_mutex: sync.Mutex, // pread pwrite calls } +@(init) +init_std_files :: proc() { + stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "") + stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "") + stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "") +} +@(fini) +fini_std_files :: proc() { + close(stdin) + close(stdout) + close(stderr) +} + + _handle :: proc(f: ^File) -> win32.HANDLE { return win32.HANDLE(_fd(f)) } @@ -755,14 +769,12 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, ferr = _flush(f) err = error_to_io_error(ferr) return - case .Close: + 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, .Query}) - case .Destroy: - return 0, .Empty } return 0, .Empty } From 91b7cdaad207511260c8cead669c276b255e7c78 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:11:50 +0100 Subject: [PATCH 26/34] Mock out `temp_file.odin` stuff --- core/os/os2/errors.odin | 3 + core/os/os2/file_linux.odin | 1 - core/os/os2/file_util.odin | 4 +- core/os/os2/file_windows.odin | 6 +- core/os/os2/path.odin | 5 +- core/os/os2/path_linux.odin | 5 +- core/os/os2/path_windows.odin | 5 +- core/os/os2/temp_file.odin | 181 ++++++++++++++++++++++++++++- core/os/os2/temp_file_linux.odin | 10 -- core/os/os2/temp_file_windows.odin | 8 -- 10 files changed, 194 insertions(+), 34 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index d76b2d549..2c570170d 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -23,6 +23,8 @@ General_Error :: enum u32 { Invalid_Dir, Invalid_Path, + Pattern_Has_Separator, + Unsupported, } @@ -63,6 +65,7 @@ error_string :: proc(ferr: Error) -> string { case .Invalid_Dir: return "invalid directory" case .Invalid_Path: return "invalid path" case .Unsupported: return "unsupported" + case .Pattern_Has_Separator: return "pattern has separator" } case io.Error: switch e { diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index b1a6d84f5..3f66bbd09 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -377,7 +377,6 @@ _temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { _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) diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 0708f708e..9ec75fc91 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -88,11 +88,11 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) - read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) { size: int has_size := true - if size64, err := file_size(f); err == nil { + if size64, serr := file_size(f); serr == nil { if i64(int(size64)) != size64 { size = int(size64) } - } else if err == .No_Size { + } else if serr == .No_Size { has_size = false } else { return diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 1984c9baf..1e0899992 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -282,10 +282,10 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) { to_read := min(win32.DWORD(length), MAX_RW) ok: win32.BOOL if f.impl.kind == .Console { - n, err := read_console(handle, p[total_read:][:to_read]) + n, cerr := read_console(handle, p[total_read:][:to_read]) total_read += n - if err != nil { - return i64(total_read), err + if cerr != nil { + return i64(total_read), cerr } } else { ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil) diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index a3e7a5a96..277da56dd 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -2,8 +2,9 @@ package os2 import "base:runtime" -Path_Separator :: _Path_Separator // OS-Specific -Path_List_Separator :: _Path_List_Separator // OS-Specific +Path_Separator :: _Path_Separator // OS-Specific +Path_Separator_String :: _Path_Separator_String // OS-Specific +Path_List_Separator :: _Path_List_Separator // OS-Specific is_path_separator :: proc(c: byte) -> bool { return _is_path_separator(c) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 93de749b8..fde4ba5b1 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -6,8 +6,9 @@ import "core:strconv" import "base:runtime" import "core:sys/unix" -_Path_Separator :: '/' -_Path_List_Separator :: ':' +_Path_Separator :: '/' +_Path_Separator_String :: "/" +_Path_List_Separator :: ':' _S_IFMT :: 0o170000 // Type of file mask _S_IFIFO :: 0o010000 // Named pipe (fifo) diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index f3a45768d..064356cf1 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -5,8 +5,9 @@ import win32 "core:sys/windows" import "base:runtime" import "core:strings" -_Path_Separator :: '\\' -_Path_List_Separator :: ';' +_Path_Separator :: '\\' +_Path_Separator_String :: "\\" +_Path_List_Separator :: ';' _is_path_separator :: proc(c: byte) -> bool { return c == '\\' || c == '/' diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index f12c2800e..d415eb7f9 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,17 +1,190 @@ package os2 +import "base:intrinsics" import "base:runtime" -create_temp_file :: proc(dir, pattern: string) -> (^File, Error) { - return _create_temp(dir, pattern) +@(private="file") +MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? + +// Creates a new temperatory file in the directory `dir`. +// +// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`. +// The filename is generated by taking a pattern, and adding a randomized string to the end. +// If the pattern includes an "*", the randm string replaces the last "*". +// If `dir` is an empty tring, `temp_directory()` will be used. +// +// The caller must `close` the file once finished with. +create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { + TEMP_ALLOCATOR_GUARD() + dir := dir if dir != "" else temp_directory(temp_allocator()) or_return + prefix, suffix := _prefix_and_suffix(pattern) or_return + prefix = temp_join_path(dir, prefix) + + rand_buf: [32]byte + name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) + + attempts := 0 + for { + name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) + f, err = open(name, {.Read, .Write, .Create, .Excl}, File_Mode(0o666)) + if err == .Exist { + close(f) + attempts += 1 + if attempts < MAX_ATTEMPTS { + continue + } + return nil, err + } + return f, err + } } mkdir_temp :: make_directory_temp -make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - return _mkdir_temp(dir, pattern, allocator) +// Creates a new temporary directory in the directory `dir`, and returns the path of the new directory. +// +// The directory name is generated by taking a pattern, and adding a randomized string to the end. +// If the pattern includes an "*", the randm string replaces the last "*". +// If `dir` is an empty tring, `temp_directory()` will be used. +make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { + TEMP_ALLOCATOR_GUARD() + dir := dir if dir != "" else temp_directory(temp_allocator()) or_return + prefix, suffix := _prefix_and_suffix(pattern) or_return + prefix = temp_join_path(dir, prefix) + + rand_buf: [32]byte + name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) + + attempts := 0 + for { + name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) + err = make_directory(name, 0o700) + if err == nil { + return clone_string(name, allocator), nil + } + if err == .Exist { + attempts += 1 + if attempts < MAX_ATTEMPTS { + continue + } + return "", err + } + if err == .Not_Exist { + if _, serr := stat(dir, temp_allocator()); serr == .Not_Exist { + return "", serr + } + } + return "", err + } + } temp_dir :: temp_directory temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) } + + +// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix +// parts which are split by the last "*" +@(private) +_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { + for i in 0..= 0; i -= 1 { + if pattern[i] == '*' { + prefix, suffix = pattern[:i], pattern[i+1:] + break + } + } + return +} + +@(private) +clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { + buf := make([]byte, len(s), allocator) + copy(buf, s) + return string(buf) +} + + +@(private) +concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { + n := 0 + for s in strings { + (n < len(buf)) or_break + n += copy(buf[n:], s) + } + n = min(len(buf), n) + return string(buf[:n]) +} + + + +@(private) +temp_join_path :: proc(dir, name: string) -> string { + concat :: proc(strings: ..string) -> string { + n := 0 + for s in strings { + n += len(s) + } + buf := make([]byte, n) + n = 0 + for s in strings { + n += copy(buf[n:], s) + } + return string(buf) + } + + if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) { + return concat(dir, name) + } + + return concat(dir, Path_Separator_String, name) +} + + +@(private="file") +random_string_seed: [2]u64 + +@(init, private="file") +init_random_string_seed :: proc() { + seed := u64(intrinsics.read_cycle_counter()) + s := &random_string_seed + s[0] = 0 + s[1] = (seed << 1) | 1 + _ = next_random(s) + s[1] += seed + _ = next_random(s) +} + +@(private="file") +next_random :: proc(r: ^[2]u64) -> u64 { + old_state := r[0] + r[0] = old_state * 6364136223846793005 + (r[1]|1) + xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 + rot := (old_state >> 59) + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +} + +@(private="file") +random_string :: proc(buf: []byte) -> string { + @static digits := "0123456789" + + u := next_random(&random_string_seed) + + b :: 10 + i := len(buf) + for u >= b { + i -= 1 + buf[i] = digits[u % b] + u /= b + } + i -= 1 + buf[i] = digits[u % b] + return string(buf[i:]) +} diff --git a/core/os/os2/temp_file_linux.odin b/core/os/os2/temp_file_linux.odin index dd7ac5c97..92afcde47 100644 --- a/core/os/os2/temp_file_linux.odin +++ b/core/os/os2/temp_file_linux.odin @@ -4,16 +4,6 @@ package os2 import "base:runtime" -_create_temp :: proc(dir, pattern: string) -> (^File, Error) { - //TODO - return nil, nil -} - -_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - //TODO - return "", nil -} - _temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) { //TODO return "", nil diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 09b5675f2..4c8ab9fb7 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -4,14 +4,6 @@ package os2 import "base:runtime" import win32 "core:sys/windows" -_create_temp :: proc(dir, pattern: string) -> (^File, Error) { - return nil, nil -} - -_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { - return "", nil -} - _temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { n := win32.GetTempPathW(0, nil) if n == 0 { From 48c18227097cf81b04dc45ee918faf37e73517be Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:14:44 +0100 Subject: [PATCH 27/34] Move utility stuff to `internal_util.odin` --- core/os/os2/internal_util.odin | 83 +++++++++++++++++++++++++++++++++ core/os/os2/temp_file.odin | 85 +--------------------------------- 2 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 core/os/os2/internal_util.odin diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin new file mode 100644 index 000000000..69c3c8941 --- /dev/null +++ b/core/os/os2/internal_util.odin @@ -0,0 +1,83 @@ +//+private +package os2 + +import "base:intrinsics" +import "base:runtime" + + +// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix +// parts which are split by the last "*" +_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { + for i in 0..= 0; i -= 1 { + if pattern[i] == '*' { + prefix, suffix = pattern[:i], pattern[i+1:] + break + } + } + return +} + +clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { + buf := make([]byte, len(s), allocator) + copy(buf, s) + return string(buf) +} + + +concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { + n := 0 + for s in strings { + (n < len(buf)) or_break + n += copy(buf[n:], s) + } + n = min(len(buf), n) + return string(buf[:n]) +} + + + +@(private="file") +random_string_seed: [2]u64 + +@(init, private="file") +init_random_string_seed :: proc() { + seed := u64(intrinsics.read_cycle_counter()) + s := &random_string_seed + s[0] = 0 + s[1] = (seed << 1) | 1 + _ = next_random(s) + s[1] += seed + _ = next_random(s) +} + +next_random :: proc(r: ^[2]u64) -> u64 { + old_state := r[0] + r[0] = old_state * 6364136223846793005 + (r[1]|1) + xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 + rot := (old_state >> 59) + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) +} + +random_string :: proc(buf: []byte) -> string { + @static digits := "0123456789" + + u := next_random(&random_string_seed) + + b :: 10 + i := len(buf) + for u >= b { + i -= 1 + buf[i] = digits[u % b] + u /= b + } + i -= 1 + buf[i] = digits[u % b] + return string(buf[i:]) +} diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index d415eb7f9..a1be8f690 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,6 +1,5 @@ package os2 -import "base:intrinsics" import "base:runtime" @(private="file") @@ -84,48 +83,8 @@ temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { } -// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix -// parts which are split by the last "*" -@(private) -_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { - for i in 0..= 0; i -= 1 { - if pattern[i] == '*' { - prefix, suffix = pattern[:i], pattern[i+1:] - break - } - } - return -} -@(private) -clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { - buf := make([]byte, len(s), allocator) - copy(buf, s) - return string(buf) -} - - -@(private) -concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { - n := 0 - for s in strings { - (n < len(buf)) or_break - n += copy(buf[n:], s) - } - n = min(len(buf), n) - return string(buf[:n]) -} - - - -@(private) +@(private="file") temp_join_path :: proc(dir, name: string) -> string { concat :: proc(strings: ..string) -> string { n := 0 @@ -146,45 +105,3 @@ temp_join_path :: proc(dir, name: string) -> string { return concat(dir, Path_Separator_String, name) } - - -@(private="file") -random_string_seed: [2]u64 - -@(init, private="file") -init_random_string_seed :: proc() { - seed := u64(intrinsics.read_cycle_counter()) - s := &random_string_seed - s[0] = 0 - s[1] = (seed << 1) | 1 - _ = next_random(s) - s[1] += seed - _ = next_random(s) -} - -@(private="file") -next_random :: proc(r: ^[2]u64) -> u64 { - old_state := r[0] - r[0] = old_state * 6364136223846793005 + (r[1]|1) - xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081 - rot := (old_state >> 59) - return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) -} - -@(private="file") -random_string :: proc(buf: []byte) -> string { - @static digits := "0123456789" - - u := next_random(&random_string_seed) - - b :: 10 - i := len(buf) - for u >= b { - i -= 1 - buf[i] = digits[u % b] - u /= b - } - i -= 1 - buf[i] = digits[u % b] - return string(buf[i:]) -} From 450b9ceaec2bf30baa4ffe8acdaa2339a84f3590 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:25:15 +0100 Subject: [PATCH 28/34] Add `@(require_results)` everywhere in os2 --- core/os/os2/allocators.odin | 2 ++ core/os/os2/env.odin | 3 ++ core/os/os2/file.odin | 8 +++++ core/os/os2/file_linux.odin | 60 +++++++++++++++------------------- core/os/os2/file_util.odin | 3 ++ core/os/os2/heap.odin | 2 ++ core/os/os2/internal_util.odin | 28 ++++++++++++++-- core/os/os2/path.odin | 2 ++ core/os/os2/path_linux.odin | 3 +- core/os/os2/pipe.odin | 1 + core/os/os2/stat.odin | 4 +++ core/os/os2/temp_file.odin | 5 ++- core/os/os2/user.odin | 3 ++ 13 files changed, 85 insertions(+), 39 deletions(-) diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index c044e4522..40672face 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -3,6 +3,7 @@ package os2 import "base:runtime" +@(require_results) file_allocator :: proc() -> runtime.Allocator { return heap_allocator() } @@ -12,6 +13,7 @@ temp_allocator_proc :: runtime.arena_allocator_proc @(private="file", thread_local) global_default_temp_allocator_arena: runtime.Arena +@(require_results) temp_allocator :: proc() -> runtime.Allocator { return runtime.Allocator{ procedure = temp_allocator_proc, diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin index bed4bebd9..c8d39b270 100644 --- a/core/os/os2/env.odin +++ b/core/os/os2/env.odin @@ -6,6 +6,7 @@ import "base:runtime" // It returns the value, which will be empty if the variable is not present // To distinguish between an empty value and an unset value, use lookup_env // NOTE: the value will be allocated with the supplied allocator +@(require_results) get_env :: proc(key: string, allocator: runtime.Allocator) -> string { value, _ := lookup_env(key, allocator) return value @@ -15,6 +16,7 @@ get_env :: proc(key: string, allocator: runtime.Allocator) -> string { // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // Otherwise the returned value will be empty and the boolean will be false // NOTE: the value will be allocated with the supplied allocator +@(require_results) lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { return _lookup_env(key, allocator) } @@ -38,6 +40,7 @@ clear_env :: proc() { // environ returns a copy of strings representing the environment, in the form "key=value" // NOTE: the slice of strings and the strings with be allocated using the supplied allocator +@(require_results) environ :: proc(allocator: runtime.Allocator) -> []string { return _environ(allocator) } diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index a0fd48c27..b6e9472fb 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -51,22 +51,27 @@ stdout: ^File = nil // OS-Specific stderr: ^File = nil // OS-Specific +@(require_results) create :: proc(name: string) -> (^File, Error) { return open(name, {.Read, .Write, .Create}, File_Mode(0o777)) } +@(require_results) open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) { return _open(name, flags, perm) } +@(require_results) new_file :: proc(handle: uintptr, name: string) -> ^File { return _new_file(handle, name) } +@(require_results) fd :: proc(f: ^File) -> uintptr { return _fd(f) } +@(require_results) name :: proc(f: ^File) -> string { return _name(f) } @@ -200,15 +205,18 @@ fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error { return _fchtimes(f, atime, mtime) } +@(require_results) exists :: proc(path: string) -> bool { return _exists(path) } +@(require_results) is_file :: proc(path: string) -> bool { return _is_file(path) } is_dir :: is_directory +@(require_results) is_directory :: proc(path: string) -> bool { return _is_dir(path) } diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 3f66bbd09..3843c1105 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -1,10 +1,9 @@ //+private package os2 +import "base:runtime" import "core:io" import "core:time" -import "core:strings" -import "base:runtime" import "core:sys/unix" INVALID_HANDLE :: -1 @@ -35,9 +34,9 @@ _File :: struct { allocator: runtime.Allocator, } -_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) { +_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return // Just default to using O_NOCTTY because needing to open a controlling // terminal would be incredibly rare. This has no effect on files while @@ -195,7 +194,7 @@ _truncate :: proc(f: ^File, size: i64) -> Error { _remove :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, int(File_Flags.Read)) if fd < 0 { @@ -210,22 +209,25 @@ _remove :: proc(name: string) -> Error { } _rename :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr)) } _link :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr)) } _symlink :: proc(old_name, new_name: string) -> Error { - old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator) - new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + old_name_cstr := temp_cstring(old_name) or_return + new_name_cstr := temp_cstring(new_name) or_return return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr)) } @@ -234,7 +236,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st bufsz : uint = 256 buf := make([]byte, bufsz, allocator) for { - rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz) + rc := unix.sys_readlink(name_cstr, &buf[0], bufsz) if rc < 0 { delete(buf) return "", _get_platform_error(rc) @@ -243,26 +245,26 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st delete(buf) buf = make([]byte, bufsz, allocator) } else { - return strings.string_from_ptr(&buf[0], rc), nil + return string(buf[:rc]), nil } } } -_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { +_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _read_link_cstr(name_cstr, allocator) } _unlink :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_unlink(name_cstr)) } _chdir :: proc(name: string) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chdir(name_cstr)) } @@ -272,7 +274,7 @@ _fchdir :: proc(f: ^File) -> Error { _chmod :: proc(name: string, mode: File_Mode) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode))) } @@ -283,14 +285,14 @@ _fchmod :: proc(f: ^File, mode: File_Mode) -> Error { // NOTE: will throw error without super user priviledges _chown :: proc(name: string, uid, gid: int) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_chown(name_cstr, uid, gid)) } // NOTE: will throw error without super user priviledges _lchown :: proc(name: string, uid, gid: int) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid)) } @@ -301,7 +303,7 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error { _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr := temp_cstring(name) or_return times := [2]Unix_File_Time { { atime._nsec, 0 }, { mtime._nsec, 0 }, @@ -319,13 +321,13 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error { _exists :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) return unix.sys_access(name_cstr, F_OK) == 0 } _is_file :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -345,7 +347,7 @@ _is_file_fd :: proc(fd: int) -> bool { _is_dir :: proc(name: string) -> bool { TEMP_ALLOCATOR_GUARD() - name_cstr := _temp_name_to_cstring(name) + name_cstr, _ := temp_cstring(name) s: _Stat res := unix.sys_stat(name_cstr, &s) if res < 0 { @@ -363,16 +365,6 @@ _is_dir_fd :: proc(fd: int) -> bool { return S_ISDIR(s.mode) } -// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly -// defined as 512, however, it is well known that paths can exceed that limit. -// So, in theory you could have a path larger than the entire temp_allocator's -// buffer. Therefor, any large paths will use context.allocator. -@(private="file") -_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) { - return strings.clone_to_cstring(name, 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) diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 9ec75fc91..977979bae 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -76,6 +76,7 @@ read_entire_file :: proc{ read_entire_file_from_file, } +@(require_results) read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) { f, ferr := open(name) if ferr != nil { @@ -85,6 +86,7 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) - return read_entire_file_from_file(f, allocator) } +@(require_results) read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) { size: int has_size := true @@ -135,6 +137,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d } } +@(require_results) write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error { flags := O_WRONLY|O_CREATE if truncate { diff --git a/core/os/os2/heap.odin b/core/os/os2/heap.odin index a07a0d618..e0cffaf0d 100644 --- a/core/os/os2/heap.odin +++ b/core/os/os2/heap.odin @@ -2,6 +2,7 @@ package os2 import "base:runtime" +@(require_results) heap_allocator :: proc() -> runtime.Allocator { return runtime.Allocator{ procedure = heap_allocator_proc, @@ -10,6 +11,7 @@ heap_allocator :: proc() -> runtime.Allocator { } +@(require_results) heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) { diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 69c3c8941..756343efd 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -7,6 +7,7 @@ import "base:runtime" // Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix // parts which are split by the last "*" +@(require_results) _prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) { for i in 0.. (prefix, suffix: string, err: Err return } -clone_string :: proc(s: string, allocator: runtime.Allocator) -> string { - buf := make([]byte, len(s), allocator) +@(require_results) +clone_string :: proc(s: string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { + buf := make([]byte, len(s), allocator) or_return copy(buf, s) - return string(buf) + return string(buf), nil } +@(require_results) +clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstring, err: runtime.Allocator_Error) { + res = "" // do not use a `nil` cstring + buf := make([]byte, len(s)+1, allocator) or_return + copy(buf, s) + buf[len(s)] = 0 + return cstring(&buf[0]), nil +} + +@(require_results) +temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { + return clone_to_cstring(s, temp_allocator()) +} + + + + +@(require_results) concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { n := 0 for s in strings { @@ -57,6 +77,7 @@ init_random_string_seed :: proc() { _ = next_random(s) } +@(require_results) next_random :: proc(r: ^[2]u64) -> u64 { old_state := r[0] r[0] = old_state * 6364136223846793005 + (r[1]|1) @@ -65,6 +86,7 @@ next_random :: proc(r: ^[2]u64) -> u64 { return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63)) } +@(require_results) random_string :: proc(buf: []byte) -> string { @static digits := "0123456789" diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin index 277da56dd..27c3d6b0b 100644 --- a/core/os/os2/path.odin +++ b/core/os/os2/path.odin @@ -6,6 +6,7 @@ Path_Separator :: _Path_Separator // OS-Specific Path_Separator_String :: _Path_Separator_String // OS-Specific Path_List_Separator :: _Path_List_Separator // OS-Specific +@(require_results) is_path_separator :: proc(c: byte) -> bool { return _is_path_separator(c) } @@ -26,6 +27,7 @@ remove_all :: proc(path: string) -> Error { getwd :: get_working_directory +@(require_results) get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _getwd(allocator) } diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index fde4ba5b1..aaaafa2b4 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -32,7 +32,8 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { return .Invalid_Argument } - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + path_cstr := strings.clone_to_cstring(path, temp_allocator()) return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777))) } diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin index 62f7ddf10..9254d6f8e 100644 --- a/core/os/os2/pipe.odin +++ b/core/os/os2/pipe.odin @@ -1,5 +1,6 @@ package os2 +@(require_results) pipe :: proc() -> (r, w: ^File, err: Error) { return _pipe() } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index a64522ac1..16f319046 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -25,20 +25,24 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } +@(require_results) fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { return _fstat(f, allocator) } +@(require_results) stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) } lstat :: stat_do_not_follow_links +@(require_results) stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _lstat(name, allocator) } +@(require_results) same_file :: proc(fi1, fi2: File_Info) -> bool { return _same_file(fi1, fi2) } diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index a1be8f690..fa71f44a7 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -13,6 +13,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? // If `dir` is an empty tring, `temp_directory()` will be used. // // The caller must `close` the file once finished with. +@(require_results) create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return @@ -44,6 +45,7 @@ mkdir_temp :: make_directory_temp // The directory name is generated by taking a pattern, and adding a randomized string to the end. // If the pattern includes an "*", the randm string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. +@(require_results) make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return @@ -58,7 +60,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) err = make_directory(name, 0o700) if err == nil { - return clone_string(name, allocator), nil + return clone_string(name, allocator) } if err == .Exist { attempts += 1 @@ -78,6 +80,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) } temp_dir :: temp_directory +@(require_results) temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 0af461bf5..a4ef7c1dd 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -3,6 +3,7 @@ package os2 import "core:strings" import "base:runtime" +@(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: @@ -31,6 +32,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error return } +@(require_results) user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: @@ -59,6 +61,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro return } +@(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { env := "HOME" #partial switch ODIN_OS { From 453fc5182b85af2e3254f242e23f0d5e89fde78d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:34:05 +0100 Subject: [PATCH 29/34] Remove all uses of `context` stuff in os2 --- core/os/os2/file_windows.odin | 3 +-- core/os/os2/internal_util.odin | 26 ++++++++++++++++++++++++-- core/os/os2/path_linux.odin | 14 ++++++++------ core/os/os2/path_windows.odin | 3 +-- core/os/os2/stat_linux.odin | 22 ++++++++++++---------- core/os/os2/temp_file.odin | 23 +++++------------------ core/os/os2/user.odin | 13 ++++++------- 7 files changed, 57 insertions(+), 47 deletions(-) diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 1e0899992..37f8f44de 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -6,7 +6,6 @@ import "base:runtime" import "core:io" import "core:mem" import "core:sync" -import "core:strings" import "core:time" import "core:unicode/utf16" import win32 "core:sys/windows" @@ -137,7 +136,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File { f.impl.allocator = file_allocator() f.impl.fd = rawptr(handle) - f.impl.name = strings.clone(name, f.impl.allocator) + f.impl.name, _ = clone_string(name, f.impl.allocator) f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator) handle := _handle(f) diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 756343efd..57c21c6bd 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -47,8 +47,16 @@ temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { return clone_to_cstring(s, temp_allocator()) } - - +string_from_null_terminated_bytes :: proc(b: []byte) -> (res: string) { + s := string(b) + i := 0 + for ; i < len(s); i += 1 { + if s[i] == 0 { + break + } + } + return s[:i] +} @(require_results) concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string { @@ -61,6 +69,20 @@ concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> strin return string(buf[:n]) } +@(require_results) +concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) { + n := 0 + for s in strings { + n += len(s) + } + buf := make([]byte, n) or_return + n = 0 + for s in strings { + n += copy(buf[n:], s) + } + return string(buf), nil +} + @(private="file") diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index aaaafa2b4..81176c219 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -1,7 +1,6 @@ //+private package os2 -import "core:strings" import "core:strconv" import "base:runtime" import "core:sys/unix" @@ -33,7 +32,7 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error { } TEMP_ALLOCATOR_GUARD() - path_cstr := strings.clone_to_cstring(path, temp_allocator()) + path_cstr := temp_cstring(path) or_return return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777))) } @@ -73,6 +72,8 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { return .Invalid_Argument } + TEMP_ALLOCATOR_GUARD() + // need something we can edit, and use to generate cstrings allocated: bool path_bytes: []u8 @@ -80,7 +81,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { allocated = true path_bytes = make([]u8, len(path) + 1) } else { - path_bytes = make([]u8, len(path) + 1, context.temp_allocator) + path_bytes = make([]u8, len(path) + 1, temp_allocator()) } // NULL terminate the byte slice to make it a valid cstring @@ -178,7 +179,8 @@ _remove_all :: proc(path: string) -> Error { return nil } - path_cstr := strings.clone_to_cstring(path, context.temp_allocator) + TEMP_ALLOCATOR_GUARD() + path_cstr := temp_cstring(path) or_return fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS) switch fd { @@ -204,7 +206,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil + return string_from_null_terminated_bytes(buf[:]), nil } if res != -ERANGE { return "", _get_platform_error(res) @@ -215,7 +217,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { } _setwd :: proc(dir: string) -> Error { - dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator) + dir_cstr := temp_cstring(dir) or_return return _ok_or_error(unix.sys_chdir(dir_cstr)) } diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin index 064356cf1..fcd1e3321 100644 --- a/core/os/os2/path_windows.odin +++ b/core/os/os2/path_windows.odin @@ -3,7 +3,6 @@ package os2 import win32 "core:sys/windows" import "base:runtime" -import "core:strings" _Path_Separator :: '\\' _Path_Separator_String :: "\\" @@ -24,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { if len(p) == len(`\\?\c:`) { if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { - s = strings.concatenate({p, `\`}, file_allocator()) or_return + s = concatenate({p, `\`}, file_allocator()) or_return allocated = true return } diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index db929a719..4508ab892 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -3,7 +3,6 @@ package os2 import "core:time" import "base:runtime" -import "core:strings" import "core:sys/unix" import "core:path/filepath" @@ -112,8 +111,9 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er } // NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath -_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) +_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + TEMP_ALLOCATOR_GUARD() + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, _O_RDONLY) if fd < 0 { @@ -123,8 +123,9 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) return _fstat_internal(fd, allocator) } -_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) +_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + TEMP_ALLOCATOR_GUARD() + name_cstr := temp_cstring(name) or_return fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW) if fd < 0 { @@ -138,8 +139,9 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } -_stat_internal :: proc(name: string) -> (s: _Stat, res: int) { - name_cstr := strings.clone_to_cstring(name, context.temp_allocator) - res = unix.sys_stat(name_cstr, &s) - return -} +// _stat_internal :: proc(name: string) -> (s: _Stat, res: int) { +// TEMP_ALLOCATOR_GUARD() +// name_cstr := temp_cstring(name) or_return +// res = unix.sys_stat(name_cstr, &s) +// return +// } diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index fa71f44a7..ead2108de 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -18,7 +18,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return - prefix = temp_join_path(dir, prefix) + prefix = temp_join_path(dir, prefix) or_return rand_buf: [32]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) @@ -50,7 +50,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) TEMP_ALLOCATOR_GUARD() dir := dir if dir != "" else temp_directory(temp_allocator()) or_return prefix, suffix := _prefix_and_suffix(pattern) or_return - prefix = temp_join_path(dir, prefix) + prefix = temp_join_path(dir, prefix) or_return rand_buf: [32]byte name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator()) @@ -88,23 +88,10 @@ temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { @(private="file") -temp_join_path :: proc(dir, name: string) -> string { - concat :: proc(strings: ..string) -> string { - n := 0 - for s in strings { - n += len(s) - } - buf := make([]byte, n) - n = 0 - for s in strings { - n += copy(buf[n:], s) - } - return string(buf) - } - +temp_join_path :: proc(dir, name: string) -> (string, runtime.Allocator_Error) { if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) { - return concat(dir, name) + return concatenate({dir, name}, temp_allocator(),) } - return concat(dir, Path_Separator_String, name) + return concatenate({dir, Path_Separator_String, name}, temp_allocator()) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index a4ef7c1dd..ca099f7ae 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,6 +1,5 @@ package os2 -import "core:strings" import "base:runtime" @(require_results) @@ -9,12 +8,12 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error case .Windows: dir = get_env("LocalAppData", allocator) if dir != "" { - dir = strings.clone(dir, allocator) or_return + dir = clone_string(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate({dir, "/Library/Caches"}, allocator) or_return + dir = concatenate({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -23,7 +22,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error if dir == "" { return } - dir = strings.concatenate({dir, "/.cache"}, allocator) or_return + dir = concatenate({dir, "/.cache"}, allocator) or_return } } if dir == "" { @@ -38,12 +37,12 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro case .Windows: dir = get_env("AppData", allocator) if dir != "" { - dir = strings.clone(dir, allocator) or_return + dir = clone_string(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", allocator) if dir != "" { - dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) or_return + dir = concatenate({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -52,7 +51,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro if dir == "" { return } - dir = strings.concatenate({dir, "/.config"}, allocator) or_return + dir = concatenate({dir, "/.config"}, allocator) or_return } } if dir == "" { From 9a487ccb6f9834a351aff6330255bd7174f14bb9 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:35:24 +0100 Subject: [PATCH 30/34] Delete dead code --- core/os/os2/stat_linux.odin | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 4508ab892..dc287cafe 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -138,10 +138,3 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er _same_file :: proc(fi1, fi2: File_Info) -> bool { return fi1.fullpath == fi2.fullpath } - -// _stat_internal :: proc(name: string) -> (s: _Stat, res: int) { -// TEMP_ALLOCATOR_GUARD() -// name_cstr := temp_cstring(name) or_return -// res = unix.sys_stat(name_cstr, &s) -// return -// } From 61826594c940f86ec6ed900f6c191ed98065bab5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:37:23 +0100 Subject: [PATCH 31/34] Add comment state to prefer `virtual.Arena` over `runtime.Arena` --- base/runtime/default_allocators_arena.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/base/runtime/default_allocators_arena.odin b/base/runtime/default_allocators_arena.odin index fcc98abfa..571590f93 100644 --- a/base/runtime/default_allocators_arena.odin +++ b/base/runtime/default_allocators_arena.odin @@ -12,6 +12,7 @@ Memory_Block :: struct { capacity: uint, } +// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary Arena :: struct { backing_allocator: Allocator, curr_block: ^Memory_Block, From 2b43535961f1e02ab3e333019c0da6fc3034d432 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:45:47 +0100 Subject: [PATCH 32/34] Fix typo --- core/os/os2/temp_file.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index ead2108de..3b3dbdd57 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -9,7 +9,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? // // Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`. // The filename is generated by taking a pattern, and adding a randomized string to the end. -// If the pattern includes an "*", the randm string replaces the last "*". +// If the pattern includes an "*", the random string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. // // The caller must `close` the file once finished with. @@ -43,7 +43,7 @@ mkdir_temp :: make_directory_temp // Creates a new temporary directory in the directory `dir`, and returns the path of the new directory. // // The directory name is generated by taking a pattern, and adding a randomized string to the end. -// If the pattern includes an "*", the randm string replaces the last "*". +// If the pattern includes an "*", the random string replaces the last "*". // If `dir` is an empty tring, `temp_directory()` will be used. @(require_results) make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) { From 6f9dcb4e02e3da76b17276a0d5de73f338d214c6 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:46:45 +0100 Subject: [PATCH 33/34] Add missing `@(require_results)` --- core/os/os2/internal_util.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 57c21c6bd..59d845350 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -47,6 +47,7 @@ temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) { return clone_to_cstring(s, temp_allocator()) } +@(require_results) string_from_null_terminated_bytes :: proc(b: []byte) -> (res: string) { s := string(b) i := 0 From 59c33dd9fce58f2fdf396a622c446f3912e88a95 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 14 May 2024 18:57:03 +0100 Subject: [PATCH 34/34] Add `Fstat_Callback` for `File.user_fstat` --- core/os/os2/file.odin | 3 ++- core/os/os2/stat.odin | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index b6e9472fb..4f4bd942e 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -5,8 +5,9 @@ import "core:time" import "base:runtime" File :: struct { - impl: _File, + impl: _File, stream: io.Stream, + user_fstat: Fstat_Callback, } File_Mode :: distinct u32 diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 16f319046..f79ad9165 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -3,6 +3,8 @@ package os2 import "core:time" import "base:runtime" +Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) + File_Info :: struct { fullpath: string, name: string, @@ -27,6 +29,9 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { @(require_results) fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { + if f != nil && f.user_fstat != nil { + return f->user_fstat(allocator) + } return _fstat(f, allocator) }