Merge branch 'master' into bill/rexcode

This commit is contained in:
gingerBill
2026-06-23 13:21:46 +01:00
41 changed files with 1084 additions and 372 deletions

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
------------ ------------------------
<positional> depends on struct layout
-<flag> set a bool true
-<flag:option> set flag to option
-<flag=option> set flag to option, alternative syntax
-<map>:<key>=<value> set map[key] to value
type handling
------------ ------------------------
<positional> depends on struct layout
-<flag> set a bool true
-<flag:option> set flag to option
-<flag=option> set flag to option, alternative syntax
-<flag:option1,opt...> set bit_set flag to one or more options
-<flag=option1,opt...> set bit_set flag to one or more options, alternative syntax
-<map>:<key>=<value> 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:

View File

@@ -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."`,

View File

@@ -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..<uint(len(str)) {
if bit_index == max_bit_index {
if bit_index == bit_index_limit {
// The string's too long for this bit_set.
return false
}
@@ -421,7 +422,7 @@ parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runt
}
} else {
parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)
if error != nil {
// So far, it's none of the types that we recognize.
// Check to see if we can set it by base type, if allowed.
@@ -471,6 +472,70 @@ parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runt
}
}
case runtime.Type_Info_Bit_Set:
if str[0] == '0' || str[0] == '1' {
if !parse_and_set_pointer_by_base_type(ptr, str, type_info) {
return Parse_Error {
// The caller will add more details.
.Bad_Value,
"",
}
} else {
error = nil
return
}
}
value: u128
et := runtime.type_info_base(specific_type_info.elem)
if enum_type_info, is_enum := et.variant.(runtime.Type_Info_Enum); is_enum {
names, _ := strings.split(str, ",", context.temp_allocator)
valid_names := enum_type_info.names
underlying_values := enum_type_info.values
#no_bounds_check outer_loop: for name in names {
found: bool
#no_bounds_check for valid_name, index in valid_names {
if name == valid_name {
shift := u128(underlying_values[index]) - u128(specific_type_info.lower)
value |= u128(1 << shift)
found = true
continue outer_loop
}
}
if !found {
return Parse_Error {
.Bad_Value,
fmt.tprintf(
"Invalid value name: `%s`. Valid names are: %s",
name,
valid_names,
),
}
}
}
} else {
return Parse_Error {
// The caller will add more details.
.Bad_Value,
"",
}
}
if specific_type_info.underlying != nil {
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
} else {
switch 8*type_info.size {
case 8: (^u8) (ptr)^ = cast(u8) value
case 16: (^u16) (ptr)^ = cast(u16) value
case 32: (^u32) (ptr)^ = cast(u32) value
case 64: (^u64) (ptr)^ = cast(u64) value
case 128: (^u128)(ptr)^ = value
}
}
error = nil
case:
if type_info.id == ^os.File {
parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)

68
core/log/doc.odin Normal file
View File

@@ -0,0 +1,68 @@
/*
Implementation of logging facilities.
Odin has builtin support for logging using procedure `context`. After a logger is created it can then be assigned to
`context.logger` and used implicitly in future log calls.
While it is ok for simple apps to use the `core:fmt` package, libraries and complex apps should prefer the `core:log`
package. By using the implicit logger library and application authors allow the caller to decide how to process log
messages.
When starting out you can easily just init the logger with a single line.
Example:
package main
import "core:log"
main :: proc() {
context.logger = log.create_console_logger()
log.info("Hello World!")
}
However when the application gets more involved you might want to try a more complex setup.
Example:
package main
import "core:log"
import "core:os"
main :: proc() {
handle, err := os.open("logs.txt", os.O_RDWR | os.O_APPEND | os.O_CREATE, 0o666)
assert(err == nil, "Cannot open log file")
file_logger := log.create_file_logger(handle)
// This closes the file handle
defer log.destroy_file_logger(file_logger)
console_logger := log.create_console_logger()
defer log.destroy_console_logger(console_logger)
multi_logger := log.create_multi_logger(console_logger, file_logger)
defer log.destroy_multi_logger(multi_logger)
context.logger = multi_logger
log.info("Application started!")
}
It is also possible to create an allocator that logs all allocations.
Example:
package main
import "core:log"
main :: proc() {
context.logger = log.create_console_logger()
alloc: log.Log_Allocator
log.log_allocator_init(&alloc, .Debug)
context.allocator = log.log_allocator(&alloc)
a := new(i32)
free(a)
}
*/
package log

View File

@@ -11,6 +11,7 @@ import "core:terminal"
import "core:terminal/ansi"
import "core:time"
// Strings to output when `.Level` is included in the logger options.
Level_Headers := [?]string{
0..<10 = "[DEBUG] --- ",
10..<20 = "[INFO ] --- ",
@@ -19,22 +20,49 @@ Level_Headers := [?]string{
40..<50 = "[FATAL] --- ",
}
/*
The default option set for a console logger.
It is similar to the file logger default option set, but the output includes colors.
When you use this set of options you can expect the following output:
[LEVEL] --- [YYYY-MM-DD HH:MM:SS] [file.odin:L:proc()] Message
For example:
[INFO ] --- [2025-01-02 12:34:56] [main.odin:8:main()] Hello World!
*/
Default_Console_Logger_Opts :: Options{
.Level,
.Terminal_Color,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts
} + Full_Timestamp_Opts
/*
The default option set for a file logger.
It is similar to the console logger default option set, but the output is not colored.
When you use this set of options you can expect the following output:
[LEVEL] --- [YYYY-MM-DD HH:MM:SS] [file.odin:L:proc()] Message
For example:
[INFO ] --- [2025-01-02 12:34:56] [main.odin:8:main()] Hello World!
*/
Default_File_Logger_Opts :: Options{
.Level,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts
} + Full_Timestamp_Opts
//Data backing a file or console logger.
File_Console_Logger_Data :: struct {
file_handle: ^os.File,
ident: string,
@@ -67,6 +95,21 @@ init_standard_stream_status :: proc "contextless" () {
}
}
/*
Create a logger that outputs to a file.
*Allocates Using Provided Allocator*
When no longer needed can be destroyed with `destroy_file_logger`.
Inputs:
- `h`: A handle to the output file
- `lowest`: Log level to use (default is `.Debug`)
- `opt`: Specifies additional data present in the log output (default is `log.Default_File_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_file_logger :: proc(f: ^os.File, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> 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

View File

@@ -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 {

View File

@@ -6,6 +6,7 @@ import "base:runtime"
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 {
// 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, // Wrapped allocator
level: Level,
prefix: string,
lock: sync.Mutex,
level: Level, // Log Level used for allocations
prefix: string, // Prefix to use in log messages
lock: sync.Mutex,
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
la.level = level
@@ -31,7 +42,15 @@ log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_All
}
/*
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,
data = la,
@@ -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) {
la := (^Log_Allocator)(allocator_data)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

13
core/mem/mem_posix.odin Normal file
View File

@@ -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)
}

16
core/mem/mem_windows.odin Normal file
View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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))

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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[:])

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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) ---
}

View File

@@ -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
}

View File

@@ -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 ---
}

View File

@@ -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) ---
}

View File

@@ -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 ---

View File

@@ -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"

View File

@@ -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 ---
}
//////////////////////////////////////////////////////////////////////////////