diff --git a/core/crypto/argon2id/argon2id.odin b/core/crypto/argon2id/argon2id.odin index 3bff5a3a9..e2f5e487b 100644 --- a/core/crypto/argon2id/argon2id.odin +++ b/core/crypto/argon2id/argon2id.odin @@ -143,7 +143,7 @@ derive :: proc( m_ := 4 * u64(p) * (m / u64(4 * p)) b := mem.alloc_bytes_non_zeroed( int(m_) * BLOCK_SIZE_BYTES, - alignment = mem.DEFAULT_PAGE_SIZE, + alignment = mem.PAGE_SIZE, allocator = allocator, ) or_return defer delete(b, allocator) diff --git a/core/encoding/json/unparse.odin b/core/encoding/json/unparse.odin new file mode 100644 index 000000000..1b00f1b0e --- /dev/null +++ b/core/encoding/json/unparse.odin @@ -0,0 +1,89 @@ +package encoding_json + +import "base:runtime" +import "core:strings" +import "core:io" +import "core:slice" + +Unparse_Error :: union #shared_nil { + io.Error, + runtime.Allocator_Error, +} + +@(require_results) +unparse :: proc(v: Value, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: string, err: Unparse_Error) { + b := strings.builder_make(allocator, loc) + defer if err != nil { + strings.builder_destroy(&b) + } + + // temp guard in case we are sorting map keys, which will use temp allocations + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) + + opt := opt + unparse_to_builder(&b, v, &opt) or_return + data = string(b.buf[:]) + return +} + +@(require_results) +unparse_to_builder :: proc(b: ^strings.Builder, v: Value, opt: ^Marshal_Options) -> Unparse_Error { + return unparse_to_writer(strings.to_writer(b), v, opt) +} + +@(require_results) +unparse_to_writer :: proc(w: io.Writer, value: Value, opt: ^Marshal_Options) -> Unparse_Error { + switch v in value { + case nil, Null: + io.write_string(w, "null") or_return + case Integer: + base := 16 if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) else 10 + io.write_i64(w, v, base) or_return + case Float: + io.write_f64(w, v) or_return + case Boolean: + io.write_string(w, "true" if v else "false") or_return + case String: + io.write_quoted_string(w, v, '"', nil, true) or_return + case Array: + opt_write_start(w, opt, '[') or_return + for e, i in v { + opt_write_iteration(w, opt, i == 0) or_return + unparse_to_writer (w, e, opt) or_return + } + opt_write_end(w, opt, ']') or_return + case Object: + if !opt.sort_maps_by_key { + opt_write_start(w, opt, '{') or_return + for first_iteration := true; key, val in v { + opt_write_iteration(w, opt, first_iteration) or_return + opt_write_key (w, opt, key) or_return + unparse_to_writer (w, val, opt) or_return + first_iteration = false + } + opt_write_end(w, opt, '}') or_return + } else { + Map_Entry :: struct { + key: string, + value: Value, + } + + entries := make([dynamic]Map_Entry, 0, len(v), context.temp_allocator) or_return + for key, val in v { + _, _ = append(&entries, Map_Entry{key, val}) + } + + slice.sort_by(entries[:], proc(i, j: Map_Entry) -> bool { return i.key < j.key }) + + opt_write_start(w, opt, '{') or_return + for e, i in entries { + opt_write_iteration(w, opt, i == 0) or_return + opt_write_key (w, opt, e.key) or_return + unparse_to_writer (w, e.value, opt) or_return + } + opt_write_end(w, opt, '}') or_return + } + return nil + } + return nil +} diff --git a/core/flags/doc.odin b/core/flags/doc.odin index 183b86f41..e17cff21b 100644 --- a/core/flags/doc.odin +++ b/core/flags/doc.odin @@ -11,13 +11,21 @@ Command-Line Syntax: Arguments are treated differently depending on how they're formatted. The format is similar to the Odin binary's way of handling compiler flags. - type handling - ------------ ------------------------ - depends on struct layout - - set a bool true - - set flag to option - - set flag to option, alternative syntax - -:= set map[key] to value + type handling + ------------ ------------------------ + depends on struct layout + - set a bool true + - set flag to option + - set flag to option, alternative syntax + - set bit_set flag to one or more options + - set bit_set flag to one or more options, alternative syntax + -:= set map[key] to value + +Underscores (`_`) in a flag will be replaced with dashes (`-`). + +Bit sets may be set with a strictly comma-separated list of options. + +Bit sets may also be set with a binary string of 0s and 1s, and will be parsed from left to right, from least significant bit to most significant bit. Underscores are allowed and will be ignored. However, starting with an underscore is disallowed. Unhandled Arguments: diff --git a/core/flags/example/example.odin b/core/flags/example/example.odin index 6ace3d852..204fca8e9 100644 --- a/core/flags/example/example.odin +++ b/core/flags/example/example.odin @@ -20,6 +20,14 @@ Optimization_Level :: enum { Ludicrous_Speed, } +Vet_Flag :: enum { + Unused, + Unused_Variables, + Unused_Imports, + Shadowing, + Using_Stmt, +} + // It's simple but powerful. my_custom_type_setter :: proc( data: rawptr, @@ -83,6 +91,8 @@ main :: proc() { schedule: datetime.DateTime `usage:"Launch tasks at this time."`, opt: Optimization_Level `usage:"Optimization level."`, + vet_flags: bit_set[Vet_Flag] `usage:"Vet flags. Example usage: + -vet-flags:Unused,Shadowing"`, todo: [dynamic]string `usage:"Todo items."`, accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`, diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 1d86f4bce..8a79f75a8 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -110,7 +110,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: case f32be: (^f32be)(ptr)^ = cast(f32be) value case f64be: (^f64be)(ptr)^ = cast(f64be) value } - + case runtime.Type_Info_Complex: value := strconv.parse_complex128(str) or_return switch type_info.id { @@ -118,7 +118,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: case complex64: (^complex64) (ptr)^ = (complex64)(value) case complex128: (^complex128)(ptr)^ = value } - + case runtime.Type_Info_Quaternion: value := strconv.parse_quaternion256(str) or_return switch type_info.id { @@ -152,15 +152,16 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: } case runtime.Type_Info_Bit_Set: - // Parse a string of 1's and 0's, from left to right, + // Parse a string of 1s and 0s, from left to right, // least significant bit to most significant bit. value: u128 // NOTE: `upper` is inclusive, i.e: `0..=31` - max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower) + underscores := u128(strings.count(str, "_")) + bit_index_limit := u128(1 + specific_type_info.upper - specific_type_info.lower) + underscores bit_index := u128(0) #no_bounds_check for string_index in 0.. Logger { data := new(File_Console_Logger_Data, allocator) data.file_handle = f @@ -74,6 +117,13 @@ create_file_logger :: proc(f: ^os.File, lowest := Level.Debug, opt := Default_Fi return Logger{file_logger_proc, data, lowest, opt} } +/* +Free the state allocated with `create_file_logger` and close the file handle. + +Inputs: +- `log`: Logger created with `create_file_logger` +- `allocator`: Allocator passed to `create_file_logger` (default is `context.allocator`) +*/ destroy_file_logger :: proc(log: Logger, allocator := context.allocator) { data := cast(^File_Console_Logger_Data)log.data if data.file_handle != nil { @@ -82,6 +132,19 @@ destroy_file_logger :: proc(log: Logger, allocator := context.allocator) { free(data, allocator) } +/* +Create a logger that outputs to the terminal. + +*Allocates Using Provided Allocator* + +When no longer needed can be destroyed with `destroy_console_logger`. + +Inputs: +- `lowest`: Log level to use (default is `.Debug`) +- `opt`: Specifies additional data present in the log output (default is `log.Default_Console_Logger_Opts`) +- `ident`: Identifier to include in the output (default is `""`) +- `allocator`: Allocator to use for data backing the logger (default is `context.allocator`) +*/ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger { data := new(File_Console_Logger_Data, allocator) data.file_handle = nil @@ -89,6 +152,13 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg return Logger{console_logger_proc, data, lowest, opt} } +/* +Free the state allocated with `create_console_logger`. + +Inputs: +- `log`: Logger created with `create_console_logger` +- `allocator`: Allocator passed to `create_console_logger` (default is `context.allocator`) +*/ destroy_console_logger :: proc(log: Logger, allocator := context.allocator) { free(log.data, allocator) } @@ -117,6 +187,7 @@ _file_console_logger_proc :: proc(h: ^os.File, ident: string, level: Level, text fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text) } + file_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { data := cast(^File_Console_Logger_Data)logger_data _file_console_logger_proc(data.file_handle, data.ident, level, text, options, location) @@ -136,6 +207,7 @@ console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, opt _file_console_logger_proc(h, data.ident, level, text, options, location) } +// Helper used to build the part of the message including the log level. do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) { RESET :: ansi.CSI + ansi.RESET + ansi.SGR @@ -162,6 +234,7 @@ do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) { } } +// Helper used to build the part of the message including the data and time. do_time_header :: proc(opts: Options, buf: ^strings.Builder, t: time.Time) { when time.IS_SUPPORTED { if Full_Timestamp_Opts & opts != nil { @@ -180,6 +253,7 @@ do_time_header :: proc(opts: Options, buf: ^strings.Builder, t: time.Time) { } } +// Helper used to build the part of the message including the file location. do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) { if Location_Header_Opts & opts == nil { return diff --git a/core/log/log.odin b/core/log/log.odin index 4209a2850..ce0f49124 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -1,52 +1,102 @@ -// Implementations of the `context.Logger` interface. package log import "base:runtime" import "core:fmt" -// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle. +//These are defined in package `base:runtime` as they are used in the `context`. This is to prevent an import definition cycle. /* -Logger_Level :: enum { - Debug = 0, - Info = 10, - Warning = 20, - Error = 30, - Fatal = 40, -} + Logger_Level :: enum { + Debug = 0, + Info = 10, + Warning = 20, + Error = 30, + Fatal = 40, + } */ Level :: runtime.Logger_Level /* -Option :: enum { - Level, - Date, - Time, - Short_File_Path, - Long_File_Path, - Line, - Procedure, - Terminal_Color -} +Specifies additional data present in the log output. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Option :: enum { + // The log level, e.g. "[DEBUG] ---" + Level, + // The date, e.g. [2025-01-02] + Date, + // The time, e.g. [12:34:56] + Time, + // Just the filename, e.g. [main.odin] + Short_File_Path, + // Full file path, e.g. [/tmp/project/main.odin] + Long_File_Path, + // File line of the log statement, e.g. [8] + Line, + // Calling procedure, e.g. [main()] + Procedure, + // Enables colored output + Terminal_Color + } */ Option :: runtime.Logger_Option /* -Options :: bit_set[Option]; +Specifies additional data present in the log output. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Options :: bit_set[Option]; */ Options :: runtime.Logger_Options +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [YYYY-MM-DD HH:MM:SS] Message + +For example: + + [2025-01-02 12:34:56] Hello World! +*/ Full_Timestamp_Opts :: Options{ .Date, .Time, } + +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [file.odin:L:proc()] Message + +For example: + + [main.odin:8:main()] Hello World! +*/ Location_Header_Opts :: Options{ .Short_File_Path, .Long_File_Path, .Line, .Procedure, } + +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [file.odin] Message + +For example: + + [main.odin] Hello World! +*/ Location_File_Opts :: Options{ .Short_File_Path, .Long_File_Path, @@ -54,67 +104,206 @@ Location_File_Opts :: Options{ /* -Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); +Implementation of the logger. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); + */ Logger_Proc :: runtime.Logger_Proc /* -Logger :: struct { - procedure: Logger_Proc, - data: rawptr, - lowest_level: Level, - options: Logger_Options, -} +Data backing the logger. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Logger :: struct { + // Implementation + procedure: Logger_Proc, + // Configuration data passed to the implementation + data: rawptr, + // Minimum level for messages passed to the implementation + lowest_level: Level, + // Additional data present in the log output + options: Logger_Options, + } */ Logger :: runtime.Logger +/* +Do nothing. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. +*/ nil_logger_proc :: runtime.default_logger_proc +/* +Create a logger that does nothing. + +Returns: +- A logger that does nothing +*/ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, Level.Debug, nil} } + +/* +Log a formatted message at the `Debug` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Debug, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Info` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Info, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Warn` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Warning, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Error` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Error, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Fatal` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Fatal, fmt_str, ..args, location=location) } +/* +Log a message at the `Debug` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ debug :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Debug, ..args, sep=sep, location=location) } + +/* +Log a message at the `Info` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ info :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Info, ..args, sep=sep, location=location) } + +/* +Log a message at the `Warn` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ warn :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Warning, ..args, sep=sep, location=location) } + +/* +Log a message at the `Error` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ error :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Error, ..args, sep=sep, location=location) } + +/* +Log a message at the `Fatal` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ fatal :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Fatal, ..args, sep=sep, location=location) } +/* +Log a message at the `Fatal` level and abort the program. + +Inputs: +- `args`: values to be concatenated into the output +- `location`: Location of the caller (default is #caller_location) +*/ panic :: proc(args: ..any, location := #caller_location) -> ! { log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } + +/* +Log a formatted message at the `Fatal` level and abort the program. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } +/* +When condition is `false` log a message at the `Fatal` level and abort the program. + +Can be disabled using `ODIN_DISABLE_ASSERT`. + +Inputs: +- `condition`: A boolean to check +- `message`: Message to log when condition is false (a default is provided) +- `loc`: Location of the caller (default is #caller_location) +*/ @(disabled=ODIN_DISABLE_ASSERT) assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { @@ -131,6 +320,17 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc := } } +/* +When condition is `false` log a formatted message at the `Fatal` level and abort the program. + +Can be disabled using `ODIN_DISABLE_ASSERT`. + +Inputs: +- `condition`: A boolean to check +- `fmt_str`: A format string to use when condition is false, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `loc`: Location of the caller (default is #caller_location) +*/ @(disabled=ODIN_DISABLE_ASSERT) assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { if !condition { @@ -152,6 +352,16 @@ assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo } } +/* +When condition is `false` log a message at the `Fatal` level and abort the program. + +Unlike `assert` this procedure cannot be disabled with `ODIN_DISABLE_ASSERT` and will always execute. + +Inputs: +- `condition`: A boolean to check +- `message`: Message to log when condition is false (a default is provided) +- `loc`: Location of the caller (default is #caller_location) +*/ ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { @(cold) @@ -167,6 +377,17 @@ ensure :: proc(condition: bool, message := #caller_expression(condition), loc := } } +/* +When condition is `false` log a formatted message at the `Fatal` level and abort the program. + +Unlike `assertf` this procedure cannot be disabled with `ODIN_DISABLE_ASSERT` and will always execute. + +Inputs: +- `condition`: A boolean to check +- `fmt_str`: A format string to use when condition is false, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `loc`: Location of the caller (default is #caller_location) +*/ ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { if !condition { @(cold) @@ -184,7 +405,15 @@ ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo } +/* +Log a message at the desired level. +Inputs: +- `level`: The level of the message +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) { logger := context.logger if logger.procedure == nil || logger.procedure == nil_logger_proc { @@ -198,6 +427,15 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) logger.procedure(logger.data, level, str, logger.options, location) } +/* +Log a formatted message at the desired level. + +Inputs: +- `level`: The level of the message +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) { logger := context.logger if logger.procedure == nil || logger.procedure == nil_logger_proc { diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin index c4c54f38f..a6784133b 100644 --- a/core/log/log_allocator.odin +++ b/core/log/log_allocator.odin @@ -6,6 +6,7 @@ import "base:runtime" import "core:fmt" import "core:sync" +// Format to use when logging allocations. Log_Allocator_Format :: enum { Bytes, // Actual number of bytes. Human, // Bytes in human units like bytes, kibibytes, etc. as appropriate. @@ -15,13 +16,23 @@ Log_Allocator_Format :: enum { // Log_Allocator is an allocator which calls `context.logger` on each of its allocations operations. // The format can be changed by setting the `size_fmt: Log_Allocator_Format` field to either `Bytes` or `Human`. Log_Allocator :: struct { - allocator: runtime.Allocator, - level: Level, - prefix: string, + allocator: runtime.Allocator, // Wrapped allocator + level: Level, // Log Level used for allocations + prefix: string, // Prefix to use in log messages lock: sync.Mutex, - size_fmt: Log_Allocator_Format, + size_fmt: Log_Allocator_Format, // Format to use when logging allocations } +/* +Initialize the backing data for the allocator that logs all allocations. + +Inputs: +- `la`: Pointer to the data structure to initialize +- `level`: Log level to use for allocations +- `size_fmt`: Format to use when logging allocations (default is `.Bytes`) +- `allocator`: Wrapped allocator (default is `context.allocator`) +- `prefix`: Prefix to use in log messages (default is `""`) +*/ log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_Allocator_Format.Bytes, allocator := context.allocator, prefix := "") { la.allocator = allocator @@ -31,7 +42,15 @@ log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_All la.size_fmt = size_fmt } +/* +Create an allocator that logs all allocations. +Inputs: +- `la`: Pointer to the data structure backing the allocator + +Returns: +- An allocator that logs all allocations +*/ log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator { return runtime.Allocator{ procedure = log_allocator_proc, @@ -39,6 +58,7 @@ log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator { } } +// Backing procedure for allocator that logs all allocations. log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) { diff --git a/core/log/multi_logger.odin b/core/log/multi_logger.odin index 6122554bb..99d7377db 100644 --- a/core/log/multi_logger.odin +++ b/core/log/multi_logger.odin @@ -1,10 +1,27 @@ package log +// A container backing for multiple loggers. Multi_Logger_Data :: struct { loggers: []Logger, } +/* +Create a logger that logs to all backing loggers. + +*Allocates Using Provided Allocator* + +When no longer needed can be destroyed with `destroy_multi_logger`. + +Note: Logs using a multi logger take both the multi logger and the backing loggers' log levels into account. + +Inputs: +- `logs` - Backing loggers passed as multiple arguments +- `allocator` - An allocator used to allocate data to store backing loggers (default is `context.allocator`) + +Returns: +- A multi logger +*/ create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> Logger { data := new(Multi_Logger_Data, allocator) data.loggers = make([]Logger, len(logs), allocator) @@ -12,12 +29,20 @@ create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> L return Logger{multi_logger_proc, data, Level.Debug, nil} } +/* +Free the state allocated with `create_multi_logger`. + +Inputs: +- `log`: Logger created with `create_multi_logger` +- `allocator`: Allocator passed to `create_multi_logger` (default is `context.allocator`) +*/ destroy_multi_logger :: proc(log: Logger, allocator := context.allocator) { data := (^Multi_Logger_Data)(log.data) delete(data.loggers, allocator) free(data, allocator) } +// Backing procedure for the multi logger. multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { data := cast(^Multi_Logger_Data)logger_data diff --git a/core/math/math.odin b/core/math/math.odin index 1ecb1da00..6fe872907 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1613,17 +1613,11 @@ is_power_of_two :: proc "contextless" (x: int) -> bool { @(require_results) next_power_of_two :: proc "contextless" (x: int) -> int { - k := x -1 - when size_of(int) == 8 { - k = k | (k >> 32) + if x <= 1 { + return 1 } - k = k | (k >> 16) - k = k | (k >> 8) - k = k | (k >> 4) - k = k | (k >> 2) - k = k | (k >> 1) - k += 1 + int(x <= 0) - return k + n := uint(size_of(x) * 8) - uint(intrinsics.count_leading_zeros(uint(x) - 1)) + return int(1) << n } @(require_results) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 48cc39245..282c8d0b4 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -255,11 +255,10 @@ alignment is not specified explicitly. DEFAULT_ALIGNMENT :: 2*align_of(rawptr) /* -Default page size. - -This value is the default page size for the current platform. +On platforms where we were able to query a configurable size, we use that value instead. +See `query_page_size_init()` */ -DEFAULT_PAGE_SIZE :: +PAGE_SIZE: int = 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else 4 * 1024 diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 68b6102b6..40c320115 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1621,20 +1621,6 @@ small_stack_allocator_proc :: proc( return nil, nil } - -/* Preserved for compatibility */ -Dynamic_Pool :: Dynamic_Arena -DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT -DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT -dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc -dynamic_pool_free_all :: dynamic_arena_free_all -dynamic_pool_reset :: dynamic_arena_reset -dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes -dynamic_pool_alloc :: dynamic_arena_alloc -dynamic_pool_init :: dynamic_arena_init -dynamic_pool_allocator :: dynamic_arena_allocator -dynamic_pool_destroy :: dynamic_arena_destroy - /* Default block size for dynamic arena. */ @@ -1651,7 +1637,7 @@ Dynamic arena allocator data. Dynamic_Arena :: struct { block_size: int, out_band_size: int, - alignment: int, + minimum_alignment: int, unused_blocks: [dynamic]rawptr, used_blocks: [dynamic]rawptr, out_band_allocations: [dynamic]rawptr, @@ -1668,23 +1654,23 @@ This procedure initializes a dynamic arena. The specified `block_allocator` will be used to allocate arena blocks, and `array_allocator` to allocate arrays of blocks and out-band blocks. The blocks have the default size of `block_size` and out-band threshold will be `out_band_size`. All allocations -will be aligned to a boundary specified by `alignment`. +will be aligned at a minimum to a boundary specified by `minimum_alignment`. */ dynamic_arena_init :: proc( - pool: ^Dynamic_Arena, - block_allocator := context.allocator, - array_allocator := context.allocator, - block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, - out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, - alignment := DEFAULT_ALIGNMENT, + arena: ^Dynamic_Arena, + block_allocator := context.allocator, + array_allocator := context.allocator, + block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, + minimum_alignment := DEFAULT_ALIGNMENT, ) { - pool.block_size = block_size - pool.out_band_size = out_band_size - pool.alignment = alignment - pool.block_allocator = block_allocator - pool.out_band_allocations.allocator = array_allocator - pool.unused_blocks.allocator = array_allocator - pool.used_blocks.allocator = array_allocator + arena.block_size = block_size + arena.out_band_size = out_band_size + arena.minimum_alignment = minimum_alignment + arena.block_allocator = block_allocator + arena.out_band_allocations.allocator = array_allocator + arena.unused_blocks.allocator = array_allocator + arena.used_blocks.allocator = array_allocator } /* @@ -1728,7 +1714,7 @@ dynamic_arena_destroy :: proc(a: ^Dynamic_Arena) { } @(private="file") -_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { +_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, alignment: int, loc := #caller_location) -> (err: Allocator_Error) { if a.block_allocator.procedure == nil { panic("You must call `dynamic_arena_init` on a Dynamic Arena before using it.", loc) } @@ -1744,7 +1730,7 @@ _dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_locatio a.block_allocator.data, Allocator_Mode.Alloc, a.block_size, - a.alignment, + max(a.minimum_alignment, alignment), nil, 0, ) @@ -1766,8 +1752,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes(a, size, loc) +dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes(a, size, alignment, loc) return raw_data(data), err } @@ -1780,8 +1766,8 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -1797,8 +1783,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) return raw_data(data), err } @@ -1811,21 +1797,21 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { if size >= a.out_band_size { assert(a.out_band_allocations.allocator.procedure != nil, "Backing array allocator must be initialized", loc=loc) - memory, err := alloc_bytes_non_zeroed(size, a.alignment, a.out_band_allocations.allocator, loc) + memory, err := alloc_bytes_non_zeroed(size, alignment, a.out_band_allocations.allocator, loc) if memory != nil { append(&a.out_band_allocations, raw_data(memory), loc = loc) } return memory, err } - n := align_formula(size, a.alignment) + n := align_formula(size, max(a.minimum_alignment, alignment)) if n > a.block_size { return nil, .Invalid_Argument } if a.bytes_left < n { - err := _dynamic_arena_cycle_new_block(a, loc) + err := _dynamic_arena_cycle_new_block(a, alignment, loc) if err != nil { return nil, err } @@ -1900,9 +1886,10 @@ dynamic_arena_resize :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1921,16 +1908,17 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { // NOTE: This allocator has no Free mode. return nil, nil } - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, alignment, loc) if bytes != nil { if old_data == nil { zero_slice(bytes) @@ -1960,9 +1948,10 @@ dynamic_arena_resize_non_zeroed :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1981,9 +1970,10 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { @@ -1998,7 +1988,7 @@ dynamic_arena_resize_bytes_non_zeroed :: proc( } // No information is kept about allocations in this allocator, thus we // cannot truly resize anything and must reallocate. - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -2017,17 +2007,17 @@ dynamic_arena_allocator_proc :: proc( arena := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc: - return dynamic_arena_alloc_bytes(arena, size, loc) + return dynamic_arena_alloc_bytes(arena, size, alignment, loc) case .Alloc_Non_Zeroed: - return dynamic_arena_alloc_bytes_non_zeroed(arena, size, loc) + return dynamic_arena_alloc_bytes_non_zeroed(arena, size, alignment, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: dynamic_arena_free_all(arena, loc) case .Resize: - return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -2038,7 +2028,7 @@ dynamic_arena_allocator_proc :: proc( info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { info.size = arena.block_size - info.alignment = arena.alignment + info.alignment = arena.minimum_alignment return byte_slice(info, size_of(info^)), nil } return nil, nil diff --git a/core/mem/mem_posix.odin b/core/mem/mem_posix.odin new file mode 100644 index 000000000..f652e6a25 --- /dev/null +++ b/core/mem/mem_posix.odin @@ -0,0 +1,13 @@ +#+build linux, darwin, netbsd, freebsd, openbsd +package mem + +import "core:sys/posix" + +@(init, private, no_sanitize_address) +query_page_size_init :: proc "contextless" () { + size := posix.sysconf(._PAGESIZE) + PAGE_SIZE = max(PAGE_SIZE, int(size)) + + // is power of two + assert_contextless(PAGE_SIZE != 0 && (PAGE_SIZE & (PAGE_SIZE-1)) == 0) +} \ No newline at end of file diff --git a/core/mem/mem_windows.odin b/core/mem/mem_windows.odin new file mode 100644 index 000000000..09f14d5d0 --- /dev/null +++ b/core/mem/mem_windows.odin @@ -0,0 +1,16 @@ +#+build windows +package mem + +import "core:sys/windows" + +@(init, private, no_sanitize_address) +query_page_size_init :: proc "contextless" () { + info: windows.SYSTEM_INFO + windows.GetSystemInfo(&info) + + size := info.dwPageSize + PAGE_SIZE = max(PAGE_SIZE, int(size)) + + // is power of two + assert_contextless(PAGE_SIZE != 0 && (PAGE_SIZE & (PAGE_SIZE-1)) == 0) +} \ No newline at end of file diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 1ee7cba6c..3d3827885 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -126,11 +126,11 @@ arena_alloc_unguarded :: proc(arena: ^Arena, size: uint, alignment: uint, loc := if err == .Out_Of_Memory { if arena.minimum_block_size == 0 { arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE - arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, DEFAULT_PAGE_SIZE) + arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, uint(mem.PAGE_SIZE)) } if arena.default_commit_size == 0 { arena.default_commit_size = min(DEFAULT_ARENA_GROWING_COMMIT_SIZE, arena.minimum_block_size) - arena.default_commit_size = mem.align_forward_uint(arena.default_commit_size, DEFAULT_PAGE_SIZE) + arena.default_commit_size = mem.align_forward_uint(arena.default_commit_size, uint(mem.PAGE_SIZE)) } if arena.default_commit_size != 0 { diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index a97e00731..ae6206de7 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -6,13 +6,6 @@ import "base:intrinsics" import "base:runtime" _ :: runtime -DEFAULT_PAGE_SIZE := uint(4096) - -@(init, private) -platform_memory_init :: proc "contextless" () { - _platform_memory_init() -} - Allocator_Error :: mem.Allocator_Error @(require_results, no_sanitize_address) @@ -79,7 +72,7 @@ align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint { @(require_results, no_sanitize_address) memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags: Memory_Block_Flags = {}) -> (block: ^Memory_Block, err: Allocator_Error) { - page_size := DEFAULT_PAGE_SIZE + page_size := uint(mem.PAGE_SIZE) assert(mem.is_power_of_two(uintptr(page_size))) committed := committed @@ -144,7 +137,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, // TODO(bill): determine a better heuristic for this behaviour extra_size := max(size, block.committed>>1) platform_total_commit := base_offset + block.used + extra_size - platform_total_commit = align_formula(platform_total_commit, DEFAULT_PAGE_SIZE) + platform_total_commit = align_formula(platform_total_commit, uint(mem.PAGE_SIZE)) platform_total_commit = min(max(platform_total_commit, default_commit_size), pmblock.reserved) assert(pmblock.committed <= pmblock.reserved) diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 92f55c4a3..665749489 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -43,12 +43,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return errno == .NONE } -_platform_memory_init :: proc "contextless" () { - DEFAULT_PAGE_SIZE = 4096 - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { prot: linux.Mem_Protection if .Read in flags { diff --git a/core/mem/virtual/virtual_other.odin b/core/mem/virtual/virtual_other.odin index 6f9c1327a..a8ba29d14 100644 --- a/core/mem/virtual/virtual_other.odin +++ b/core/mem/virtual/virtual_other.odin @@ -25,10 +25,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return false } -_platform_memory_init :: proc "contextless" () { - -} - _map_file :: proc "contextless" (f: any, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { return nil, .Map_Failure } diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin index 6f257c385..623a2b911 100644 --- a/core/mem/virtual/virtual_posix.odin +++ b/core/mem/virtual/virtual_posix.odin @@ -28,15 +28,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK } -_platform_memory_init :: proc "contextless" () { - // NOTE: `posix.PAGESIZE` due to legacy reasons could be wrong so we use `sysconf`. - size := posix.sysconf(._PAGESIZE) - DEFAULT_PAGE_SIZE = uint(max(size, posix.PAGESIZE)) - - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { #assert(i32(posix.Prot_Flag_Bits.READ) == i32(Map_File_Flag.Read)) #assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Map_File_Flag.Write)) diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 14fc35f62..768eae711 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -146,18 +146,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return bool(ok) } - -@(no_sanitize_address) -_platform_memory_init :: proc "contextless" () { - sys_info: SYSTEM_INFO - GetSystemInfo(&sys_info) - DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize)) - - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - - @(no_sanitize_address) _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { page_flags: u32 diff --git a/core/os/file_util.odin b/core/os/file_util.odin index 854977b01..94e1a5841 100644 --- a/core/os/file_util.odin +++ b/core/os/file_util.odin @@ -216,18 +216,18 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator, loc : return } else { buffer: [1024]u8 - out_buffer := make([dynamic]u8, 0, 0, allocator, loc) - total := 0 + out_buffer := make([dynamic]u8, 0, 0, allocator, loc) or_return for { n: int n, err = read(f, buffer[:]) - total += n - append_elems(&out_buffer, ..buffer[:n], loc=loc) or_return + if _, aerr := append_elems(&out_buffer, ..buffer[:n], loc=loc); aerr != nil { + return out_buffer[:], aerr + } if err != nil { if err == .EOF || err == .Broken_Pipe { err = nil } - data = out_buffer[:total] + data = out_buffer[:] return } } diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 24205c53c..9b08d7ba6 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -571,17 +571,18 @@ foreign kernel32 { DisconnectNamedPipe :: proc(hNamedPipe: HANDLE) -> BOOL --- WaitNamedPipeW :: proc(lpNamedPipeName: LPCWSTR, nTimeOut: DWORD) -> BOOL --- - AllocConsole :: proc() -> BOOL --- - AttachConsole :: proc(dwProcessId: DWORD) -> BOOL --- - SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL --- - GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- - FreeConsole :: proc() -> BOOL --- - GetConsoleWindow :: proc() -> HWND --- - GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL --- - SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL --- - SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL --- - GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- - SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- + AllocConsole :: proc() -> BOOL --- + AttachConsole :: proc(dwProcessId: DWORD) -> BOOL --- + SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL --- + GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- + FreeConsole :: proc() -> BOOL --- + GetConsoleWindow :: proc() -> HWND --- + GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL --- + GetConsoleScreenBufferInfoEx :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfoEx: PCONSOLE_SCREEN_BUFFER_INFOEX) -> BOOL --- + SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL --- + SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL --- + GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- + SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- GetDiskFreeSpaceExW :: proc( lpDirectoryName: LPCWSTR, diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index ab707e43a..0f80e0b18 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -3411,9 +3411,21 @@ CONSOLE_READCONSOLE_CONTROL :: struct { dwCtrlWakeupMask: ULONG, dwControlKeyState: ULONG, } - PCONSOLE_READCONSOLE_CONTROL :: ^CONSOLE_READCONSOLE_CONTROL +CONSOLE_SCREEN_BUFFER_INFOEX :: struct { + cbSize: ULONG, + dwSize: COORD, + dwCursorPosition: COORD, + wAttributes: WORD, + srWindow: SMALL_RECT, + dwMaximumWindowSize: COORD, + wPopupAttributes: WORD, + bFullscreenSupported: BOOL, + ColorTable: [16]COLORREF, +} +PCONSOLE_SCREEN_BUFFER_INFOEX :: ^CONSOLE_SCREEN_BUFFER_INFOEX + BY_HANDLE_FILE_INFORMATION :: struct { dwFileAttributes: DWORD, ftCreationTime: FILETIME, @@ -3426,7 +3438,6 @@ BY_HANDLE_FILE_INFORMATION :: struct { nFileIndexHigh: DWORD, nFileIndexLow: DWORD, } - LPBY_HANDLE_FILE_INFORMATION :: ^BY_HANDLE_FILE_INFORMATION FILE_STANDARD_INFO :: struct { diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin index f316c5a6d..14a095229 100644 --- a/core/text/i18n/doc.odin +++ b/core/text/i18n/doc.odin @@ -51,8 +51,6 @@ Example: Tn :: i18n.get_n mo :: proc() { - using fmt - err: i18n.Error // Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter. @@ -62,26 +60,24 @@ Example: if err != .None { return } // These are in the .MO catalog. - println("-----") - println(T("")) - println("-----") - println(T("There are 69,105 leaves here.")) - println("-----") - println(T("Hellope, World!")) - println("-----") + fmt.println("-----") + fmt.println(T("")) + fmt.println("-----") + fmt.println(T("There are 69,105 leaves here.")) + fmt.println("-----") + fmt.println(T("Hellope, World!")) + fmt.println("-----") // We pass 1 into `T` to get the singular format string, then 1 again into printf. - printf(Tn("There is %d leaf.\n", 1), 1) + fmt.printf(Tn("There is %d leaf.\n", 1), 1) // We pass 42 into `T` to get the plural format string, then 42 again into printf. - printf(Tn("There is %d leaf.\n", 42), 42) + fmt.printf(Tn("There is %d leaf.\n", 42), 42) // This isn't in the translation catalog, so the key is passed back untranslated. - println("-----") - println(T("Come visit us on Discord!")) + fmt.println("-----") + fmt.println(T("Come visit us on Discord!")) } qt :: proc() { - using fmt - err: i18n.Error // Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter. @@ -91,18 +87,18 @@ Example: if err != .None { return } // These are in the .TS catalog. As you can see they have sections. - println("--- Page section ---") - println("Page:Text for translation =", T("Page", "Text for translation")) - println("-----") - println("Page:Also text to translate =", T("Page", "Also text to translate")) - println("-----") - println("--- installscript section ---") - println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall")) - println("-----") - println("--- apple_count section ---") - println("apple_count:%d apple(s) =") - println("\t 1 =", Tn("apple_count", "%d apple(s)", 1)) - println("\t 42 =", Tn("apple_count", "%d apple(s)", 42)) + fmt.println("--- Page section ---") + fmt.println("Page:Text for translation =", T("Page", "Text for translation")) + fmt.println("-----") + fmt.println("Page:Also text to translate =", T("Page", "Also text to translate")) + fmt.println("-----") + fmt.println("--- installscript section ---") + fmt.println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall")) + fmt.println("-----") + fmt.println("--- apple_count section ---") + fmt.println("apple_count:%d apple(s) =") + fmt.println("\t 1 =", Tn("apple_count", "%d apple(s)", 1)) + fmt.println("\t 42 =", Tn("apple_count", "%d apple(s)", 42)) } */ package i18n diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cd38ff69a..dd5e99128 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6595,11 +6595,16 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); } else if (positional_operand_count > pt->param_count) { err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; if (show_error) { gbString proc_str = expr_to_string(ce->proc); defer (gb_string_free(proc_str)); - error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + if (param_count_excluding_defaults != pt->param_count) { + char const *err_fmt = "Too many arguments for '%s', expected %td..=%td arguments, got %td"; + error(call, err_fmt, proc_str, param_count_excluding_defaults, pt->param_count, positional_operands.count); + } else { + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + error(call, err_fmt, proc_str, pt->param_count, positional_operands.count); + } } return err; } diff --git a/src/gb/gb.h b/src/gb/gb.h index 3413d1367..84bbc2e0e 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2493,7 +2493,13 @@ extern "C" { #pragma warning(disable:4127) // Conditional expression is constant #endif +gb_internal void print_all_errors(void); +gb_internal bool any_errors(void); +gb_internal bool any_warnings(void); void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...) { + if (any_errors() || any_warnings()) { + print_all_errors(); + } gb_printf_err("%s(%d): %s: ", file, line, prefix); if (condition) gb_printf_err( "`%s` ", condition); diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index 834f6b630..f6399461f 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -307,7 +307,7 @@ test_all_bit_sets :: proc(t: ^testing.T) { "-a:10101", "-b:0000_0000_0000_0001", "-c:11", - "-d:___1", + "-d:1", "-e:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "-f:1", "-g:01", @@ -333,6 +333,65 @@ test_all_bit_sets :: proc(t: ^testing.T) { testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) } } + { + // Starting a binary string with underscore is disallowed. + args := [?]string { "-d:_1" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + + E2 :: enum { + Option_A=5, + Option_B, + Option_C=3, + Option_D, + } + R :: struct { + a: bit_set[E2], + } + r: R + { + // Least significant bit > 0 + args := [?]string { + "-a:Option_A,Option_D", + } + result := flags.parse(&r, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, r.a, bit_set[E2]{E2.Option_A, E2.Option_D}) + } + { + args := [?]string { "-a:Option_Non_Existent" } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + { + // Value names list must be strictly comma-separated. + args := [?]string { "-a:Option_A, Option_B" } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + { + // Value names list must not end with a comma. + args := [?]string { "-a:Option_A,Option_B," } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } } @(test) @@ -1288,13 +1347,11 @@ test_distinct_types :: proc(t: ^testing.T) { unmodified_i: I, } s: S - { args := [?]string {"-base-i:1"} result := flags.parse(&s, args[:]) testing.expect_value(t, result, nil) } - { args := [?]string {"-unmodified-i:1"} result := flags.parse(&s, args[:]) diff --git a/tests/core/mem/test_mem_dynamic_arena.odin b/tests/core/mem/test_mem_dynamic_arena.odin new file mode 100644 index 000000000..7d2b32f1a --- /dev/null +++ b/tests/core/mem/test_mem_dynamic_arena.odin @@ -0,0 +1,103 @@ +package test_core_mem + +import "core:testing" +import "core:mem" + + +expect_arena_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, alignment: int) { + arena: mem.Dynamic_Arena + mem.dynamic_arena_init(&arena, minimum_alignment = alignment) + arena_allocator := mem.dynamic_arena_allocator(&arena) + + element, err := mem.alloc(num_bytes, alignment, arena_allocator) + testing.expect(t, err == .None) + testing.expect(t, element != nil) + + expected_bytes_left := arena.block_size - expected_used_bytes + testing.expectf(t, arena.bytes_left == expected_bytes_left, + ` + Allocated data with size %v bytes, expected %v bytes left, got %v bytes left, off by %v bytes. + Pool: + block_size = %v + out_band_size = %v + minimum_alignment = %v + unused_blocks = %v + used_blocks = %v + out_band_allocations = %v + current_block = %v + current_pos = %v + bytes_left = %v + `, + num_bytes, expected_bytes_left, arena.bytes_left, expected_bytes_left - arena.bytes_left, + arena.block_size, + arena.out_band_size, + arena.minimum_alignment, + arena.unused_blocks, + arena.used_blocks, + arena.out_band_allocations, + arena.current_block, + arena.current_pos, + arena.bytes_left, + ) + testing.expectf(t, uintptr(element) % uintptr(alignment) == 0, "Expected allocation to be aligned to %d byte boundary, got %v", alignment, element) + + mem.dynamic_arena_destroy(&arena) + testing.expect(t, arena.used_blocks == nil) +} + +expect_arena_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, block_size, out_band_size: int) { + testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!") + + arena: mem.Dynamic_Arena + mem.dynamic_arena_init(&arena, block_size = block_size, out_band_size = out_band_size) + arena_allocator := mem.dynamic_arena_allocator(&arena) + + element, err := mem.alloc(num_bytes, allocator = arena_allocator) + testing.expect(t, err == .None) + testing.expect(t, element != nil) + testing.expectf(t, arena.out_band_allocations != nil, + "Allocated data with size %v bytes, which is >= out_of_band_size and it should be in arena.out_band_allocations, but isn't!", + ) + + mem.dynamic_arena_destroy(&arena) + testing.expect(t, arena.out_band_allocations == nil) +} + +@(test) +test_dynamic_arena_alloc_aligned :: proc(t: ^testing.T) { + expect_arena_allocation(t, expected_used_bytes = 16, num_bytes = 16, alignment=8) +} + +@(test) +test_dynamic_arena_alloc_unaligned :: proc(t: ^testing.T) { + expect_arena_allocation(t, expected_used_bytes = 8, num_bytes = 1, alignment = 8) + expect_arena_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8) +} + +@(test) +test_dynamic_arena_alloc_out_of_band :: proc(t: ^testing.T) { + expect_arena_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128) + expect_arena_allocation_out_of_band(t, num_bytes = 129, block_size = 512, out_band_size = 128) + expect_arena_allocation_out_of_band(t, num_bytes = 513, block_size = 512, out_band_size = 128) +} + +@(test) +test_intentional_leaks :: proc(t: ^testing.T) { + testing.expect_leaks(t, intentionally_leaky_test, leak_verifier) +} + +// Not tagged with @(test) because it's run through `test_intentional_leaks` +intentionally_leaky_test :: proc(t: ^testing.T) { + a: [dynamic]int + // Intentional leak + append(&a, 42) + + // Intentional bad free + b := uintptr(&a[0]) + 42 + free(rawptr(b)) +} + +leak_verifier :: proc(t: ^testing.T, ta: ^mem.Tracking_Allocator) { + testing.expect_value(t, len(ta.allocation_map), 1) + testing.expect_value(t, len(ta.bad_free_array), 1) +} diff --git a/tests/core/mem/test_mem_dynamic_pool.odin b/tests/core/mem/test_mem_dynamic_pool.odin deleted file mode 100644 index 82cee89af..000000000 --- a/tests/core/mem/test_mem_dynamic_pool.odin +++ /dev/null @@ -1,102 +0,0 @@ -package test_core_mem - -import "core:testing" -import "core:mem" - - -expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, alignment: int) { - pool: mem.Dynamic_Pool - mem.dynamic_pool_init(&pool, alignment = alignment) - pool_allocator := mem.dynamic_pool_allocator(&pool) - - element, err := mem.alloc(num_bytes, alignment, pool_allocator) - testing.expect(t, err == .None) - testing.expect(t, element != nil) - - expected_bytes_left := pool.block_size - expected_used_bytes - testing.expectf(t, pool.bytes_left == expected_bytes_left, - ` - Allocated data with size %v bytes, expected %v bytes left, got %v bytes left, off by %v bytes. - Pool: - block_size = %v - out_band_size = %v - alignment = %v - unused_blocks = %v - used_blocks = %v - out_band_allocations = %v - current_block = %v - current_pos = %v - bytes_left = %v - `, - num_bytes, expected_bytes_left, pool.bytes_left, expected_bytes_left - pool.bytes_left, - pool.block_size, - pool.out_band_size, - pool.alignment, - pool.unused_blocks, - pool.used_blocks, - pool.out_band_allocations, - pool.current_block, - pool.current_pos, - pool.bytes_left, - ) - - mem.dynamic_pool_destroy(&pool) - testing.expect(t, pool.used_blocks == nil) -} - -expect_pool_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, block_size, out_band_size: int) { - testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!") - - pool: mem.Dynamic_Pool - mem.dynamic_pool_init(&pool, block_size = block_size, out_band_size = out_band_size) - pool_allocator := mem.dynamic_pool_allocator(&pool) - - element, err := mem.alloc(num_bytes, allocator = pool_allocator) - testing.expect(t, err == .None) - testing.expect(t, element != nil) - testing.expectf(t, pool.out_band_allocations != nil, - "Allocated data with size %v bytes, which is >= out_of_band_size and it should be in pool.out_band_allocations, but isn't!", - ) - - mem.dynamic_pool_destroy(&pool) - testing.expect(t, pool.out_band_allocations == nil) -} - -@(test) -test_dynamic_pool_alloc_aligned :: proc(t: ^testing.T) { - expect_pool_allocation(t, expected_used_bytes = 16, num_bytes = 16, alignment=8) -} - -@(test) -test_dynamic_pool_alloc_unaligned :: proc(t: ^testing.T) { - expect_pool_allocation(t, expected_used_bytes = 8, num_bytes = 1, alignment = 8) - expect_pool_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8) -} - -@(test) -test_dynamic_pool_alloc_out_of_band :: proc(t: ^testing.T) { - expect_pool_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128) - expect_pool_allocation_out_of_band(t, num_bytes = 129, block_size = 512, out_band_size = 128) - expect_pool_allocation_out_of_band(t, num_bytes = 513, block_size = 512, out_band_size = 128) -} - -@(test) -test_intentional_leaks :: proc(t: ^testing.T) { - testing.expect_leaks(t, intentionally_leaky_test, leak_verifier) -} - -// Not tagged with @(test) because it's run through `test_intentional_leaks` -intentionally_leaky_test :: proc(t: ^testing.T) { - a: [dynamic]int - // Intentional leak - append(&a, 42) - - // Intentional bad free - b := uintptr(&a[0]) + 42 - free(rawptr(b)) -} - -leak_verifier :: proc(t: ^testing.T, ta: ^mem.Tracking_Allocator) { - testing.expect_value(t, len(ta.allocation_map), 1) - testing.expect_value(t, len(ta.bad_free_array), 1) -} diff --git a/vendor/raylib/rlgl/rlgl.odin b/vendor/raylib/rlgl/rlgl.odin index 14a7cf5b0..a08479680 100644 --- a/vendor/raylib/rlgl/rlgl.odin +++ b/vendor/raylib/rlgl/rlgl.odin @@ -431,17 +431,19 @@ foreign lib { EnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap DisableTextureCubemap :: proc() --- // Disable texture cubemap TextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) - CubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) + CubemapParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) // Shader state EnableShader :: proc(id: c.uint) --- // Enable shader program DisableShader :: proc() --- // Disable shader program // Framebuffer state - EnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) - DisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer - ActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers - BlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer + EnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) + DisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer + GetActiveFramebuffer :: proc() -> c.uint --- // Get the currently active render texture (fbo), 0 for default framebuffer + ActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers + BlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight: c.int, dstX, dstY, dstWidth, dstHeight: c.int, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer + BindFramebuffer :: proc(target, framebuffer: c.uint) --- // Bind framebuffer (FBO) // General render state EnableColorBlend :: proc() --- // Enable color blending @@ -452,12 +454,13 @@ foreign lib { DisableDepthMask :: proc() --- // Disable depth write EnableBackfaceCulling :: proc() --- // Enable backface culling DisableBackfaceCulling :: proc() --- // Disable backface culling + ColorMask :: proc(r, g, b, a: bool) --- // Color mask control SetCullFace :: proc(mode: CullMode) --- // Set face culling mode EnableScissorTest :: proc() --- // Enable scissor test DisableScissorTest :: proc() --- // Disable scissor test Scissor :: proc(x, y, width, height: c.int) --- // Scissor test EnableWireMode :: proc() --- // Enable wire mode - EnablePointMode :: proc() --- // Enable point mode + EnablePointMode :: proc() --- // Enable point mode DisableWireMode :: proc() --- // Disable wire and point modes SetLineWidth :: proc(width: f32) --- // Set the line drawing width GetLineWidth :: proc() -> f32 --- // Get the line drawing width @@ -503,7 +506,7 @@ foreign lib { DrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) SetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) DrawRenderBatchActive :: proc() --- // Update and draw internal render batch - CheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex + CheckRenderBatchLimit :: proc(vCount: c.int) -> bool --- // Check internal buffer overflow for a given number of vertex SetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits @@ -528,7 +531,7 @@ foreign lib { // Textures management LoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU LoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo) - LoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap + LoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture cubemap UpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data GetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats GetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format @@ -552,6 +555,7 @@ foreign lib { GetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute SetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform SetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix + SetUniformMatrices :: proc(locIndex: c.int, matrices: [^]Matrix, count: c.int) --- // Set shader value matrices SetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler SetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations) diff --git a/vendor/sdl3/sdl3_audio.odin b/vendor/sdl3/sdl3_audio.odin index 277aefde7..72afd8c98 100644 --- a/vendor/sdl3/sdl3_audio.odin +++ b/vendor/sdl3/sdl3_audio.odin @@ -38,14 +38,14 @@ AudioFormat :: enum c.int { F32 = F32LE when BYTEORDER == LIL_ENDIAN else F32BE, } -@(require_results) AUDIO_BITSIZE :: proc "c" (x: AudioFormat) -> Uint16 { return (Uint16(x) & AUDIO_MASK_BITSIZE) } -@(require_results) AUDIO_BYTESIZE :: proc "c" (x: AudioFormat) -> Uint16 { return AUDIO_BITSIZE(x) / 8 } -@(require_results) AUDIO_ISFLOAT :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_FLOAT) != 0 } -@(require_results) AUDIO_ISBIGENDIAN :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_BIG_ENDIAN) != 0 } -@(require_results) AUDIO_ISLITTLEENDIAN :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISBIGENDIAN(x) } -@(require_results) AUDIO_ISSIGNED :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_SIGNED) != 0 } -@(require_results) AUDIO_ISINT :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISFLOAT(x) } -@(require_results) AUDIO_ISUNSIGNED :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISSIGNED(x) } +@(require_results) AUDIO_BITSIZE :: #force_inline proc "c" (x: AudioFormat) -> Uint16 { return (Uint16(x) & AUDIO_MASK_BITSIZE) } +@(require_results) AUDIO_BYTESIZE :: #force_inline proc "c" (x: AudioFormat) -> Uint16 { return AUDIO_BITSIZE(x) / 8 } +@(require_results) AUDIO_ISFLOAT :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_FLOAT) != 0 } +@(require_results) AUDIO_ISBIGENDIAN :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_BIG_ENDIAN) != 0 } +@(require_results) AUDIO_ISLITTLEENDIAN :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISBIGENDIAN(x) } +@(require_results) AUDIO_ISSIGNED :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_SIGNED) != 0 } +@(require_results) AUDIO_ISINT :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISFLOAT(x) } +@(require_results) AUDIO_ISUNSIGNED :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISSIGNED(x) } AudioDeviceID :: distinct Uint32 @@ -60,7 +60,7 @@ AudioSpec :: struct { } @(require_results) -AUDIO_FRAMESIZE :: proc "c" (x: AudioSpec) -> c.int { +AUDIO_FRAMESIZE :: #force_inline proc "c" (x: AudioSpec) -> c.int { return c.int(AUDIO_BYTESIZE(x.format)) * x.channels } diff --git a/vendor/sdl3/sdl3_hints.odin b/vendor/sdl3/sdl3_hints.odin index ba7e55d3d..64fe486a1 100644 --- a/vendor/sdl3/sdl3_hints.odin +++ b/vendor/sdl3/sdl3_hints.odin @@ -20,6 +20,7 @@ HINT_AUDIO_DEVICE_APP_ICON_NAME :: "SDL_AUDIO_DEVICE_APP_ICON_NAME" HINT_AUDIO_DEVICE_SAMPLE_FRAMES :: "SDL_AUDIO_DEVICE_SAMPLE_FRAMES" HINT_AUDIO_DEVICE_STREAM_NAME :: "SDL_AUDIO_DEVICE_STREAM_NAME" HINT_AUDIO_DEVICE_STREAM_ROLE :: "SDL_AUDIO_DEVICE_STREAM_ROLE" +HINT_AUDIO_DEVICE_RAW_STREAM :: "SDL_AUDIO_DEVICE_RAW_STREAM" HINT_AUDIO_DISK_INPUT_FILE :: "SDL_AUDIO_DISK_INPUT_FILE" HINT_AUDIO_DISK_OUTPUT_FILE :: "SDL_AUDIO_DISK_OUTPUT_FILE" HINT_AUDIO_DISK_TIMESCALE :: "SDL_AUDIO_DISK_TIMESCALE" @@ -36,6 +37,7 @@ HINT_CPU_FEATURE_MASK :: "SDL_CPU_FEATURE_MASK" HINT_JOYSTICK_DIRECTINPUT :: "SDL_JOYSTICK_DIRECTINPUT" HINT_FILE_DIALOG_DRIVER :: "SDL_FILE_DIALOG_DRIVER" HINT_DISPLAY_USABLE_BOUNDS :: "SDL_DISPLAY_USABLE_BOUNDS" +HINT_INVALID_PARAM_CHECKS :: "SDL_INVALID_PARAM_CHECKS" HINT_EMSCRIPTEN_ASYNCIFY :: "SDL_EMSCRIPTEN_ASYNCIFY" HINT_EMSCRIPTEN_CANVAS_SELECTOR :: "SDL_EMSCRIPTEN_CANVAS_SELECTOR" HINT_EMSCRIPTEN_KEYBOARD_ELEMENT :: "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT" @@ -56,6 +58,7 @@ HINT_GDK_TEXTINPUT_MAX_LENGTH :: "SDL_GDK_TEXTINPUT_MAX_LENGTH" HINT_GDK_TEXTINPUT_SCOPE :: "SDL_GDK_TEXTINPUT_SCOPE" HINT_GDK_TEXTINPUT_TITLE :: "SDL_GDK_TEXTINPUT_TITLE" HINT_HIDAPI_LIBUSB :: "SDL_HIDAPI_LIBUSB" +HINT_HIDAPI_LIBUSB_GAMECUBE :: "SDL_HIDAPI_LIBUSB_GAMECUBE" HINT_HIDAPI_LIBUSB_WHITELIST :: "SDL_HIDAPI_LIBUSB_WHITELIST" HINT_HIDAPI_UDEV :: "SDL_HIDAPI_UDEV" HINT_GPU_DRIVER :: "SDL_GPU_DRIVER" @@ -247,6 +250,7 @@ HINT_WINDOWS_ENABLE_MENU_MNEMONICS :: "SDL_WINDOWS_ENABLE_MENU_MNEMONI HINT_WINDOWS_ENABLE_MESSAGELOOP :: "SDL_WINDOWS_ENABLE_MESSAGELOOP" HINT_WINDOWS_GAMEINPUT :: "SDL_WINDOWS_GAMEINPUT" HINT_WINDOWS_RAW_KEYBOARD :: "SDL_WINDOWS_RAW_KEYBOARD" +HINT_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS :: "SDL_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS" HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL :: "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL" HINT_WINDOWS_INTRESOURCE_ICON :: "SDL_WINDOWS_INTRESOURCE_ICON" HINT_WINDOWS_INTRESOURCE_ICON_SMALL :: "SDL_WINDOWS_INTRESOURCE_ICON_SMALL" diff --git a/vendor/sdl3/sdl3_main.odin b/vendor/sdl3/sdl3_main.odin index 103372bed..8c737b5b8 100644 --- a/vendor/sdl3/sdl3_main.odin +++ b/vendor/sdl3/sdl3_main.odin @@ -3,7 +3,7 @@ package sdl3 import "core:c" -main_func :: #type proc(argc: c.int, argv: [^]cstring) +main_func :: #type proc "c" (argc: c.int, argv: [^]cstring) -> c.int @(default_calling_convention="c", link_prefix="SDL_") foreign lib { diff --git a/vendor/sdl3/sdl3_mutex.odin b/vendor/sdl3/sdl3_mutex.odin index 8067473f3..7d48c0b5d 100644 --- a/vendor/sdl3/sdl3_mutex.odin +++ b/vendor/sdl3/sdl3_mutex.odin @@ -1,8 +1,24 @@ package sdl3 +import "core:c" + Mutex :: struct {} RWLock :: struct {} Semaphore :: struct {} +Condition :: struct {} + +InitStatus :: enum c.int { + UNINITIALIZED, + INITIALIZING, + INITIALIZED, + UNINITIALIZING, +} + +InitState :: struct { + status: AtomicInt, + thread: ThreadID, + reserved: rawptr, +} @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { @@ -27,4 +43,15 @@ foreign lib { TryWaitSemaphore :: proc(sem: ^Semaphore) -> bool --- WaitSemaphore :: proc(sem: ^Semaphore) --- WaitSemaphoreTimeout :: proc(sem: ^Semaphore, timeout_ms: Sint32) --- + + CreateCondition :: proc() -> ^Condition --- + DestroyCondition :: proc(cond: ^Condition) --- + SignalCondition :: proc(cond: ^Condition) --- + BroadcastCondition :: proc(cond: ^Condition) --- + WaitCondition :: proc(cond: ^Condition, mutex: ^Mutex) --- + WaitConditionTimeout :: proc(cond: ^Condition, mutex: ^Mutex, timeout_ms: Sint32) -> bool --- + + ShouldInit :: proc(state: ^InitState) -> bool --- + ShouldQuit :: proc(state: ^InitState) -> bool --- + SetInitialized :: proc(state: ^InitState, initialized: bool) --- } diff --git a/vendor/sdl3/sdl3_pixels.odin b/vendor/sdl3/sdl3_pixels.odin index e3a1c1537..af6541757 100644 --- a/vendor/sdl3/sdl3_pixels.odin +++ b/vendor/sdl3/sdl3_pixels.odin @@ -75,20 +75,20 @@ DEFINE_PIXELFORMAT :: #force_inline proc "c" (type: PixelType, order: PackedOrde return PixelFormat(((1 << 28) | (Uint32(type) << 24) | (Uint32(order) << 20) | (Uint32(layout) << 16) | (Uint32(bits) << 8) | (Uint32(bytes) << 0))) } -@(require_results) PIXELFLAG :: proc "c" (format: PixelFormat) -> Uint32 { return ((Uint32(format) >> 28) & 0x0F) } -@(require_results) PIXELTYPE :: proc "c" (format: PixelFormat) -> PixelType { return PixelType((Uint32(format) >> 24) & 0x0F) } -@(require_results) PIXELORDER :: proc "c" (format: PixelFormat) -> PackedOrder { return PackedOrder((Uint32(format) >> 20) & 0x0F) } -@(require_results) PIXELLAYOUT :: proc "c" (format: PixelFormat) -> PackedLayout { return PackedLayout((Uint32(format) >> 16) & 0x0F) } -@(require_results) PIXELARRAYORDER :: proc "c" (format: PixelFormat) -> ArrayOrder { return ArrayOrder((Uint32(format) >> 20) & 0x0F) } +@(require_results) PIXELFLAG :: #force_inline proc "c" (format: PixelFormat) -> Uint32 { return ((Uint32(format) >> 28) & 0x0F) } +@(require_results) PIXELTYPE :: #force_inline proc "c" (format: PixelFormat) -> PixelType { return PixelType((Uint32(format) >> 24) & 0x0F) } +@(require_results) PIXELORDER :: #force_inline proc "c" (format: PixelFormat) -> PackedOrder { return PackedOrder((Uint32(format) >> 20) & 0x0F) } +@(require_results) PIXELLAYOUT :: #force_inline proc "c" (format: PixelFormat) -> PackedLayout { return PackedLayout((Uint32(format) >> 16) & 0x0F) } +@(require_results) PIXELARRAYORDER :: #force_inline proc "c" (format: PixelFormat) -> ArrayOrder { return ArrayOrder((Uint32(format) >> 20) & 0x0F) } @(require_results) -BITSPERPIXEL :: proc "c" (format: PixelFormat) -> Uint32 { +BITSPERPIXEL :: #force_inline proc "c" (format: PixelFormat) -> Uint32 { return ISPIXELFORMAT_FOURCC(format) ? 0 : ((Uint32(format) >> 8) & 0xFF) } @(require_results) -ISPIXELFORMAT_INDEXED :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_INDEXED :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .INDEX1) || (PIXELTYPE(format) == .INDEX2) || @@ -98,7 +98,7 @@ ISPIXELFORMAT_INDEXED :: proc "c" (format: PixelFormat) -> bool { @(require_results) -ISPIXELFORMAT_PACKED :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_PACKED :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .PACKED8) || (PIXELTYPE(format) == .PACKED16) || @@ -106,7 +106,7 @@ ISPIXELFORMAT_PACKED :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_ARRAY :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_ARRAY :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .ARRAYU8) || (PIXELTYPE(format) == .ARRAYU16) || @@ -116,21 +116,21 @@ ISPIXELFORMAT_ARRAY :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_10BIT :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_10BIT :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .PACKED32) && (PIXELLAYOUT(format) == .LAYOUT_2101010))) } @(require_results) -ISPIXELFORMAT_FLOAT :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_FLOAT :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .ARRAYF16) || (PIXELTYPE(format) == .ARRAYF32))) } @(require_results) -ISPIXELFORMAT_ALPHA :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_ALPHA :: #force_inline proc "c" (format: PixelFormat) -> bool { return ((ISPIXELFORMAT_PACKED(format) && ((PIXELORDER(format) == .ARGB) || (PIXELORDER(format) == .RGBA) || @@ -144,7 +144,7 @@ ISPIXELFORMAT_ALPHA :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_FOURCC :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_FOURCC :: #force_inline proc "c" (format: PixelFormat) -> bool { return format != nil && PIXELFLAG(format) != 1 } @@ -369,63 +369,63 @@ ChromaLocation :: enum c.int { @(require_results) -DEFINE_COLORSPACE :: proc "c" (type: ColorType, range: ColorRange, primaries: ColorPrimaries, transfer: TransferCharacteristics, matrix_: MatrixCoefficients, chroma: ChromaLocation) -> Colorspace { +DEFINE_COLORSPACE :: #force_inline proc "c" (type: ColorType, range: ColorRange, primaries: ColorPrimaries, transfer: TransferCharacteristics, matrix_: MatrixCoefficients, chroma: ChromaLocation) -> Colorspace { return Colorspace((Uint32(type) << 28) | (Uint32(range) << 24) | (Uint32(chroma) << 20) | (Uint32(primaries) << 10) | (Uint32(transfer) << 5) | (Uint32(matrix_) << 0)) } @(require_results) -COLORSPACETYPE :: proc "c" (cspace: Colorspace) -> ColorType { +COLORSPACETYPE :: #force_inline proc "c" (cspace: Colorspace) -> ColorType { return ColorType((Uint32(cspace) >> 28) & 0x0F) } @(require_results) -COLORSPACERANGE :: proc "c" (cspace: Colorspace) -> ColorRange { +COLORSPACERANGE :: #force_inline proc "c" (cspace: Colorspace) -> ColorRange { return ColorRange((Uint32(cspace) >> 24) & 0x0F) } @(require_results) -COLORSPACECHROMA :: proc "c" (cspace: Colorspace) -> ChromaLocation { +COLORSPACECHROMA :: #force_inline proc "c" (cspace: Colorspace) -> ChromaLocation { return ChromaLocation((Uint32(cspace) >> 20) & 0x0F) } @(require_results) -COLORSPACEPRIMARIES :: proc "c" (cspace: Colorspace) -> ColorPrimaries { +COLORSPACEPRIMARIES :: #force_inline proc "c" (cspace: Colorspace) -> ColorPrimaries { return ColorPrimaries((Uint32(cspace) >> 10) & 0x1F) } @(require_results) -COLORSPACETRANSFER :: proc "c" (cspace: Colorspace) -> TransferCharacteristics { +COLORSPACETRANSFER :: #force_inline proc "c" (cspace: Colorspace) -> TransferCharacteristics { return TransferCharacteristics((Uint32(cspace) >> 5) & 0x1F) } @(require_results) -COLORSPACEMATRIX :: proc "c" (cspace: Colorspace) -> MatrixCoefficients { +COLORSPACEMATRIX :: #force_inline proc "c" (cspace: Colorspace) -> MatrixCoefficients { return MatrixCoefficients(Uint32(cspace) & 0x1F) } @(require_results) -ISCOLORSPACE_MATRIX_BT601 :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT601 :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT601 || COLORSPACEMATRIX(cspace) == .BT470BG } @(require_results) -ISCOLORSPACE_MATRIX_BT709 :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT709 :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT709 } @(require_results) -ISCOLORSPACE_MATRIX_BT2020_NCL :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT2020_NCL :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT2020_NCL } @(require_results) -ISCOLORSPACE_LIMITED_RANGE :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_LIMITED_RANGE :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACERANGE(cspace) != .FULL } @(require_results) -ISCOLORSPACE_FULL_RANGE :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_FULL_RANGE :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACERANGE(cspace) == .FULL } diff --git a/vendor/sdl3/sdl3_rect.odin b/vendor/sdl3/sdl3_rect.odin index 6af9dc937..be082119e 100644 --- a/vendor/sdl3/sdl3_rect.odin +++ b/vendor/sdl3/sdl3_rect.odin @@ -24,38 +24,51 @@ RectToFRect :: #force_inline proc "c" (rect: Rect, frect: ^FRect) { @(require_results) -PointInRect :: proc "c" (p: Point, r: Rect) -> bool { +PointInRect :: #force_inline proc "c" (p: Point, r: Rect) -> bool { return ( (p.x >= r.x) && (p.x < (r.x + r.w)) && (p.y >= r.y) && (p.y < (r.y + r.h)) ) } @(require_results) -PointInRectFloat :: proc "c" (p: FPoint, r: FRect) -> bool { +PointInRectFloat :: #force_inline proc "c" (p: FPoint, r: FRect) -> bool { return ( (p.x >= r.x) && (p.x <= (r.x + r.w)) && (p.y >= r.y) && (p.y <= (r.y + r.h)) ) } @(require_results) -RectEmpty :: proc "c" (r: Rect) -> bool { +RectEmpty :: #force_inline proc "c" (r: Rect) -> bool { return r.w <= 0 || r.h <= 0 } @(require_results) -RectEqual :: proc "c" (a, b: Rect) -> bool { +RectEqual :: #force_inline proc "c" (a, b: Rect) -> bool { return a == b } +@(require_results) +RectsEqualEpsilon :: #force_inline proc "c" (a, b: FRect, epsilon: f32) -> bool { + return abs(a.x - b.x) <= epsilon && + abs(a.y - b.y) <= epsilon && + abs(a.w - b.w) <= epsilon && + abs(a.h - b.h) <= epsilon +} + +@(require_results) +RectsEqualFloat :: #force_inline proc "c" (a, b: FRect) -> bool { + return RectsEqualEpsilon(a, b, FLT_EPSILON) +} + @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { HasRectIntersection :: proc(#by_ptr A, B: Rect) -> bool --- GetRectIntersection :: proc(#by_ptr A, B: Rect, result: ^Rect) -> bool --- GetRectUnion :: proc(#by_ptr A, B: Rect, result: ^Rect) -> bool --- - GetRectEnclosingPoints :: proc(points: [^]Point, count: c.int, #by_ptr clip: Rect, result: ^Rect) -> bool --- + GetRectEnclosingPoints :: proc(points: [^]Point, count: c.int, clip: Maybe(^Rect), result: ^Rect) -> bool --- GetRectAndLineIntersection :: proc(#by_ptr rect: Rect, X1, Y1, X2, Y2: ^c.int) -> bool --- HasRectIntersectionFloat :: proc(#by_ptr A, B: FRect) -> bool --- GetRectIntersectionFloat :: proc(#by_ptr A, B: FRect, result: ^FRect) -> bool --- GetRectUnionFloat :: proc(#by_ptr A, B: FRect, result: ^FRect) -> bool --- - GetRectEnclosingPointsFloat :: proc(points: [^]FPoint, count: c.int, #by_ptr clip: FRect, result: ^FRect) -> bool --- + GetRectEnclosingPointsFloat :: proc(points: [^]FPoint, count: c.int, clip: Maybe(^FRect), result: ^FRect) -> bool --- GetRectAndLineIntersectionFloat :: proc(#by_ptr rect: FRect, X1, Y1, X2, Y2: ^f32) -> bool --- } \ No newline at end of file diff --git a/vendor/sdl3/sdl3_render.odin b/vendor/sdl3/sdl3_render.odin index fd5b0705e..759683dfe 100644 --- a/vendor/sdl3/sdl3_render.odin +++ b/vendor/sdl3/sdl3_render.odin @@ -240,8 +240,8 @@ foreign lib { RenderTextureTiled :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), scale: f32, dstrect: Maybe(^FRect)) -> bool --- RenderTexture9Grid :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), left_width, right_width, top_height, bottom_height: f32, scale: f32, dstrect: Maybe(^FRect)) -> bool --- RenderTexture9GridTiled :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), left_width, right_width, top_height, bottom_height: f32, scale: f32, dstrect: Maybe(^FRect), tileScale: f32) -> bool --- - RenderGeometry :: proc(renderer: ^Renderer, texture: ^Texture, vertices: [^]Vertex, num_vertices: c.int, indices: [^]c.int, num_indices: c.int) -> bool --- - RenderGeometryRaw :: proc(renderer: ^Renderer, texture: ^Texture, xy: [^]f32, xy_stride: c.int, color: [^]FColor, color_stride: c.int, uv: [^]f32, uv_stride: c.int, num_vertices: c.int, indices: rawptr, num_indices: c.int, size_indices: c.int) -> bool --- + RenderGeometry :: proc(renderer: ^Renderer, texture: Maybe(^Texture), vertices: [^]Vertex, num_vertices: c.int, indices: [^]c.int, num_indices: c.int) -> bool --- + RenderGeometryRaw :: proc(renderer: ^Renderer, texture: Maybe(^Texture), xy: [^]f32, xy_stride: c.int, color: [^]FColor, color_stride: c.int, uv: [^]f32, uv_stride: c.int, num_vertices: c.int, indices: rawptr, num_indices: c.int, size_indices: c.int) -> bool --- SetRenderTextureAddressMode :: proc(renderer: ^Renderer, u_mode, v_mode: TextureAddressMode) -> bool --- GetRenderTextureAddressMode :: proc(renderer: ^Renderer, u_mode, v_mode: ^TextureAddressMode) -> bool --- RenderPresent :: proc(renderer: ^Renderer) -> bool --- @@ -276,5 +276,5 @@ foreign lib { CreateGPURenderState :: proc(renderer: ^Renderer, #by_ptr createinfo: GPURenderStateCreateInfo) -> ^GPURenderState --- SetGPURenderStateFragmentUniforms :: proc(state: ^GPURenderState, slot_index: Uint32, data: rawptr, length: Uint32) -> bool --- SetGPURenderState :: proc(renderer: ^Renderer, state: ^GPURenderState) -> bool --- - DestroyGPURenderState :: proc(renderer: ^Renderer) --- + DestroyGPURenderState :: proc(renderer: ^GPURenderState) --- } diff --git a/vendor/sdl3/sdl3_system.odin b/vendor/sdl3/sdl3_system.odin index 44c026e82..63b0637d7 100644 --- a/vendor/sdl3/sdl3_system.odin +++ b/vendor/sdl3/sdl3_system.odin @@ -6,7 +6,7 @@ import "core:c" import win32 "core:sys/windows" -WindowsMessageHook :: #type proc(userdata: rawptr, msg: ^win32.MSG) -> bool +WindowsMessageHook :: #type proc "c" (userdata: rawptr, msg: ^win32.MSG) -> bool @(default_calling_convention="c", link_prefix="SDL_") foreign lib { @@ -46,6 +46,11 @@ foreign lib { RequestAndroidPermissionCallback :: #type proc "c" (userdata: rawptr, permission: cstring, granted: bool) +AndroidExternalStorageFlags :: distinct bit_set[AndroidExternalStorageFlag; Uint32] +AndroidExternalStorageFlag :: enum Uint32 { + Read, + Write, +} @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { @@ -56,7 +61,7 @@ foreign lib { IsDeXMode :: proc() -> bool --- SendAndroidBackButton :: proc() --- GetAndroidInternalStoragePath :: proc() -> cstring --- - GetAndroidExternalStorageState :: proc() -> Uint32 --- + GetAndroidExternalStorageState :: proc() -> AndroidExternalStorageFlags --- GetAndroidExternalStoragePath :: proc() -> cstring --- GetAndroidCachePath :: proc() -> cstring --- RequestAndroidPermission :: proc(permission: cstring, cb: RequestAndroidPermissionCallback, userdata: rawptr) -> bool --- diff --git a/vendor/sdl3/sdl3_video.odin b/vendor/sdl3/sdl3_video.odin index e4547d991..3ddcfedc6 100644 --- a/vendor/sdl3/sdl3_video.odin +++ b/vendor/sdl3/sdl3_video.odin @@ -151,7 +151,7 @@ EGLSurface :: distinct rawptr EGLAttrib :: distinct uintptr EGLint :: distinct c.int -EGLAttribArrayCallback :: #type proc "c" (userdata: rawptr) -> ^EGLint +EGLAttribArrayCallback :: #type proc "c" (userdata: rawptr) -> [^]EGLint EGLIntArrayCallback :: #type proc "c" (userdata: rawptr, display: EGLDisplay, config: EGLConfig) -> [^]EGLint GLAttr :: enum c.int { @@ -256,9 +256,12 @@ GL_CONTEXT_RESET_LOSE_CONTEXT :: GLContextResetNotification{.LOSE_CONTEXT} PROP_DISPLAY_HDR_ENABLED_BOOLEAN :: "SDL.display.HDR_enabled" PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER :: "SDL.display.KMSDRM.panel_orientation" +PROP_DISPLAY_WAYLAND_WL_OUTPUT_POINTER :: "SDL.display.wayland.wl_output" +PROP_DISPLAY_WINDOWS_HMONITOR_POINTER :: "SDL.display.windows.hmonitor" PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN :: "SDL.window.create.always_on_top" PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN :: "SDL.window.create.borderless" +PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN :: "SDL.window.create.constrain_popup" PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN :: "SDL.window.create.focusable" PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN :: "SDL.window.create.external_graphics_context" PROP_WINDOW_CREATE_FLAGS_NUMBER :: "SDL.window.create.flags" diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index d88fafda1..9d8674941 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -111,7 +111,7 @@ pack_range :: struct { first_unicode_codepoint_in_range: c.int, array_of_unicode_codepoints: [^]rune, num_chars: c.int, - chardata_for_range: ^packedchar, + chardata_for_range: [^]packedchar, _, _: u8, // used internally to store oversample info } @@ -138,7 +138,7 @@ foreign stbtt { // bilinear filtering). // // Returns 0 on failure, 1 on success. - PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> c.int --- + PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> b32 --- // Cleans up the packing context and frees all memory. PackEnd :: proc(spc: ^pack_context) --- @@ -155,13 +155,17 @@ foreign stbtt { // and pass that result as 'font_size': // ..., 20 , ... // font max minus min y is 20 pixels tall // ..., POINT_SIZE(20), ... // 'M' is 20 pixels tall - PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: ^packedchar) -> c.int --- + // + // Returns 0 on failure, 1 on success. + PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: [^]packedchar) -> b32 --- // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. Note that you can call this multiple // times within a single PackBegin/PackEnd. - PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> c.int --- + // + // Returns 0 on failure, 1 on success. + PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> b32 --- // Oversampling a font increases the quality by allowing higher-quality subpixel // positioning, and is especially valuable at smaller text sizes. @@ -201,9 +205,13 @@ foreign stbtt { // then call RenderIntoRects repeatedly. This may result in a // better packing than calling PackFontRanges multiple times // (or it may not). - PackFontRangesGatherRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- + // + // Returns the number of rects which were not skipped. + // PackSetSkipMissingCodepoints controls this behavior. + PackFontRangesGatherRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: [^]pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- PackFontRangesPackRects :: proc(spc: ^pack_context, rects: [^]stbrp.Rect, num_rects: c.int) --- - PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- + // Returns 0 on failure, 1 on success. + PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: [^]pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> b32 --- } //////////////////////////////////////////////////////////////////////////////