diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc2691d80..5fb98fca4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,6 +163,13 @@ jobs: cd tests\internal call build.bat timeout-minutes: 10 + - name: Odin documentation tests + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + cd tests\documentation + call build.bat + timeout-minutes: 10 - name: core:math/big tests shell: cmd run: | diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin index dc9bfcb06..211db9770 100644 --- a/core/builtin/builtin.odin +++ b/core/builtin/builtin.odin @@ -94,7 +94,15 @@ cap :: proc(array: Array_Type) -> int --- size_of :: proc($T: typeid) -> int --- align_of :: proc($T: typeid) -> int --- -offset_of :: proc($T: typeid) -> uintptr --- + +// e.g. offset_of(t.f), where t is an instance of the type T +offset_of_selector :: proc(selector: $T) -> uintptr --- +// e.g. offset_of(T, f), where T can be the type instead of a variable +offset_of_member :: proc($T: typeid, member: $M) -> uintptr --- +offset_of :: proc{offset_of_selector, offset_of_member} +// e.g. offset_of(T, "f"), where T can be the type instead of a variable +offset_of_by_string :: proc($T: typeid, member: string) -> uintptr --- + type_of :: proc(x: expr) -> type --- type_info_of :: proc($T: typeid) -> ^runtime.Type_Info --- typeid_of :: proc($T: typeid) -> typeid --- diff --git a/core/crypto/util/util.odin b/core/crypto/util/util.odin index 6273a232e..b9b80124a 100644 --- a/core/crypto/util/util.odin +++ b/core/crypto/util/util.odin @@ -11,6 +11,8 @@ package util */ import "core:mem" +// Keep vet happy +_ :: mem // @note(bp): this can replace the other two cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D { diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin new file mode 100644 index 000000000..37dab8758 --- /dev/null +++ b/core/dynlib/lib_js.odin @@ -0,0 +1,15 @@ +//+build js +//+private +package dynlib + +_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { + return +} + +_unload_library :: proc(library: Library) -> bool { + return +} + +_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { + return +} diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 468774aa9..089fd9c9b 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -87,7 +87,8 @@ Error :: enum { -destroy_value :: proc(value: Value) { +destroy_value :: proc(value: Value, allocator := context.allocator) { + context.allocator = allocator #partial switch v in value { case Object: for key, elem in v { @@ -103,5 +104,4 @@ destroy_value :: proc(value: Value) { case String: delete(v) } -} - +} \ No newline at end of file diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin index 668fc9bc6..991058661 100644 --- a/core/fmt/doc.odin +++ b/core/fmt/doc.odin @@ -68,7 +68,7 @@ A period with no following number specifies a precision of 0. Examples: %f default width, default precision %8f width 8, default precision - %.3f default width, precision 2 + %.2f default width, precision 2 %8.3f width 8, precision 3 %8.f width 8, precision 0 diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index c52869daa..8a5f54516 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -44,6 +44,31 @@ Info :: struct { // Custom formatter signature. It returns true if the formatting was successful and false when it could not be done User_Formatter :: #type proc(fi: ^Info, arg: any, verb: rune) -> bool +// Example User Formatter: +// SomeType :: struct { +// value: int, +// } +// // Custom Formatter for SomeType +// User_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool { +// m := cast(^SomeType)arg.data +// switch verb { +// case 'v', 'd': +// fmt.fmt_int(fi, u64(m.value), true, 8 * size_of(SomeType), verb) +// case: +// return false +// } +// return true +// } +// main :: proc() { +// // Ensure the fmt._user_formatters map is initialized +// fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter)) +// err := fmt.register_user_formatter(type_info_of(SomeType).id, User_Formatter) +// assert(err == .None) +// // Use the custom formatter +// x := SomeType{42} +// fmt.println("Custom type value: ", x) +// } + Register_User_Formatter_Error :: enum { None, No_User_Formatter, @@ -54,13 +79,27 @@ Register_User_Formatter_Error :: enum { // it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary _user_formatters: ^map[typeid]User_Formatter -// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific -// types +// Sets user-defined formatters for custom print formatting of specific types +// +// Inputs: +// - m: A pointer to a map of typeids to User_Formatter structs. +// +// NOTE: Must be called before using register_user_formatter. +// set_user_formatters :: proc(m: ^map[typeid]User_Formatter) { - _user_formatters = m + assert(_user_formatters == nil, "set_user_formatters must not be called more than once.") + _user_formatters = m } -// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called -// before any use of this procedure. +// Registers a user-defined formatter for a specific typeid +// +// Inputs: +// - id: The typeid of the custom type. +// - formatter: The User_Formatter function for the custom type. +// +// Returns: A Register_User_Formatter_Error value indicating the success or failure of the operation. +// +// WARNING: set_user_formatters must be called before using this procedure. +// register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error { if _user_formatters == nil { return .No_User_Formatter @@ -71,75 +110,151 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist _user_formatters[id] = formatter return .None } - - -// aprint procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string. +// aprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// aprintln procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string with a newline character at the end +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string with a newline character at the end. +// aprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// aprintf procedure return a string that was allocated with the current context -// They must be freed accordingly +// Creates a formatted string using a format string and arguments +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// +// Returns: A formatted string. The returned string must be freed accordingly. +// aprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder strings.builder_init(&str) sbprintf(&str, fmt, ..args) return strings.to_string(str) } - - -// tprint procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string. +// tprint :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprint(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// tprintln procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string with a newline character at the end +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - args: A variadic list of arguments to be formatted. +// - sep: An optional separator string (default is a single space). +// +// Returns: A formatted string with a newline character at the end. +// tprintln :: proc(args: ..any, sep := " ") -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintln(buf=&str, args=args, sep=sep) return strings.to_string(str) } -// tprintf procedure return a string that was allocated with the current context's temporary allocator +// Creates a formatted string using a format string and arguments +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments. +// - args: A variadic list of arguments to be formatted. +// +// Returns: A formatted string. +// tprintf :: proc(fmt: string, args: ..any) -> string { str: strings.Builder strings.builder_init(&str, context.temp_allocator) sbprintf(&str, fmt, ..args) return strings.to_string(str) } - - -// bprint procedures return a string using a buffer from an array +// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. +// +// Inputs: +// - buf: The backing buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string +// bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprint(buf=&sb, args=args, sep=sep) } -// bprintln procedures return a string using a buffer from an array +// Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer. +// +// Inputs: +// - buf: The backing buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string with a newline character at the end +// bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintln(buf=&sb, args=args, sep=sep) } -// bprintf procedures return a string using a buffer from an array +// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer. +// +// Inputs: +// - buf: The backing buffer +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted string +// bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string { sb := strings.builder_from_bytes(buf[0:len(buf)]) return sbprintf(&sb, fmt, ..args) } - - -// formatted assert +// Runtime assertion with a formatted message +// +// Inputs: +// - condition: The boolean condition to be asserted +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// - loc: The location of the caller +// +// Returns: True if the condition is met, otherwise triggers a runtime assertion with a formatted message +// assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool { if !condition { p := context.assertion_failure_proc @@ -151,8 +266,13 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati } return condition } - -// formatted panic +// Runtime panic with a formatted message +// +// Inputs: +// - fmt: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// - loc: The location of the caller +// panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { p := context.assertion_failure_proc if p == nil { @@ -161,8 +281,16 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! { message := tprintf(fmt, ..args) p("Panic", message, loc) } - -// formatted printing for cstrings +// Creates a formatted C string +// +// *Allocates Using Context's Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted C string +// caprintf :: proc(format: string, args: ..any) -> cstring { str: strings.Builder strings.builder_init(&str) @@ -171,8 +299,16 @@ caprintf :: proc(format: string, args: ..any) -> cstring { s := strings.to_string(str) return cstring(raw_data(s)) } - -// c string with temp allocator +// Creates a formatted C string +// +// *Allocates Using Context's Temporary Allocator* +// +// Inputs: +// - format: A format string with placeholders for the provided arguments +// - args: A variadic list of arguments to be formatted +// +// Returns: A formatted C string +// ctprintf :: proc(format: string, args: ..any) -> cstring { str: strings.Builder strings.builder_init(&str, context.temp_allocator) @@ -181,27 +317,54 @@ ctprintf :: proc(format: string, args: ..any) -> cstring { s := strings.to_string(str) return cstring(raw_data(s)) } - -// sbprint formats using the default print settings and writes to buf +// Formats using the default print settings and writes to the given strings.Builder +// +// Inputs: +// - buf: A pointer to a strings.Builder to store the formatted string +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: A formatted string +// sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprint(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } - -// sbprintln formats using the default print settings and writes to buf +// Formats and writes to a strings.Builder buffer using the default print settings +// +// Inputs: +// - buf: A pointer to a strings.Builder buffer +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The resulting formatted string +// sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { wprintln(w=strings.to_writer(buf), args=args, sep=sep) return strings.to_string(buf^) } - -// sbprintf formats according to the specififed format string and writes to buf +// Formats and writes to a strings.Builder buffer according to the specified format string +// +// Inputs: +// - buf: A pointer to a strings.Builder buffer +// - fmt: The format string +// - args: A variadic list of arguments to be formatted +// +// Returns: The resulting formatted string +// sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string { wprintf(w=strings.to_writer(buf), fmt=fmt, args=args) return strings.to_string(buf^) } - - -// wprint formats using the default print settings and writes to w +// Formats and writes to an io.Writer using the default print settings +// +// Inputs: +// - w: An io.Writer to write to +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The number of bytes written +// wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w @@ -232,8 +395,15 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int { return fi.n } - -// wprintln formats using the default print settings and writes to w +// Formats and writes to an io.Writer using the default print settings with a newline character at the end +// +// Inputs: +// - w: An io.Writer to write to +// - args: A variadic list of arguments to be formatted +// - sep: An optional separator string (default is a single space) +// +// Returns: The number of bytes written +// wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { fi: Info fi.writer = w @@ -249,8 +419,15 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int { io.flush(auto_cast w) return fi.n } - -// wprintf formats according to the specififed format string and writes to w +// Formats and writes to an io.Writer according to the specified format string +// +// Inputs: +// - w: An io.Writer to write to +// - fmt: The format string +// - args: A variadic list of arguments to be formatted +// +// Returns: The number of bytes written +// wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { fi: Info arg_index: int = 0 @@ -526,23 +703,43 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int { return fi.n } - -// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w +// Writes a ^runtime.Type_Info value to an io.Writer +// +// Inputs: +// - w: An io.Writer to write to +// - info: A pointer to a runtime.Type_Info value +// +// Returns: The number of bytes written and an io.Error if encountered +// wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) { n, err := reflect.write_type(w, info) io.flush(auto_cast w) return n, err } -// wprint_typeid is a utility procedure to write a typeid value to w +// Writes a typeid value to an io.Writer +// +// Inputs: +// - w: An io.Writer to write to +// - id: A typeid value +// +// Returns: The number of bytes written and an io.Error if encountered +// wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) { n, err := reflect.write_type(w, type_info_of(id)) io.flush(auto_cast w) return n, err } - - - - +// Parses an integer from a given string starting at a specified offset +// +// Inputs: +// - s: The string to parse the integer from +// - offset: The position in the string to start parsing the integer +// +// Returns: +// - result: The parsed integer +// - new_offset: The position in the string after parsing the integer +// - ok: A boolean indicating if the parsing was successful +// _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) { is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' } @@ -560,7 +757,20 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: ok = new_offset > offset return } - +// Parses an argument number from a format string and determines if it's valid +// +// Inputs: +// - fi: A pointer to an Info structure +// - arg_index: The current argument index +// - format: The format string to parse +// - offset: The current position in the format string +// - arg_count: The total number of arguments +// +// Returns: +// - index: The parsed argument index +// - new_offset: The new position in the format string +// - ok: A boolean indicating if the parsed argument number is valid +// _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) { parse_arg_number :: proc(format: string) -> (int, int, bool) { if len(format) < 3 { @@ -594,7 +804,17 @@ _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count fi.good_arg_index = false return arg_index, offset+width, false } - +// Retrieves an integer from a list of any type at the specified index +// +// Inputs: +// - args: A list of values of any type +// - arg_index: The index to retrieve the integer from +// +// Returns: +// - int: The integer value at the specified index +// - new_arg_index: The new argument index +// - ok: A boolean indicating if the conversion to integer was successful +// int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { num := 0 new_arg_index := arg_index @@ -609,8 +829,12 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) { return num, new_arg_index, ok } - - +// Writes a bad verb error message +// +// Inputs: +// - fi: A pointer to an Info structure +// - verb: The invalid format verb +// fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { prev_in_bad := fi.in_bad defer fi.in_bad = prev_in_bad @@ -628,7 +852,13 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { } io.write_byte(writer, ')', &fi.n) } - +// Formats a boolean value according to the specified format verb +// +// Inputs: +// - fi: A pointer to an Info structure +// - b: The boolean value to format +// - verb: The format verb +// fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { switch verb { case 't', 'v': @@ -637,8 +867,12 @@ fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { fmt_bad_verb(fi, verb) } } - - +// Writes padding characters for formatting +// +// Inputs: +// - fi: A pointer to an Info structure +// - width: The number of padding characters to write +// fmt_write_padding :: proc(fi: ^Info, width: int) { if width <= 0 { return @@ -653,7 +887,18 @@ fmt_write_padding :: proc(fi: ^Info, width: int) { io.write_byte(fi.writer, pad_byte, &fi.n) } } - +// Formats an integer value with specified base, sign, bit size, and digits +// +// Inputs: +// - fi: A pointer to an Info structure +// - u: The integer value to format +// - base: The base for integer formatting +// - is_signed: A boolean indicating if the integer is signed +// - bit_size: The bit size of the integer +// - digits: A string containing the digits for formatting +// +// WARNING: May panic if the width and precision are too big, causing a buffer overrun +// _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) { _, neg := strconv.is_integer_negative(u, is_signed, bit_size) @@ -718,7 +963,18 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d fi.zero = false _pad(fi, s) } - +// Formats an int128 value based on the provided formatting options. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The int128 value to be formatted. +// - base: The base to be used for formatting the integer (e.g. 2, 8, 10, 12, 16). +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 64, 128). +// - digits: A string containing the digit characters to use for the formatted integer. +// +// WARNING: Panics if the formatting options result in a buffer overrun. +// _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: int, digits: string) { _, neg := strconv.is_integer_negative_128(u, is_signed, bit_size) @@ -783,10 +1039,16 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i fi.zero = false _pad(fi, s) } - +// Hex Values: __DIGITS_LOWER := "0123456789abcdefx" __DIGITS_UPPER := "0123456789ABCDEFX" - +// Formats a rune value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - r: The rune value to be formatted. +// - verb: The formatting verb to use (e.g. 'c', 'r', 'v', 'q'). +// fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { switch verb { case 'c', 'r', 'v': @@ -797,7 +1059,15 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) { fmt_int(fi, u64(r), false, 32, verb) } } - +// Formats an integer value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The integer value to be formatted. +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 32, 64). +// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U'). +// fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { switch verb { case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) @@ -822,7 +1092,15 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { fmt_bad_verb(fi, verb) } } - +// Formats an int128 value according to the specified formatting verb. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - u: The int128 value to be formatted. +// - is_signed: Whether the value should be treated as signed or unsigned. +// - bit_size: The number of bits of the value (e.g. 64, 128). +// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U'). +// fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) { switch verb { case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER) @@ -847,7 +1125,12 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru fmt_bad_verb(fi, verb) } } - +// Pads a formatted string with the appropriate padding, based on the provided formatting options. +// +// Inputs: +// - fi: A pointer to the Info struct containing formatting options. +// - s: The string to be padded. +// _pad :: proc(fi: ^Info, s: string) { if !fi.width_set { io.write_string(fi.writer, s, &fi.n) @@ -864,7 +1147,17 @@ _pad :: proc(fi: ^Info, s: string) { io.write_string(fi.writer, s, &fi.n) } } - +// Formats a floating-point number with a specific format and precision. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - v: The floating-point number to format. +// - bit_size: The size of the floating-point number in bits (16, 32, or 64). +// - verb: The format specifier character. +// - float_fmt: The byte format used for formatting the float (either 'f' or 'e'). +// +// NOTE: Can return "NaN", "+Inf", "-Inf", "+", or "-". +// _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) { prec := fi.prec if fi.prec_set else 3 buf: [386]byte @@ -881,7 +1174,14 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b _pad(fi, str) } - +// Formats a floating-point number with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - v: The floating-point number to format. +// - bit_size: The size of the floating-point number in bits (16, 32, or 64). +// - verb: The format specifier character. +// fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { switch verb { case 'f', 'F', 'g', 'G', 'v': @@ -914,8 +1214,13 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { fmt_bad_verb(fi, verb) } } - - +// Formats a string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The string to format. +// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X'). +// fmt_string :: proc(fi: ^Info, s: string, verb: rune) { s, verb := s, verb if ol, ok := fi.optional_len.?; ok { @@ -973,10 +1278,23 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) { fmt_bad_verb(fi, verb) } } +// Formats a C-style string with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - s: The C-style string to format. +// - verb: The format specifier character (Ref fmt_string). +// fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) { fmt_string(fi, string(s), verb) } - +// Formats a raw pointer with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - p: The raw pointer to format. +// - verb: The format specifier character (e.g. 'p', 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X'). +// fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { u := u64(uintptr(p)) switch verb { @@ -997,7 +1315,13 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { fmt_bad_verb(fi, verb) } } - +// Formats a Structure of Arrays (SoA) pointer with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - p: The SoA pointer to format. +// - verb: The format specifier character. +// fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { io.write_string(fi.writer, "#soa{data=0x", &fi.n) _fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER) @@ -1005,8 +1329,13 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) { _fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER) io.write_string(fi.writer, "}", &fi.n) } - - +// String representation of an enum value. +// +// Inputs: +// - val: The enum value. +// +// Returns: The string representation of the enum value and a boolean indicating success. +// enum_value_to_string :: proc(val: any) -> (string, bool) { v := val v.id = runtime.typeid_base(v.id) @@ -1036,7 +1365,14 @@ enum_value_to_string :: proc(val: any) -> (string, bool) { return "", false } - +// Returns the enum value of a string representation. +// +// $T: The typeid of the enum type. +// Inputs: +// - s: The string representation of the enum value. +// +// Returns: The enum value and a boolean indicating success. +// string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { ti := runtime.type_info_base(type_info_of(T)) if e, ok := ti.variant.(runtime.Type_Info_Enum); ok { @@ -1050,7 +1386,13 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) { } return T{}, false } - +// Formats an enum value with a specific format. +// +// Inputs: +// - fi: Pointer to the Info struct containing format settings. +// - v: The enum value to format. +// - verb: The format specifier character (e.g. 'i','d','f','s','v','q'). +// fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { if v.id == nil || v.data == nil { io.write_string(fi.writer, "", &fi.n) @@ -1076,8 +1418,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { } } } - - +// Converts a stored enum value to a string representation +// +// Inputs: +// - enum_type: A pointer to the runtime.Type_Info of the enumeration. +// - ev: The runtime.Type_Info_Enum_Value of the stored enum value. +// - offset: An optional integer to adjust the enumeration value (default is 0). +// +// Returns: A tuple containing the string representation of the enum value and a bool indicating success. +// stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) { et := runtime.type_info_base(enum_type) ev := ev @@ -1105,7 +1454,13 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false } - +// Formats a bit set and writes it to the provided Info structure +// +// Inputs: +// - fi: A pointer to the Info structure where the formatted bit set will be written. +// - v: The bit set value to be formatted. +// - name: An optional string for the name of the bit set (default is an empty string). +// fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { if ti == nil { @@ -1199,13 +1554,26 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } } } - +// Writes the specified number of indents to the provided Info structure +// +// Inputs: +// - fi: A pointer to the Info structure where the indents will be written. +// fmt_write_indent :: proc(fi: ^Info) { for in 0.. (do_continue: bool) { handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) { @@ -1294,7 +1672,15 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: } return false } - +// Formats a struct for output, handling various struct types (e.g., SOA, raw unions) +// +// Inputs: +// - fi: A mutable pointer to an Info struct containing formatting state +// - v: The value to be formatted +// - the_verb: The formatting verb to be used (e.g. 'v') +// - info: Type information about the struct +// - type_name: The name of the type being formatted +// fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) { if the_verb != 'v' { fmt_bad_verb(fi, the_verb) @@ -1448,7 +1834,15 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St } } } - +// Searches for the first NUL-terminated element in a given buffer +// +// Inputs: +// - ptr: The raw pointer to the buffer. +// - elem_size: The size of each element in the buffer. +// - max_n: The maximum number of elements to search (use -1 for no limit). +// +// Returns: The number of elements before the first NUL-terminated element. +// @(private) search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: int) { for p := uintptr(ptr); max_n < 0 || n < max_n; p += uintptr(elem_size) { @@ -1459,7 +1853,16 @@ search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: i } return n } - +// Formats a NUL-terminated array into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - data: The raw pointer to the array data. +// - max_n: The maximum number of elements to process. +// - elem_size: The size of each element in the array. +// - elem: Pointer to the type information of the array element. +// - verb: The formatting verb. +// fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { if data == nil { io.write_string(fi.writer, "", &fi.n) @@ -1468,7 +1871,16 @@ fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: n := search_nul_termination(data, elem_size, max_n) fmt_array(fi, data, n, elem_size, elem, verb) } - +// Formats an array into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - data: The raw pointer to the array data. +// - n: The number of elements in the array. +// - elem_size: The size of each element in the array. +// - elem: Pointer to the type information of the array element. +// - verb: The formatting verb (e.g. 's','q','p'). +// fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) { if data == nil && n > 0 { io.write_string(fi.writer, "nil") @@ -1523,7 +1935,16 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec fmt_write_array(fi, data, n, elem_size, elem.id, verb) } } - +// Formats a named type into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - v: The value to format. +// - verb: The formatting verb. +// - info: The named type information. +// +// NOTE: This procedure supports built-in custom formatters for core library types such as runtime.Source_Code_Location, time.Duration, and time.Time. +// fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) { write_padded_number :: proc(fi: ^Info, i: i64, width: int) { n := width-1 @@ -1672,7 +2093,15 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) fmt_value(fi, any{v.data, info.base.id}, verb) } } - +// Formats a union type into a string representation +// +// Inputs: +// - fi: Pointer to the formatting Info struct. +// - v: The value to format. +// - verb: The formatting verb. +// - info: The union type information. +// - type_size: The size of the union type. +// fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, type_size: int) { if type_size == 0 { io.write_string(fi.writer, "nil", &fi.n) @@ -1718,7 +2147,14 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, fmt_arg(fi, any{v.data, id}, verb) } } - +// Formats a matrix as a string +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - v: The matrix value to be formatted. +// - verb: The formatting verb rune. +// - info: A runtime.Type_Info_Matrix struct containing matrix type information. +// fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) { io.write_string(fi.writer, "matrix[", &fi.n) defer io.write_byte(fi.writer, ']', &fi.n) @@ -1761,7 +2197,15 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix fmt_write_indent(fi) } } - +// Formats a value based on its type and formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - v: The value to be formatted. +// - verb: The formatting verb rune. +// +// NOTE: Uses user formatters if available and not ignored. +// fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if v.data == nil || v.id == nil { io.write_string(fi.writer, "", &fi.n) @@ -2106,7 +2550,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { fmt_matrix(fi, v, verb, info) } } - +// Formats a complex number based on the given formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - c: The complex128 value to be formatted. +// - bits: The number of bits in the complex number (32 or 64). +// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H'). +// fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { switch verb { case 'f', 'F', 'v', 'h', 'H': @@ -2123,7 +2574,14 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { return } } - +// Formats a quaternion number based on the given formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - q: The quaternion256 value to be formatted. +// - bits: The number of bits in the quaternion number (64, 128, or 256). +// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H'). +// fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { switch verb { case 'f', 'F', 'v', 'h', 'H': @@ -2154,7 +2612,15 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { return } } - +// Formats an argument based on its type and the given formatting verb +// +// Inputs: +// - fi: A pointer to an Info struct containing formatting information. +// - arg: The value to be formatted. +// - verb: The formatting verb rune (e.g. 'T'). +// +// NOTE: Uses user formatters if available and not ignored. +// fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { io.write_string(fi.writer, "") @@ -2271,7 +2737,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { } } - - - - diff --git a/core/image/which.odin b/core/image/general.odin similarity index 75% rename from core/image/which.odin rename to core/image/general.odin index ab608174f..17a8f35ea 100644 --- a/core/image/which.odin +++ b/core/image/general.odin @@ -1,6 +1,48 @@ package image -import "core:os" +import "core:mem" +import "core:bytes" + +Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) +Destroy_Proc :: #type proc(img: ^Image) + +@(private) +_internal_loaders: [Which_File_Type]Loader_Proc +_internal_destroyers: [Which_File_Type]Destroy_Proc + +register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) { + assert(loader != nil) + assert(destroyer != nil) + assert(_internal_loaders[kind] == nil) + _internal_loaders[kind] = loader + + assert(_internal_destroyers[kind] == nil) + _internal_destroyers[kind] = destroyer +} + +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + loader := _internal_loaders[which(data)] + if loader == nil { + return nil, .Unsupported_Format + } + return loader(data, options, allocator) +} + + +destroy :: proc(img: ^Image, allocator := context.allocator) { + if img == nil { + return + } + context.allocator = allocator + destroyer := _internal_destroyers[img.which] + if destroyer != nil { + destroyer(img) + } else { + assert(img.metadata == nil) + bytes.buffer_destroy(&img.pixels) + free(img) + } +} Which_File_Type :: enum { Unknown, @@ -28,11 +70,6 @@ Which_File_Type :: enum { XBM, // X BitMap } -which :: proc{ - which_bytes, - which_file, -} - which_bytes :: proc(data: []byte) -> Which_File_Type { test_tga :: proc(s: string) -> bool { get8 :: #force_inline proc(s: ^string) -> u8 { @@ -164,16 +201,3 @@ which_bytes :: proc(data: []byte) -> Which_File_Type { } return .Unknown } - - -which_file :: proc(path: string) -> Which_File_Type { - f, err := os.open(path) - if err != 0 { - return .Unknown - } - header: [128]byte - os.read(f, header[:]) - file_type := which_bytes(header[:]) - os.close(f) - return file_type -} \ No newline at end of file diff --git a/core/image/general_js.odin b/core/image/general_js.odin new file mode 100644 index 000000000..841d9c200 --- /dev/null +++ b/core/image/general_js.odin @@ -0,0 +1,10 @@ +//+build js +package image + +load :: proc{ + load_from_bytes, +} + +which :: proc{ + which_bytes, +} diff --git a/core/image/general_loader.odin b/core/image/general_loader.odin deleted file mode 100644 index 36629c39e..000000000 --- a/core/image/general_loader.odin +++ /dev/null @@ -1,61 +0,0 @@ -package image - -import "core:mem" -import "core:os" -import "core:bytes" - -Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) -Destroy_Proc :: #type proc(img: ^Image) - -@(private) -_internal_loaders: [Which_File_Type]Loader_Proc -_internal_destroyers: [Which_File_Type]Destroy_Proc - -register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) { - assert(loader != nil) - assert(destroyer != nil) - assert(_internal_loaders[kind] == nil) - _internal_loaders[kind] = loader - - assert(_internal_destroyers[kind] == nil) - _internal_destroyers[kind] = destroyer -} - -load :: proc{ - load_from_bytes, - load_from_file, -} - -load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { - loader := _internal_loaders[which(data)] - if loader == nil { - return nil, .Unsupported_Format - } - return loader(data, options, allocator) -} - - -load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { - data, ok := os.read_entire_file(filename, allocator) - defer delete(data, allocator) - if ok { - return load_from_bytes(data, options, allocator) - } else { - return nil, .Unable_To_Read_File - } -} - -destroy :: proc(img: ^Image, allocator := context.allocator) { - if img == nil { - return - } - context.allocator = allocator - destroyer := _internal_destroyers[img.which] - if destroyer != nil { - destroyer(img) - } else { - assert(img.metadata == nil) - bytes.buffer_destroy(&img.pixels) - free(img) - } -} \ No newline at end of file diff --git a/core/image/general_os.odin b/core/image/general_os.odin new file mode 100644 index 000000000..144a3470f --- /dev/null +++ b/core/image/general_os.odin @@ -0,0 +1,38 @@ +//+build !js +package image + +import "core:os" + +load :: proc{ + load_from_bytes, + load_from_file, +} + + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + data, ok := os.read_entire_file(filename, allocator) + defer delete(data, allocator) + if ok { + return load_from_bytes(data, options, allocator) + } else { + return nil, .Unable_To_Read_File + } +} + + +which :: proc{ + which_bytes, + which_file, +} + +which_file :: proc(path: string) -> Which_File_Type { + f, err := os.open(path) + if err != 0 { + return .Unknown + } + header: [128]byte + os.read(f, header[:]) + file_type := which_bytes(header[:]) + os.close(f) + return file_type +} \ No newline at end of file diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin index 70eb3567e..cb07b1e3a 100644 --- a/core/image/netpbm/netpbm.odin +++ b/core/image/netpbm/netpbm.odin @@ -4,7 +4,6 @@ import "core:bytes" import "core:fmt" import "core:image" import "core:mem" -import "core:os" import "core:strconv" import "core:strings" import "core:unicode" @@ -27,23 +26,6 @@ PFM :: Formats{.Pf, .PF} ASCII :: Formats{.P1, .P2, .P3} BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM -load :: proc { - load_from_file, - load_from_bytes, -} - -load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) { - context.allocator = allocator - - data, ok := os.read_entire_file(filename); defer delete(data) - if !ok { - err = .Unable_To_Read_File - return - } - - return load_from_bytes(data) -} - load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) { context.allocator = allocator @@ -67,24 +49,6 @@ load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^ return img, nil } -save :: proc { - save_to_file, - save_to_buffer, -} - -save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) { - context.allocator = allocator - - data: []byte; defer delete(data) - data = save_to_buffer(img, custom_info) or_return - - if ok := os.write_entire_file(filename, data); !ok { - return .Unable_To_Write_File - } - - return Format_Error.None -} - save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (buffer: []byte, err: Error) { context.allocator = allocator diff --git a/core/image/netpbm/netpbm_js.odin b/core/image/netpbm/netpbm_js.odin new file mode 100644 index 000000000..7db17a05d --- /dev/null +++ b/core/image/netpbm/netpbm_js.odin @@ -0,0 +1,10 @@ +//+build js +package netpbm + +load :: proc { + load_from_bytes, +} + +save :: proc { + save_to_buffer, +} diff --git a/core/image/netpbm/netpbm_os.odin b/core/image/netpbm/netpbm_os.odin new file mode 100644 index 000000000..609f1ea1f --- /dev/null +++ b/core/image/netpbm/netpbm_os.odin @@ -0,0 +1,41 @@ +//+build !js +package netpbm + +import "core:os" + +load :: proc { + load_from_file, + load_from_bytes, +} + + +load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename); defer delete(data) + if !ok { + err = .Unable_To_Read_File + return + } + + return load_from_bytes(data) +} + + +save :: proc { + save_to_file, + save_to_buffer, +} + +save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + data: []byte; defer delete(data) + data = save_to_buffer(img, custom_info) or_return + + if ok := os.write_entire_file(filename, data); !ok { + return .Unable_To_Write_File + } + + return Format_Error.None +} \ No newline at end of file diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 02983997c..91adddafc 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -17,7 +17,6 @@ import "core:compress" import "core:compress/zlib" import "core:image" -import "core:os" import "core:hash" import "core:bytes" import "core:io" @@ -336,19 +335,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context return img, err } -load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { - context.allocator = allocator - - data, ok := os.read_entire_file(filename) - defer delete(data) - - if ok { - return load_from_bytes(data, options) - } else { - return nil, .Unable_To_Read_File - } -} - load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { context.allocator = allocator options := options @@ -1641,8 +1627,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH return nil } -load :: proc{load_from_file, load_from_bytes, load_from_context} - @(init, private) _register :: proc() { diff --git a/core/image/png/png_js.odin b/core/image/png/png_js.odin new file mode 100644 index 000000000..57c27fc64 --- /dev/null +++ b/core/image/png/png_js.odin @@ -0,0 +1,4 @@ +//+build js +package png + +load :: proc{load_from_bytes, load_from_context} diff --git a/core/image/png/png_os.odin b/core/image/png/png_os.odin new file mode 100644 index 000000000..cc65e7b42 --- /dev/null +++ b/core/image/png/png_os.odin @@ -0,0 +1,19 @@ +//+build !js +package png + +import "core:os" + +load :: proc{load_from_file, load_from_bytes, load_from_context} + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename) + defer delete(data) + + if ok { + return load_from_bytes(data, options) + } else { + return nil, .Unable_To_Read_File + } +} diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin index 29a17d4f4..27903c00f 100644 --- a/core/image/qoi/qoi.odin +++ b/core/image/qoi/qoi.odin @@ -15,7 +15,6 @@ package qoi import "core:image" import "core:compress" import "core:bytes" -import "core:os" Error :: image.Error Image :: image.Image @@ -24,7 +23,7 @@ Options :: image.Options RGB_Pixel :: image.RGB_Pixel RGBA_Pixel :: image.RGBA_Pixel -save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { +save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { context.allocator = allocator if img == nil { @@ -166,20 +165,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{} return nil } -save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { - context.allocator = allocator - - out := &bytes.Buffer{} - defer bytes.buffer_destroy(out) - - save_to_memory(out, img, options) or_return - write_ok := os.write_entire_file(output, out.buf[:]) - - return nil if write_ok else .Unable_To_Write_File -} - -save :: proc{save_to_memory, save_to_file} - load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { ctx := &compress.Context_Memory_Input{ input_data = data, @@ -189,19 +174,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context return img, err } -load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { - context.allocator = allocator - - data, ok := os.read_entire_file(filename) - defer delete(data) - - if ok { - return load_from_bytes(data, options) - } else { - return nil, .Unable_To_Read_File - } -} - @(optimization_mode="speed") load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { context.allocator = allocator @@ -359,8 +331,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return } -load :: proc{load_from_file, load_from_bytes, load_from_context} - /* Cleanup of image-specific data. */ diff --git a/core/image/qoi/qoi_js.odin b/core/image/qoi/qoi_js.odin new file mode 100644 index 000000000..2c23cc17a --- /dev/null +++ b/core/image/qoi/qoi_js.odin @@ -0,0 +1,6 @@ +//+build js +package qoi + +save :: proc{save_to_buffer} + +load :: proc{load_from_bytes, load_from_context} diff --git a/core/image/qoi/qoi_os.odin b/core/image/qoi/qoi_os.odin new file mode 100644 index 000000000..efcec6c52 --- /dev/null +++ b/core/image/qoi/qoi_os.odin @@ -0,0 +1,37 @@ +//+build !js +package qoi + +import "core:os" +import "core:bytes" + +save :: proc{save_to_buffer, save_to_file} + + +save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + out := &bytes.Buffer{} + defer bytes.buffer_destroy(out) + + save_to_buffer(out, img, options) or_return + write_ok := os.write_entire_file(output, out.buf[:]) + + return nil if write_ok else .Unable_To_Write_File +} + + +load :: proc{load_from_file, load_from_bytes, load_from_context} + + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename) + defer delete(data) + + if ok { + return load_from_bytes(data, options) + } else { + return nil, .Unable_To_Read_File + } +} \ No newline at end of file diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index 39c46c7c7..9fc616804 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -14,7 +14,6 @@ package tga import "core:mem" import "core:image" import "core:bytes" -import "core:os" import "core:compress" import "core:strings" @@ -28,7 +27,7 @@ GA_Pixel :: image.GA_Pixel RGB_Pixel :: image.RGB_Pixel RGBA_Pixel :: image.RGBA_Pixel -save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { +save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { context.allocator = allocator if img == nil { @@ -92,20 +91,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{} return nil } -save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { - context.allocator = allocator - - out := &bytes.Buffer{} - defer bytes.buffer_destroy(out) - - save_to_memory(out, img, options) or_return - write_ok := os.write_entire_file(output, out.buf[:]) - - return nil if write_ok else .Unable_To_Write_File -} - -save :: proc{save_to_memory, save_to_file} - load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { context.allocator = allocator options := options @@ -398,20 +383,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context return img, err } -load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { - context.allocator = allocator - - data, ok := os.read_entire_file(filename) - defer delete(data) - - if ok { - return load_from_bytes(data, options) - } else { - return nil, .Unable_To_Read_File - } -} - -load :: proc{load_from_file, load_from_bytes, load_from_context} destroy :: proc(img: ^Image) { if img == nil || img.width == 0 || img.height == 0 { diff --git a/core/image/tga/tga_js.odin b/core/image/tga/tga_js.odin new file mode 100644 index 000000000..d98b241a7 --- /dev/null +++ b/core/image/tga/tga_js.odin @@ -0,0 +1,5 @@ +//+build js +package tga + +save :: proc{save_to_buffer} +load :: proc{load_from_bytes, load_from_context} diff --git a/core/image/tga/tga_os.odin b/core/image/tga/tga_os.odin new file mode 100644 index 000000000..12747a684 --- /dev/null +++ b/core/image/tga/tga_os.odin @@ -0,0 +1,34 @@ +//+build !js +package tga + +import "core:os" +import "core:bytes" + +save :: proc{save_to_buffer, save_to_file} + +save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) { + context.allocator = allocator + + out := &bytes.Buffer{} + defer bytes.buffer_destroy(out) + + save_to_buffer(out, img, options) or_return + write_ok := os.write_entire_file(output, out.buf[:]) + + return nil if write_ok else .Unable_To_Write_File +} + +load :: proc{load_from_file, load_from_bytes, load_from_context} + +load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { + context.allocator = allocator + + data, ok := os.read_entire_file(filename) + defer delete(data) + + if ok { + return load_from_bytes(data, options) + } else { + return nil, .Unable_To_Read_File + } +} \ No newline at end of file diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin index b78697cbd..9dee12eb9 100644 --- a/core/math/linalg/extended.odin +++ b/core/math/linalg/extended.odin @@ -429,11 +429,11 @@ reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) b := N * (2 * dot(N, I)) return I - b } -refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) { - dv := dot(N, I) - k := 1 - eta*eta - (1 - dv*dv) +refract :: proc(I, Normal: $V/[$N]$E, eta: E) -> (out: V) where IS_ARRAY(V), IS_FLOAT(ELEM_TYPE(V)) { + dv := dot(Normal, I) + k := 1 - eta*eta * (1 - dv*dv) a := I * eta - b := N * eta*dv*math.sqrt(k) + b := Normal * (eta*dv+math.sqrt(k)) return (a - b) * E(int(k >= 0)) } diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index b70c8528e..29af7e71e 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1425,7 +1425,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { return es case "force_inline", "force_no_inline": - expr := parse_inlining_operand(p, true, tok) + expr := parse_inlining_operand(p, true, tag) es := ast.new(ast.Expr_Stmt, expr.pos, expr.end) es.expr = expr return es diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 60161f496..3120fcda3 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -211,7 +211,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil + return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil } if res != -ERANGE { return "", _get_platform_error(res) diff --git a/core/os/os_js.odin b/core/os/os_js.odin index f2563f217..5d7eb784e 100644 --- a/core/os/os_js.odin +++ b/core/os/os_js.odin @@ -1,4 +1,277 @@ +//+build js package os -// +build js -#panic("package os does not support a js target") \ No newline at end of file +import "core:intrinsics" +import "core:runtime" +import "core:unicode/utf16" + +is_path_separator :: proc(c: byte) -> bool { + return c == '/' || c == '\\' +} + +open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +close :: proc(fd: Handle) -> Errno { + unimplemented("core:os procedure not supported on JS target") +} + +flush :: proc(fd: Handle) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +@(private="file") +read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + +@(private) +MAX_RW :: 1<<30 + +@(private) +pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + unimplemented("core:os procedure not supported on JS target") +} +@(private) +pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} +write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +// NOTE(bill): Uses startup to initialize it +//stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE)) +//stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE)) +//stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) + + +get_std_handle :: proc "contextless" (h: uint) -> Handle { + context = runtime.default_context() + unimplemented("core:os procedure not supported on JS target") +} + + +exists :: proc(path: string) -> bool { + unimplemented("core:os procedure not supported on JS target") +} + +is_file :: proc(path: string) -> bool { + unimplemented("core:os procedure not supported on JS target") +} + +is_dir :: proc(path: string) -> bool { + unimplemented("core:os procedure not supported on JS target") +} + +// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName +//@private cwd_lock := win32.SRWLOCK{} // zero is initialized + +get_current_directory :: proc(allocator := context.allocator) -> string { + unimplemented("core:os procedure not supported on JS target") +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +change_directory :: proc(path: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + +remove_directory :: proc(path: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +@(private) +is_abs :: proc(path: string) -> bool { + unimplemented("core:os procedure not supported on JS target") +} + +@(private) +fix_long_path :: proc(path: string) -> string { + unimplemented("core:os procedure not supported on JS target") +} + + +link :: proc(old_name, new_name: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +unlink :: proc(path: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +rename :: proc(old_path, new_path: string) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + +ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +truncate :: proc(path: string, length: i64) -> (err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + +remove :: proc(name: string) -> Errno { + unimplemented("core:os procedure not supported on JS target") +} + + +pipe :: proc() -> (r, w: Handle, err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +Handle :: distinct uintptr +File_Time :: distinct u64 +Errno :: distinct int + + +INVALID_HANDLE :: ~Handle(0) + + + +O_RDONLY :: 0x00000 +O_WRONLY :: 0x00001 +O_RDWR :: 0x00002 +O_CREATE :: 0x00040 +O_EXCL :: 0x00080 +O_NOCTTY :: 0x00100 +O_TRUNC :: 0x00200 +O_NONBLOCK :: 0x00800 +O_APPEND :: 0x00400 +O_SYNC :: 0x01000 +O_ASYNC :: 0x02000 +O_CLOEXEC :: 0x80000 + + +ERROR_NONE: Errno : 0 +ERROR_FILE_NOT_FOUND: Errno : 2 +ERROR_PATH_NOT_FOUND: Errno : 3 +ERROR_ACCESS_DENIED: Errno : 5 +ERROR_INVALID_HANDLE: Errno : 6 +ERROR_NOT_ENOUGH_MEMORY: Errno : 8 +ERROR_NO_MORE_FILES: Errno : 18 +ERROR_HANDLE_EOF: Errno : 38 +ERROR_NETNAME_DELETED: Errno : 64 +ERROR_FILE_EXISTS: Errno : 80 +ERROR_INVALID_PARAMETER: Errno : 87 +ERROR_BROKEN_PIPE: Errno : 109 +ERROR_BUFFER_OVERFLOW: Errno : 111 +ERROR_INSUFFICIENT_BUFFER: Errno : 122 +ERROR_MOD_NOT_FOUND: Errno : 126 +ERROR_PROC_NOT_FOUND: Errno : 127 +ERROR_DIR_NOT_EMPTY: Errno : 145 +ERROR_ALREADY_EXISTS: Errno : 183 +ERROR_ENVVAR_NOT_FOUND: Errno : 203 +ERROR_MORE_DATA: Errno : 234 +ERROR_OPERATION_ABORTED: Errno : 995 +ERROR_IO_PENDING: Errno : 997 +ERROR_NOT_FOUND: Errno : 1168 +ERROR_PRIVILEGE_NOT_HELD: Errno : 1314 +WSAEACCES: Errno : 10013 +WSAECONNRESET: Errno : 10054 + +// Windows reserves errors >= 1<<29 for application use +ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0 +ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1 +ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2 + +// "Argv" arguments converted to Odin strings +args := _alloc_command_line_arguments() + + + + + +last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + +last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { + unimplemented("core:os procedure not supported on JS target") +} + + + +heap_alloc :: proc(size: int, zero_memory := true) -> rawptr { + unimplemented("core:os procedure not supported on JS target") +} +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + unimplemented("core:os procedure not supported on JS target") +} +heap_free :: proc(ptr: rawptr) { + unimplemented("core:os procedure not supported on JS target") +} + +get_page_size :: proc() -> int { + unimplemented("core:os procedure not supported on JS target") +} + +@(private) +_processor_core_count :: proc() -> int { + unimplemented("core:os procedure not supported on JS target") +} + +exit :: proc "contextless" (code: int) -> ! { + context = runtime.default_context() + unimplemented("core:os procedure not supported on JS target") +} + + + +current_thread_id :: proc "contextless" () -> int { + context = runtime.default_context() + unimplemented("core:os procedure not supported on JS target") +} + + + +_alloc_command_line_arguments :: proc() -> []string { + return nil +} + diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index d001d5ec7..3dc48087a 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -913,7 +913,7 @@ get_current_directory :: proc() -> string { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)) + return strings.string_from_null_terminated_ptr(&buf[0], len(buf)) } if _get_errno(res) != ERANGE { delete(buf) diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index 2abf450fa..f8343ead2 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -449,7 +449,14 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) return nil } - +@(require_results) +struct_field_value :: proc(a: any, field: Struct_Field) -> any { + if a == nil { return nil } + return any { + rawptr(uintptr(a.data) + field.offset), + field.type.id, + } +} @(require_results) struct_field_names :: proc(T: typeid) -> []string { diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 2d20310ae..040590b4a 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -621,7 +621,9 @@ __init_context :: proc "contextless" (c: ^Context) { c.allocator.data = nil c.temp_allocator.procedure = default_temp_allocator_proc - c.temp_allocator.data = &global_default_temp_allocator_data + when !NO_DEFAULT_TEMP_ALLOCATOR { + c.temp_allocator.data = &global_default_temp_allocator_data + } when !ODIN_DISABLE_ASSERT { c.assertion_failure_proc = default_assertion_failure_proc diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin index ae6a4aaf4..84fe5e522 100644 --- a/core/runtime/core_builtin.odin +++ b/core/runtime/core_builtin.odin @@ -15,11 +15,15 @@ container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: type } -@thread_local global_default_temp_allocator_data: Default_Temp_Allocator +when !NO_DEFAULT_TEMP_ALLOCATOR { + @thread_local global_default_temp_allocator_data: Default_Temp_Allocator +} -@builtin +@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR) init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { - default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator) + when !NO_DEFAULT_TEMP_ALLOCATOR { + default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator) + } } diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin index 296ead722..c90f0388d 100644 --- a/core/runtime/default_temporary_allocator.odin +++ b/core/runtime/default_temporary_allocator.odin @@ -1,9 +1,9 @@ package runtime DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte) +NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR - -when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR { +when NO_DEFAULT_TEMP_ALLOCATOR { Default_Temp_Allocator :: struct {} default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {} @@ -54,6 +54,11 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) { arena_temp_end(temp, loc) } + + @(fini, private) + _destroy_temp_allocator_fini :: proc() { + default_temp_allocator_destroy(&global_default_temp_allocator_data) + } } @(deferred_out=default_temp_allocator_temp_end) @@ -72,8 +77,3 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator data = allocator, } } - -@(fini, private) -_destroy_temp_allocator_fini :: proc() { - default_temp_allocator_destroy(&global_default_temp_allocator_data) -} diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index 9b59666f3..c65ef1c61 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -3,9 +3,22 @@ package strings import "core:unicode/utf8" +/* +Ascii_Set is designed to store ASCII characters efficiently as a bit-array +Each bit in the array corresponds to a specific ASCII character, where the value of the bit (0 or 1) +indicates if the character is present in the set or not. +*/ Ascii_Set :: distinct [8]u32 +/* +Creates an Ascii_Set with unique characters from the input string. -// create an ascii set of all unique characters in the string +Inputs: +- chars: A string containing characters to include in the Ascii_Set. + +Returns: +- as: An Ascii_Set with unique characters from the input string. +- ok: false if any character in the input string is not a valid ASCII character. +*/ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check { for i in 0.. (as: Ascii_Set, ok: bool) #no_bounds_ch ok = true return } +/* +Determines if a given char is contained within an Ascii_Set. -// returns true when the `c` byte is contained in the `as` ascii set +Inputs: +- as: The Ascii_Set to search. +- c: The char to check for in the Ascii_Set. + +Returns: +A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). +*/ ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 -} \ No newline at end of file +} diff --git a/core/strings/builder.odin b/core/strings/builder.odin index a6d5b78b4..32442c21a 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -4,68 +4,133 @@ import "core:runtime" import "core:unicode/utf8" import "core:strconv" import "core:io" - -Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) - /* - dynamic byte buffer / string builder with helper procedures - the dynamic array is wrapped inside the struct to be more opaque - you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly +Type definition for a procedure that flushes a Builder + +Inputs: +- b: A pointer to the Builder + +Returns: +A boolean indicating whether the Builder should be reset +*/ +Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) +/* +A dynamic byte buffer / string builder with helper procedures +The dynamic array is wrapped inside the struct to be more opaque +You can use `fmt.sbprint*` procedures with a `^strings.Builder` directly */ Builder :: struct { buf: [dynamic]byte, } +/* +Produces a Builder with a default length of 0 and cap of 16 -// return a builder, default length 0 / cap 16 are done through make +*Allocates Using Provided Allocator* + +Inputs: +- allocator: (default is context.allocator) + +Returns: +A new Builder +*/ builder_make_none :: proc(allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, allocator)} } +/* +Produces a Builder with a specified length and cap of max(16,len) byte buffer -// return a builder, with a set length `len` and cap 16 byte buffer +*Allocates Using Provided Allocator* + +Inputs: +- len: The desired length of the Builder's buffer +- allocator: (default is context.allocator) + +Returns: +A new Builder +*/ builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, allocator)} } +/* +Produces a Builder with a specified length and cap -// return a builder, with a set length `len` byte buffer and a custom `cap` +*Allocates Using Provided Allocator* + +Inputs: +- len: The desired length of the Builder's buffer +- cap: The desired capacity of the Builder's buffer, cap is max(cap, len) +- allocator: (default is context.allocator) + +Returns: +A new Builder +*/ builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, cap, allocator)} } - // overload simple `builder_make_*` with or without len / cap parameters builder_make :: proc{ builder_make_none, builder_make_len, builder_make_len_cap, } +/* +Initializes a Builder with a length of 0 and cap of 16 +It replaces the existing `buf` -// initialize a builder, default length 0 / cap 16 are done through make -// replaces the existing `buf` +*Allocates Using Provided Allocator* + +Inputs: +- b: A pointer to the Builder +- allocator: (default is context.allocator) + +Returns: +initialized ^Builder +*/ builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, allocator) return b } +/* +Initializes a Builder with a specified length and cap, which is max(len,16) +It replaces the existing `buf` -// initialize a builder, with a set length `len` and cap 16 byte buffer -// replaces the existing `buf` +*Allocates Using Provided Allocator* + +Inputs: +- b: A pointer to the Builder +- len: The desired length of the Builder's buffer +- allocator: (default is context.allocator) + +Returns: +Initialized ^Builder +*/ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, allocator) return b } +/* +Initializes a Builder with a specified length and cap +It replaces the existing `buf` -// initialize a builder, with a set length `len` byte buffer and a custom `cap` -// replaces the existing `buf` +Inputs: +- b: A pointer to the Builder +- len: The desired length of the Builder's buffer +- cap: The desired capacity of the Builder's buffer, actual max(len,cap) +- allocator: (default is context.allocator) + +Returns: +A pointer to the initialized Builder +*/ builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, cap, allocator) return b } - -// overload simple `builder_init_*` with or without len / ap parameters +// Overload simple `builder_init_*` with or without len / ap parameters builder_init :: proc{ builder_init_none, builder_init_len, builder_init_len_cap, } - @(private) _builder_stream_vtable_obj := io.Stream_VTable{ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { @@ -90,50 +155,95 @@ _builder_stream_vtable_obj := io.Stream_VTable{ }, impl_destroy = proc(s: io.Stream) -> io.Error { b := (^Builder)(s.stream_data) - delete(b.buf) + builder_destroy(b) return .None }, } - // NOTE(dweiler): Work around a miscompilation bug on Linux still. @(private) _builder_stream_vtable := &_builder_stream_vtable_obj +/* +Returns an io.Stream from a Builder -// return an `io.Stream` from a builder +Inputs: +- b: A pointer to the Builder + +Returns: +An io.Stream +*/ to_stream :: proc(b: ^Builder) -> io.Stream { return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} } +/* +Returns an io.Writer from a Builder -// return an `io.Writer` from a builder +Inputs: +- b: A pointer to the Builder + +Returns: +An io.Writer +*/ to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) } +/* +Deletes the Builder byte buffer content -// delete and clear the builder byte buffer content +Inputs: +- b: A pointer to the Builder +*/ builder_destroy :: proc(b: ^Builder) { delete(b.buf) - clear(&b.buf) + b.buf = nil } +/* +Reserves the Builder byte buffer to a specific capacity, when it's higher than before -// reserve the builfer byte buffer to a specific cap, when it's higher than before +Inputs: +- b: A pointer to the Builder +- cap: The desired capacity for the Builder's buffer +*/ builder_grow :: proc(b: ^Builder, cap: int) { reserve(&b.buf, cap) } +/* +Clears the Builder byte buffer content (sets len to zero) -// clear the builder byte buffer content +Inputs: +- b: A pointer to the Builder +*/ builder_reset :: proc(b: ^Builder) { clear(&b.buf) } - /* - create an empty builder with the same slice length as its cap - uses the `mem.nil_allocator` to avoid allocation and keep a fixed length - used in `fmt.bprint*` - - bytes: [8]byte // <-- gets filled - builder := strings.builder_from_bytes(bytes[:]) - strings.write_byte(&builder, 'a') -> "a" - strings.write_byte(&builder, 'b') -> "ab" +Creates a Builder from a slice of bytes with the same slice length as its capacity. Used in fmt.bprint* + +*Uses Nil Allocator - Does NOT allocate* + +Inputs: +- backing: A slice of bytes to be used as the backing buffer + +Returns: +A new Builder + +Example: + + import "core:fmt" + import "core:strings" + builder_from_bytes_example :: proc() { + bytes: [8]byte // <-- gets filled + builder := strings.builder_from_bytes(bytes[:]) + strings.write_byte(&builder, 'a') + fmt.println(strings.to_string(builder)) // -> "a" + strings.write_byte(&builder, 'b') + fmt.println(strings.to_string(builder)) // -> "ab" + } + +Output: + + a + ab + */ builder_from_bytes :: proc(backing: []byte) -> Builder { s := transmute(runtime.Raw_Slice)backing @@ -147,36 +257,84 @@ builder_from_bytes :: proc(backing: []byte) -> Builder { buf = transmute([dynamic]byte)d, } } +// Alias to `builder_from_bytes` builder_from_slice :: builder_from_bytes +/* +Casts the Builder byte buffer to a string and returns it -// cast the builder byte buffer to a string and return it +Inputs: +- b: A Builder + +Returns: +The contents of the Builder's buffer, as a string +*/ to_string :: proc(b: Builder) -> string { return string(b.buf[:]) } +/* +Returns the length of the Builder's buffer, in bytes -// return the length of the builder byte buffer +Inputs: +- b: A Builder + +Returns: +The length of the Builder's buffer +*/ builder_len :: proc(b: Builder) -> int { return len(b.buf) } +/* +Returns the capacity of the Builder's buffer, in bytes -// return the cap of the builder byte buffer +Inputs: +- b: A Builder + +Returns: +The capacity of the Builder's buffer +*/ builder_cap :: proc(b: Builder) -> int { return cap(b.buf) } +/* +The free space left in the Builder's buffer, in bytes -// returns the space left in the builder byte buffer to use up +Inputs: +- b: A Builder + +Returns: +The available space left in the Builder's buffer +*/ builder_space :: proc(b: Builder) -> int { return cap(b.buf) - len(b.buf) } - /* - appends a byte to the builder, returns the append diff +Appends a byte to the Builder and returns the number of bytes appended + +Inputs: +- b: A pointer to the Builder +- x: The byte to be appended + +Returns: +The number of bytes appended + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_byte_example :: proc() { + builder := strings.builder_make() + strings.write_byte(&builder, 'a') // 1 + strings.write_byte(&builder, 'b') // 1 + fmt.println(strings.to_string(builder)) // -> ab + } + +Output: + + ab - builder := strings.builder_make() - strings.write_byte(&builder, 'a') // 1 - strings.write_byte(&builder, 'b') // 1 - strings.write_byte(&builder, 'c') // 1 - fmt.println(strings.to_string(builder)) // -> abc */ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n0 := len(b.buf) @@ -184,14 +342,29 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n1 := len(b.buf) return n1-n0 } - /* - appends a slice of bytes to the builder, returns the append diff +Appends a slice of bytes to the Builder and returns the number of bytes appended - builder := strings.builder_make() - bytes := [?]byte { 'a', 'b', 'c' } - strings.write_bytes(&builder, bytes[:]) // 3 - fmt.println(strings.to_string(builder)) // -> abc +Inputs: +- b: A pointer to the Builder +- x: The slice of bytes to be appended + +Example: + + import "core:fmt" + import "core:strings" + + write_bytes_example :: proc() { + builder := strings.builder_make() + bytes := [?]byte { 'a', 'b', 'c' } + strings.write_bytes(&builder, bytes[:]) // 3 + fmt.println(strings.to_string(builder)) // -> abc + } + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of bytes appended */ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n0 := len(b.buf) @@ -199,42 +372,99 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n1 := len(b.buf) return n1-n0 } - /* - appends a single rune into the builder, returns written rune size and an `io.Error` +Appends a single rune to the Builder and returns the number of bytes written and an `io.Error` + +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended + +Returns: +The number of bytes written and an io.Error (if any) + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_rune_example :: proc() { + builder := strings.builder_make() + strings.write_rune(&builder, 'ä') // 2 None + strings.write_rune(&builder, 'b') // 1 None + fmt.println(strings.to_string(builder)) // -> äb + } + +Output: + + äb - builder := strings.builder_make() - strings.write_rune(&builder, 'ä') // 2 None - strings.write_rune(&builder, 'b') // 1 None - strings.write_rune(&builder, 'c') // 1 None - fmt.println(strings.to_string(builder)) // -> äbc */ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { return io.write_rune(to_writer(b), r) } - /* - appends a quoted rune into the builder, returns written size +Appends a quoted rune to the Builder and returns the number of bytes written + +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended + +Returns: +The number of bytes written + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_quoted_rune_example :: proc() { + builder := strings.builder_make() + strings.write_string(&builder, "abc") // 3 + strings.write_quoted_rune(&builder, 'ä') // 4 + strings.write_string(&builder, "abc") // 3 + fmt.println(strings.to_string(builder)) // -> abc'ä'abc + } + +Output: + + abc'ä'abc - builder := strings.builder_make() - strings.write_string(&builder, "abc") // 3 - strings.write_quoted_rune(&builder, 'ä') // 4 - strings.write_string(&builder, "abc") // 3 - fmt.println(strings.to_string(builder)) // -> abc'ä'abc */ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { return io.write_quoted_rune(to_writer(b), r) } - - /* - appends a string to the builder, return the written byte size - - builder := strings.builder_make() - strings.write_string(&builder, "a") // 1 - strings.write_string(&builder, "bc") // 2 - strings.write_string(&builder, "xyz") // 3 - fmt.println(strings.to_string(builder)) // -> abcxyz +Appends a string to the Builder and returns the number of bytes written + +Inputs: +- b: A pointer to the Builder +- s: The string to be appended + +Returns: +The number of bytes written + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_string_example :: proc() { + builder := strings.builder_make() + strings.write_string(&builder, "a") // 1 + strings.write_string(&builder, "bc") // 2 + fmt.println(strings.to_string(builder)) // -> abc + } + +Output: + + abc + */ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n0 := len(b.buf) @@ -242,10 +472,15 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n1 := len(b.buf) return n1-n0 } +/* +Pops and returns the last byte in the Builder or 0 when the Builder is empty +Inputs: +- b: A pointer to the Builder -// pops and returns the last byte in the builder -// returns 0 when the builder is empty +Returns: +The last byte in the Builder or 0 if empty +*/ pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0 @@ -256,9 +491,15 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) { d.len = max(d.len-1, 0) return } +/* +Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty -// pops the last rune in the builder and returns the popped rune and its rune width -// returns 0, 0 when the builder is empty +Inputs: +- b: A pointer to the Builder + +Returns: +The popped rune and its rune width or (0, 0) if empty +*/ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { if len(b.buf) == 0 { return 0, 0 @@ -269,41 +510,116 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { d.len = max(d.len-width, 0) return } - @(private) DIGITS_LOWER := "0123456789abcdefx" - /* - append a quoted string into the builder, return the written byte size +Inputs: +- b: A pointer to the Builder +- str: The string to be quoted and appended +- quote: The optional quote character (default is double quotes) + +Returns: +The number of bytes written + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_quoted_string_example :: proc() { + builder := strings.builder_make() + strings.write_quoted_string(&builder, "a") // 3 + strings.write_quoted_string(&builder, "bc", '\'') // 4 + strings.write_quoted_string(&builder, "xyz") // 5 + fmt.println(strings.to_string(builder)) + } + +Output: + + "a"'bc'"xyz" - builder := strings.builder_make() - strings.write_quoted_string(&builder, "a") // 3 - strings.write_quoted_string(&builder, "bc", '\'') // 4 - strings.write_quoted_string(&builder, "xyz") // 5 - fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" */ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { n, _ = io.write_quoted_string(to_writer(b), str, quote) return } +/* +Appends a rune to the Builder and returns the number of bytes written +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended +- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true) -// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size +Returns: +The number of bytes written + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_encoded_rune_example :: proc() { + builder := strings.builder_make() + strings.write_encoded_rune(&builder, 'a', false) // 1 + strings.write_encoded_rune(&builder, '\"', true) // 3 + strings.write_encoded_rune(&builder, 'x', false) // 1 + fmt.println(strings.to_string(builder)) + } + +Output: + + a'"'x + +*/ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) return } +/* +Appends an escaped rune to the Builder and returns the number of bytes written -// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such -// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes -// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended +- quote: The quote character +- html_safe: Optional boolean flag to encode '<', '>', '&' as digits (default is false) + +**Usage** +- '\a' will be written as such +- `r` and `quote` match and `quote` is `\\` - they will be written as two slashes +- `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of bytes written +*/ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) return } +/* +Writes a f64 value to the Builder and returns the number of characters written -// writes a f64 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f64 value to be appended +- fmt: The format byte +- prec: The precision +- bit_size: The bit size +- always_signed: Optional boolean flag to always include the sign (default is false) + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f, fmt, prec, bit_size) @@ -314,8 +630,20 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_ } return write_string(b, s) } +/* +Writes a f16 value to the Builder and returns the number of characters written -// writes a f16 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f16 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -324,8 +652,38 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a f32 value to the Builder and returns the number of characters written -// writes a f32 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f32 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +Returns: +The number of characters written + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Example: + + import "core:fmt" + import "core:strings" + + write_f32_example :: proc() { + builder := strings.builder_make() + strings.write_f32(&builder, 3.14159, 'f') // 6 + strings.write_string(&builder, " - ") // 3 + strings.write_f32(&builder, -0.123, 'e') // 8 + fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01 + } + +Output: + + 3.14159012 - -1.23000003e-01 + +*/ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -334,8 +692,20 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a f32 value to the Builder and returns the number of characters written -// writes a f64 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f32 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -344,30 +714,71 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a u64 value to the Builder and returns the number of characters written +Inputs: +- b: A pointer to the Builder +- i: The u64 value to be appended +- base: The optional base for the numeric representation +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters +Returns: +The number of characters written +*/ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(b, s) } +/* +Writes a i64 value to the Builder and returns the number of characters written -// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The i64 value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(b, s) } +/* +Writes a uint value to the Builder and returns the number of characters written -// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The uint value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { return write_u64(b, u64(i), base) } +/* +Writes a int value to the Builder and returns the number of characters written -// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The int value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: +The number of characters written +*/ write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) { return write_i64(b, i64(i), base) } - diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index 8a67618da..0160c8a60 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -4,6 +4,21 @@ import "core:io" import "core:unicode" import "core:unicode/utf8" +/* +Converts invalid UTF-8 sequences in the input string `s` to the `replacement` string. + +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string that may contain invalid UTF-8 sequences. +- replacement: String to replace invalid UTF-8 sequences with. +- allocator: (default: context.allocator). + +WARNING: Allocation does not occur when len(s) == 0 + +Returns: +A valid UTF-8 string with invalid sequences replaced by `replacement`. +*/ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { if len(s) == 0 { return "" @@ -33,7 +48,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> invalid := false - for i := 0; i < len(s); /**/ { + for i := 0; i < len(s); /**/{ c := s[i] if c < utf8.RUNE_SELF { i += 1 @@ -57,13 +72,31 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> } return to_string(b) } - /* - returns the input string `s` with all runes set to lowered case - always allocates using the `allocator` +Converts the input string `s` to all lowercase characters. + +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: +A new string with all characters converted to lowercase. + +Example: + + import "core:fmt" + import "core:strings" + + to_lower_example :: proc() { + fmt.println(strings.to_lower("TeST")) + } + +Output: + + test - strings.to_lower("test") -> test - strings.to_lower("Test") -> test */ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -73,13 +106,31 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string { } return to_string(b) } - /* - returns the input string `s` with all runes set to upper case - always allocates using the `allocator` +Converts the input string `s` to all uppercase characters. + +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: +A new string with all characters converted to uppercase. + +Example: + + import "core:fmt" + import "core:strings" + + to_upper_example :: proc() { + fmt.println(strings.to_upper("Test")) + } + +Output: + + TEST - strings.to_upper("test") -> TEST - strings.to_upper("Test") -> TEST */ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -89,21 +140,38 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { } return to_string(b) } +/* +Checks if the rune `r` is a delimiter (' ', '-', or '_'). -// returns true when the `c` rune is a space, '-' or '_' -// useful when treating strings like words in a text editor or html paths -is_delimiter :: proc(c: rune) -> bool { - return c == '-' || c == '_' || is_space(c) +Inputs: +- r: Rune to check for delimiter status. + +Returns: +True if `r` is a delimiter, false otherwise. +*/ +is_delimiter :: proc(r: rune) -> bool { + return r == '-' || r == '_' || is_space(r) } +/* +Checks if the rune `r` is a non-alphanumeric or space character. -// returns true when the `r` rune is a non alpha or `unicode.is_space` rune +Inputs: +- r: Rune to check for separator status. + +Returns: +True if `r` is a non-alpha or `unicode.is_space` rune. +*/ is_separator :: proc(r: rune) -> bool { if r <= 0x7f { switch r { - case '0'..='9': return false - case 'a'..='z': return false - case 'A'..='Z': return false - case '_': return false + case '0' ..= '9': + return false + case 'a' ..= 'z': + return false + case 'A' ..= 'Z': + return false + case '_': + return false } return true } @@ -115,12 +183,46 @@ is_separator :: proc(r: rune) -> bool { return unicode.is_space(r) } - /* - iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune - on empty string `s` the callback gets called once with empty runes +Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments. + +Inputs: +- w: An io.Writer to be used by the callback for writing output. +- s: The input string to be iterated over. +- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune). +The callback can utilize the provided io.Writer to write output during the iteration. + +Example: + + import "core:fmt" + import "core:strings" + import "core:io" + + string_case_iterator_example :: proc() { + my_callback :: proc(w: io.Writer, prev, curr, next: rune) { + fmt.println("my_callback", curr) // <-- Custom logic here + } + s := "hello" + b: strings.Builder + strings.builder_init_len(&b, len(s)) + w := strings.to_writer(&b) + strings.string_case_iterator(w, s, my_callback) + } + +Output: + + my_callback h + my_callback e + my_callback l + my_callback l + my_callback o + */ -string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) { +string_case_iterator :: proc( + w: io.Writer, + s: string, + callback: proc(w: io.Writer, prev, curr, next: rune), +) { prev, curr: rune for next in s { if curr == 0 { @@ -139,10 +241,20 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write callback(w, prev, curr, 0) } } - +// Alias to `to_camel_case` to_lower_camel_case :: to_camel_case +/* +Converts the input string `s` to "lowerCamelCase". -// converts the `s` string to "lowerCamelCase" +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: +A "lowerCamelCase" formatted string. +*/ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) @@ -164,10 +276,20 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } - +// Alias to `to_pascal_case` to_upper_camel_case :: to_pascal_case +/* +Converts the input string `s` to "UpperCamelCase" (PascalCase). -// converts the `s` string to "PascalCase" +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: +A "PascalCase" formatted string. +*/ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) @@ -189,17 +311,44 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } +/* +Returns a string converted to a delimiter-separated case with configurable casing -/* - returns the `s` string to words seperated by the given `delimiter` rune - all runes will be upper or lowercased based on the `all_uppercase` bool +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- delimiter: The rune to be used as the delimiter between words +- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false) +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_delimiter_case_example :: proc() { + fmt.println(strings.to_delimiter_case("Hello World", '_', false)) + fmt.println(strings.to_delimiter_case("Hello World", ' ', true)) + fmt.println(strings.to_delimiter_case("aBC", '_', false)) + } + +Output: + + hello_world + HELLO WORLD + a_bc - strings.to_delimiter_case("Hello World", '_', false) -> hello_world - strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD - strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD - strings.to_delimiter_case("aBC", '_', false) -> a_b_c */ -to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { +to_delimiter_case :: proc( + s: string, + delimiter: rune, + all_upper_case: bool, + allocator := context.allocator, +) -> string { s := s s = trim_space(s) b: Builder @@ -237,73 +386,171 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo return to_string(b) } +/* +Converts a string to "snake_case" with all runes lowercased + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_snake_case_example :: proc() { + fmt.println(strings.to_snake_case("HelloWorld")) + fmt.println(strings.to_snake_case("Hello World")) + } + +Output: + + hello_world + hello_world -/* - converts the `s` string to "snake_case" with all runes lowercased - - strings.to_snake_case("HelloWorld") -> hello_world - strings.to_snake_case("Hello World") -> hello_world */ to_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', false, allocator) } - +// Alias for `to_upper_snake_case` to_screaming_snake_case :: to_upper_snake_case +/* +Converts a string to "SNAKE_CASE" with all runes uppercased -// converts the `s` string to "SNAKE_CASE" with all runes uppercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_upper_snake_case_example :: proc() { + fmt.println(strings.to_upper_snake_case("HelloWorld")) + } + +Output: + + HELLO_WORLD + +*/ to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', true, allocator) } +/* +Converts a string to "kebab-case" with all runes lowercased -// converts the `s` string to "kebab-case" with all runes lowercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_kebab_case_example :: proc() { + fmt.println(strings.to_kebab_case("HelloWorld")) + } + +Output: + + hello-world + +*/ to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', false, allocator) } +/* +Converts a string to "KEBAB-CASE" with all runes uppercased -// converts the `s` string to "KEBAB-CASE" with all runes uppercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_upper_kebab_case_example :: proc() { + fmt.println(strings.to_upper_kebab_case("HelloWorld")) + } + +Output: + + HELLO-WORLD + +*/ to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', true, allocator) } +/* +Converts a string to "Ada_Case" -// converts the `s` string to "Ada_case" +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Returns: +The converted string + +Example: + + import "core:fmt" + import "core:strings" + + to_ada_case_example :: proc() { + fmt.println(strings.to_ada_case("HelloWorld")) + } + +Output: + + Hello_World + +*/ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { - delimiter :: '_' - s := s s = trim_space(s) b: Builder builder_init(&b, 0, len(s), allocator) w := to_writer(&b) - prev, curr: rune - - for next in s { - if is_delimiter(curr) { - if !is_delimiter(prev) { - io.write_rune(w, delimiter) + string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { + if !is_delimiter(curr) { + if is_delimiter(prev) || prev == 0 || (unicode.is_lower(prev) && unicode.is_upper(curr)) { + if prev != 0 { + io.write_rune(w, '_') + } + io.write_rune(w, unicode.to_upper(curr)) + } else { + io.write_rune(w, unicode.to_lower(curr)) } - } else if unicode.is_upper(curr) { - if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) { - io.write_rune(w, delimiter) - } - io.write_rune(w, unicode.to_upper(curr)) - } else if curr != 0 { - io.write_rune(w, unicode.to_lower(curr)) } - - prev = curr - curr = next - } - - if len(s) > 0 { - if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 { - io.write_rune(w, delimiter) - io.write_rune(w, unicode.to_upper(curr)) - } else { - io.write_rune(w, unicode.to_lower(curr)) - } - } + }) return to_string(b) } - diff --git a/core/strings/intern.odin b/core/strings/intern.odin index 5e9193a0d..463abeb1e 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -2,49 +2,99 @@ package strings import "core:runtime" -// custom string entry struct +// Custom string entry struct Intern_Entry :: struct { len: int, str: [1]byte, // string is allocated inline with the entry to keep allocations simple } +/* +Intern is a more memory efficient string map -// "intern" is a more memory efficient string map -// `allocator` is used to allocate the actual `Intern_Entry` strings +Uses Specified Allocator for `Intern_Entry` strings + +Fields: +- allocator: The allocator used for the Intern_Entry strings +- entries: A map of strings to interned string entries +*/ Intern :: struct { allocator: runtime.Allocator, entries: map[string]^Intern_Entry, } +/* +Initializes the entries map and sets the allocator for the string entries -// initialize the entries map and set the allocator for the string entries +*Allocates Using Provided Allocators* + +Inputs: +- m: A pointer to the Intern struct to be initialized +- allocator: The allocator for the Intern_Entry strings (Default: context.allocator) +- map_allocator: The allocator for the map of entries (Default: context.allocator) +*/ intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) { m.allocator = allocator m.entries = make(map[string]^Intern_Entry, 16, map_allocator) } +/* +Frees the map and all its content allocated using the `.allocator`. -// free the map and all its content allocated using the `.allocator` +Inputs: +- m: A pointer to the Intern struct to be destroyed +*/ intern_destroy :: proc(m: ^Intern) { for _, value in m.entries { free(value, m.allocator) } delete(m.entries) } +/* +Returns an interned copy of the given text, adding it to the map if not already present. -// returns the `text` string from the intern map - gets set if it didnt exist yet -// the returned string lives as long as the map entry lives +*Allocate using the Intern's Allocator (First time string is seen only)* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be interned + +NOTE: The returned string lives as long as the map entry lives. + +Returns: +The interned string and an allocator error if any +*/ intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return #no_bounds_check return string(entry.str[:entry.len]), nil } +/* +Returns an interned copy of the given text as a cstring, adding it to the map if not already present. -// returns the `text` cstring from the intern map - gets set if it didnt exist yet -// the returned cstring lives as long as the map entry lives +*Allocate using the Intern's Allocator (First time string is seen only)* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be interned + +NOTE: The returned cstring lives as long as the map entry lives + +Returns: +The interned cstring and an allocator error if any +*/ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return return cstring(&entry.str[0]), nil } +/* +Internal function to lookup whether the text string exists in the map, returns the entry +Sets and allocates the entry if it wasn't set yet -// looks up wether the `text` string exists in the map, returns the entry -// sets & allocates the entry if it wasnt set yet +*Allocate using the Intern's Allocator (First time string is seen only)* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be looked up or interned + +Returns: +The new or existing interned entry and an allocator error if any +*/ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check { if prev, ok := m.entries[text]; ok { return prev, nil diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 038740526..715e57ada 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -4,59 +4,109 @@ import "core:io" import "core:unicode/utf8" /* - io stream data for a string reader that can read based on bytes or runes - implements the vtable when using the io.Reader variants - "read" calls advance the current reading offset `i` +io stream data for a string reader that can read based on bytes or runes +implements the vtable when using the `io.Reader` variants +"read" calls advance the current reading offset `i` */ Reader :: struct { s: string, // read-only buffer i: i64, // current reading index prev_rune: int, // previous reading index of rune or < 0 } +/* +Initializes a string Reader with the provided string -// init the reader to the string `s` +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read +*/ reader_init :: proc(r: ^Reader, s: string) { r.s = s r.i = 0 r.prev_rune = -1 } +/* +Converts a Reader into an `io.Stream` -// returns a stream from the reader data +Inputs: +- r: A pointer to a Reader struct + +Returns: +An io.Stream for the given Reader +*/ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { s.stream_data = r s.stream_vtable = &_reader_vtable return } +/* +Initializes a string Reader and returns an `io.Reader` for the given string -// init a reader to the string `s` and return an io.Reader +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read + +Returns: +An io.Reader for the given string +*/ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { reader_init(r, s) rr, _ := io.to_reader(reader_to_stream(r)) return rr } +/* +Initializes a string Reader and returns an `io.Reader_At` for the given string -// init a reader to the string `s` and return an io.Reader_At +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read + +Returns: +An `io.Reader_At` for the given string +*/ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) rr, _ := io.to_reader_at(reader_to_stream(r)) return rr } +/* +Returns the remaining length of the Reader -// remaining length of the reader +Inputs: +- r: A pointer to a Reader struct + +Returns: +The remaining length of the Reader +*/ reader_length :: proc(r: ^Reader) -> int { if r.i >= i64(len(r.s)) { return 0 } return int(i64(len(r.s)) - r.i) } +/* +Returns the length of the string stored in the Reader -// returns the string length stored by the reader +Inputs: +- r: A pointer to a Reader struct + +Returns: +The length of the string stored in the Reader +*/ reader_size :: proc(r: ^Reader) -> i64 { return i64(len(r.s)) } +/* +Reads len(p) bytes from the Reader's string and copies into the provided slice. -// reads len(p) bytes into the slice from the string in the reader -// returns `n` amount of read bytes and an io.Error +Inputs: +- r: A pointer to a Reader struct +- p: A byte slice to copy data into + +Returns: +- n: The number of bytes read +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. +*/ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { if r.i >= i64(len(r.s)) { return 0, .EOF @@ -66,9 +116,18 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { r.i += i64(n) return } +/* +Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index. -// reads len(p) bytes into the slice from the string in the reader at an offset -// returns `n` amount of read bytes and an io.Error +Inputs: +- r: A pointer to a Reader struct +- p: A byte slice to copy data into +- off: The offset from which to read + +Returns: +- n: The number of bytes read +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. +*/ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { if off < 0 { return 0, .Invalid_Offset @@ -82,8 +141,16 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro } return } +/* +Reads and returns a single byte from the Reader's string -// reads and returns a single byte - error when out of bounds +Inputs: +- r: A pointer to a Reader struct + +Returns: +- The byte read from the Reader +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. +*/ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -93,8 +160,15 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.i += 1 return b, nil } +/* +Decrements the Reader's index (i) by 1 -// decreases the reader offset - error when below 0 +Inputs: +- r: A pointer to a Reader struct + +Returns: +An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success. +*/ reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -103,9 +177,18 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { r.i -= 1 return nil } +/* +Reads and returns a single rune and its `size` from the Reader's string -// reads and returns a single rune and the rune size - error when out bounds -reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { +Inputs: +- r: A pointer to a Reader struct + +Returns: +- rr: The rune read from the Reader +- size: The size of the rune in bytes +- err: An `io.Error` if an error occurs while reading +*/ +reader_read_rune :: proc(r: ^Reader) -> (rr: rune, size: int, err: io.Error) { if r.i >= i64(len(r.s)) { r.prev_rune = -1 return 0, 0, .EOF @@ -115,13 +198,21 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { r.i += 1 return rune(c), 1, nil } - ch, size = utf8.decode_rune_in_string(r.s[r.i:]) + rr, size = utf8.decode_rune_in_string(r.s[r.i:]) r.i += i64(size) return } +/* +Decrements the Reader's index (i) by the size of the last read rune -// decreases the reader offset by the last rune -// can only be used once and after a valid read_rune call +Inputs: +- r: A pointer to a Reader struct + +WARNING: May only be used once and after a valid `read_rune` call + +Returns: +An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success. +*/ reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -133,8 +224,18 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error { r.prev_rune = -1 return nil } +/* +Seeks the Reader's index to a new position -// seeks the reader offset to a wanted offset +Inputs: +- r: A pointer to a Reader struct +- offset: The new offset position +- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`) + +Returns: +- The absolute offset after seeking +- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`) +*/ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r.prev_rune = -1 abs: i64 @@ -155,8 +256,19 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E r.i = abs return abs, nil } +/* +Writes the remaining content of the Reader's string into the provided `io.Writer` -// writes the string content left to read into the io.Writer `w` +Inputs: +- r: A pointer to a Reader struct +- w: The io.Writer to write the remaining content into + +WARNING: Panics if writer writes more bytes than remainig length of string. + +Returns: +- n: The number of bytes written +- err: An io.Error if an error occurs while writing (`.Short_Write`) +*/ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -175,7 +287,12 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { } return } +/* +VTable containing implementations for various `io.Stream` methods +This VTable is used by the Reader struct to provide its functionality +as an `io.Stream`. +*/ @(private) _reader_vtable := io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 33cdafef3..3c55374b7 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1,4 +1,4 @@ -// simple procedures to manipulate UTF-8 encoded strings +// Procedures to manipulate UTF-8 encoded strings package strings import "core:io" @@ -6,58 +6,135 @@ import "core:mem" import "core:unicode" import "core:unicode/utf8" -// returns a clone of the string `s` allocated using the `allocator` +/* +Clones a string + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +A cloned string +*/ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s), allocator, loc) copy(c, s) return string(c[:len(s)]) } +/* +Clones a string safely (returns early with an allocation error on failure) -// returns a clone of the string `s` allocated using the `allocator` +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- str: A cloned string +- err: A mem.Allocator_Error if an error occurs during allocation +*/ clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (str: string, err: mem.Allocator_Error) { c := make([]byte, len(s), allocator, loc) or_return copy(c, s) return string(c[:len(s)]), nil } +/* +Clones a string and appends a null-byte to make it a cstring -// returns a clone of the string `s` allocated using the `allocator` as a cstring -// a nul byte is appended to the clone, to make the cstring safe +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +A cloned cstring with an appended null-byte +*/ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) c[len(s)] = 0 return cstring(&c[0]) } +/* +Transmutes a raw pointer into a string. Non-allocating. -// returns a string from a byte pointer `ptr` and byte length `len` -// the string is valid as long as the parameters stay alive +Inputs: +- ptr: A pointer to the start of the byte sequence +- len: The length of the byte sequence + +NOTE: The created string is only valid as long as the pointer and length are valid. + +Returns: +A string created from the byte pointer and length +*/ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} } +/* +Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0.. string { +NOTE: The created string is only valid as long as the pointer and length are valid. + The string is truncated at the first null-byte encountered. + +Inputs: +- ptr: A pointer to the start of the null-terminated byte sequence +- len: The length of the byte sequence + +Returns: +A string created from the null-terminated byte pointer and length +*/ +string_from_null_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { s := transmute(string)mem.Raw_String{ptr, len} s = truncate_to_byte(s, 0) return s } +/* +Gets the raw byte pointer for the start of a string `str` -// returns the raw ^byte start of the string `str` +Inputs: +- str: The input string + +Returns: +A pointer to the start of the string's bytes +*/ ptr_from_string :: proc(str: string) -> ^byte { d := transmute(mem.Raw_String)str return d.data } +/* +Converts a string `str` to a cstring -// returns the transmute of string `str` to a cstring -// not safe since the origin string may not contain a nul byte +Inputs: +- str: The input string + +WARNING: This is unsafe because the original string may not contain a null-byte. + +Returns: +The converted cstring +*/ unsafe_string_to_cstring :: proc(str: string) -> cstring { d := transmute(mem.Raw_String)str return cstring(d.data) } +/* +Truncates a string `str` at the first occurrence of char/byte `b` -// returns a string truncated to the first time it finds the byte `b` -// uses the `len` of the string `str` when it couldn't find the input +Inputs: +- str: The input string +- b: The byte to truncate the string at + +NOTE: Failure to find the byte results in returning the entire string. + +Returns: +The truncated string +*/ truncate_to_byte :: proc(str: string, b: byte) -> string { n := index_byte(str, b) if n < 0 { @@ -65,9 +142,16 @@ truncate_to_byte :: proc(str: string, b: byte) -> string { } return str[:n] } +/* +Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found -// returns a string truncated to the first time it finds the rune `r` -// uses the `len` of the string `str` when it couldn't find the input +Inputs: +- str: The input string +- r: The rune to truncate the string at + +Returns: +The truncated string +*/ truncate_to_rune :: proc(str: string, r: rune) -> string { n := index_rune(str, r) if n < 0 { @@ -75,51 +159,113 @@ truncate_to_rune :: proc(str: string, r: rune) -> string { } return str[:n] } +/* +Clones a byte array `s` and appends a null-byte -// returns a cloned string of the byte array `s` using the `allocator` -// appends a leading nul byte +*Allocates Using Provided Allocator* + +Inputs: +- s: The byte array to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: `#caller_location`) + +Returns: +A cloned string from the byte array with a null-byte +*/ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) c[len(s)] = 0 return string(c[:len(s)]) } +/* +Clones a cstring `s` as a string -// returns a clone of the cstring `s` using the `allocator` as a string +*Allocates Using Provided Allocator* + +Inputs: +- s: The cstring to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: `#caller_location`) + +Returns: +A cloned string from the cstring +*/ clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string { return clone(string(s), allocator, loc) } +/* +Clones a string from a byte pointer `ptr` and a byte length `len` -// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator` -// same to `string_from_ptr` but allocates +*Allocates Using Provided Allocator* + +Inputs: +- ptr: A pointer to the start of the byte sequence +- len: The length of the byte sequence +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: `#caller_location`) + +NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation + +Returns: +A cloned string from the byte pointer and length +*/ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr(ptr, len) return clone(s, allocator, loc) } - -// overload to clone from a `string`, `[]byte`, `cstring` or a `^byte + length` to a string +// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length clone_from :: proc{ clone, clone_from_bytes, clone_from_cstring, clone_from_ptr, } +/* +Clones a string from a null-terminated cstring `ptr` and a byte length `len` -// returns a cloned string from the cstring `ptr` and a byte length `len` using the `allocator` -// truncates till the first nul byte it finds or the byte len +*Allocates Using Provided Allocator* + +Inputs: +- ptr: A pointer to the start of the null-terminated cstring +- len: The byte length of the cstring +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: `#caller_location`) + +NOTE: Truncates at the first null-byte encountered or the byte length. + +Returns: +A cloned string from the null-terminated cstring and byte length +*/ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) s = truncate_to_byte(s, 0) return clone(s, allocator, loc) } +/* +Compares two strings, returning a value representing which one comes first lexicographically. +-1 for `lhs`; 1 for `rhs`, or 0 if they are equal. -// Compares two strings, returning a value representing which one comes first lexiographically. -// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal. +Inputs: +- lhs: First string for comparison +- rhs: Second string for comparison + +Returns: +-1 if `lhs` comes first, 1 if `rhs` comes first, or 0 if they are equal +*/ compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } +/* +Returns the byte offset of the rune `r` in the string `s`, -1 when not found -// returns the byte offset of the rune `r` in the string `s`, -1 when not found +Inputs: +- s: The input string +- r: The rune to search for + +Returns: +The byte offset of the rune `r` in the string `s`, or -1 if not found +*/ contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { if c == r { @@ -128,48 +274,128 @@ contains_rune :: proc(s: string, r: rune) -> int { } return -1 } - /* - returns true when the string `substr` is contained inside the string `s` +Returns true when the string `substr` is contained inside the string `s` + +Inputs: +- s: The input string +- substr: The substring to search for + +Returns: +`true` if `substr` is contained inside the string `s`, `false` otherwise + +Example: + + import "core:fmt" + import "core:strings" + + contains_example :: proc() { + fmt.println(strings.contains("testing", "test")) + fmt.println(strings.contains("testing", "ing")) + fmt.println(strings.contains("testing", "text")) + } + +Output: + + true + true + false - strings.contains("testing", "test") -> true - strings.contains("testing", "ing") -> true - strings.contains("testing", "text") -> false */ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 } - /* - returns true when the string `s` contains any of the characters inside the string `chars` - - strings.contains_any("test", "test") -> true - strings.contains_any("test", "ts") -> true - strings.contains_any("test", "et") -> true - strings.contains_any("test", "a") -> false +Returns `true` when the string `s` contains any of the characters inside the string `chars` + +Inputs: +- s: The input string +- chars: The characters to search for + +Returns: +`true` if the string `s` contains any of the characters in `chars`, `false` otherwise + +Example: + + import "core:fmt" + import "core:strings" + + contains_any_example :: proc() { + fmt.println(strings.contains_any("test", "test")) + fmt.println(strings.contains_any("test", "ts")) + fmt.println(strings.contains_any("test", "et")) + fmt.println(strings.contains_any("test", "a")) + } + +Output: + + true + true + true + false + */ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 } - /* - returns the utf8 rune count of the string `s` +Returns the UTF-8 rune count of the string `s` + +Inputs: +- s: The input string + +Returns: +The UTF-8 rune count of the string `s` + +Example: + + import "core:fmt" + import "core:strings" + + rune_count_example :: proc() { + fmt.println(strings.rune_count("test")) + fmt.println(strings.rune_count("testö")) // where len("testö") == 6 + } + +Output: + + 4 + 5 - strings.rune_count("test") -> 4 - strings.rune_count("testö") -> 5, where len("testö") -> 6 */ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) } - /* - returns wether the strings `u` and `v` are the same alpha characters - works with utf8 string content and ignores different casings +Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings +Works with UTF-8 string content + +Inputs: +- u: The first string for comparison +- v: The second string for comparison + +Returns: +`true` if the strings `u` and `v` are the same alpha characters (ignoring case) + +Example: + + import "core:fmt" + import "core:strings" + + equal_fold_example :: proc() { + fmt.println(strings.equal_fold("test", "test")) + fmt.println(strings.equal_fold("Test", "test")) + fmt.println(strings.equal_fold("Test", "tEsT")) + fmt.println(strings.equal_fold("test", "tes")) + } + +Output: + + true + true + true + false - strings.equal_fold("test", "test") -> true - strings.equal_fold("Test", "test") -> true - strings.equal_fold("Test", "tEsT") -> true - strings.equal_fold("test", "tes") -> false */ equal_fold :: proc(u, v: string) -> bool { s, t := u, v @@ -213,14 +439,35 @@ equal_fold :: proc(u, v: string) -> bool { return s == t } - /* - return the prefix length common between strings `a` and `b`. +Returns the prefix length common between strings `a` and `b` + +Inputs: +- a: The first input string +- b: The second input string + +Returns: +The prefix length common between strings `a` and `b` + +Example: + + import "core:fmt" + import "core:strings" + + prefix_length_example :: proc() { + fmt.println(strings.prefix_length("testing", "test")) + fmt.println(strings.prefix_length("testing", "te")) + fmt.println(strings.prefix_length("telephone", "te")) + fmt.println(strings.prefix_length("testing", "est")) + } + +Output: + + 4 + 2 + 2 + 0 - strings.prefix_length("testing", "test") -> 4 - strings.prefix_length("testing", "te") -> 2 - strings.prefix_length("telephone", "te") -> 2 - strings.prefix_length("testing", "est") -> 0 */ prefix_length :: proc(a, b: string) -> (n: int) { _len := min(len(a), len(b)) @@ -245,39 +492,101 @@ prefix_length :: proc(a, b: string) -> (n: int) { } return } - /* - return true when the string `prefix` is contained at the start of the string `s` +Determines if a string `s` starts with a given `prefix` + +Inputs: +- s: The string to check for the `prefix` +- prefix: The prefix to look for + +Returns: +`true` if the string `s` starts with the `prefix`, otherwise `false` + +Example: + + import "core:fmt" + import "core:strings" + + has_prefix_example :: proc() { + fmt.println(strings.has_prefix("testing", "test")) + fmt.println(strings.has_prefix("testing", "te")) + fmt.println(strings.has_prefix("telephone", "te")) + fmt.println(strings.has_prefix("testing", "est")) + } + +Output: + + true + true + true + false - strings.has_prefix("testing", "test") -> true - strings.has_prefix("testing", "te") -> true - strings.has_prefix("telephone", "te") -> true - strings.has_prefix("testing", "est") -> false */ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } - /* - returns true when the string `suffix` is contained at the end of the string `s` - good example to use this is for file extensions +Determines if a string `s` ends with a given `suffix` + +Inputs: +- s: The string to check for the `suffix` +- suffix: The suffix to look for + +Returns: +`true` if the string `s` ends with the `suffix`, otherwise `false` + +Example: + + import "core:fmt" + import "core:strings" + + has_suffix_example :: proc() { + fmt.println(strings.has_suffix("todo.txt", ".txt")) + fmt.println(strings.has_suffix("todo.doc", ".txt")) + fmt.println(strings.has_suffix("todo.doc.txt", ".txt")) + } + +Output: + + true + false + true - strings.has_suffix("todo.txt", ".txt") -> true - strings.has_suffix("todo.doc", ".txt") -> false - strings.has_suffix("todo.doc.txt", ".txt") -> true */ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } - /* - returns a combined string from the slice of strings `a` seperated with the `sep` string - allocates the string using the `allocator` +Joins a slice of strings `a` with a `sep` string + +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to join +- sep: The separator string +- allocator: (default is context.allocator) + +Returns: +A combined string from the slice of strings `a` separated with the `sep` string + +Example: + + import "core:fmt" + import "core:strings" + + join_example :: proc() { + a := [?]string { "a", "b", "c" } + fmt.println(strings.join(a[:], " ")) + fmt.println(strings.join(a[:], "-")) + fmt.println(strings.join(a[:], "...")) + } + +Output: + + a b c + a-b-c + a...b...c - a := [?]string { "a", "b", "c" } - b := strings.join(a[:], " ") -> "a b c" - c := strings.join(a[:], "-") -> "a-b-c" - d := strings.join(a[:], "...") -> "a...b...c" */ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -297,7 +606,20 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string } return string(b) } +/* +Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to join +- sep: The separator string +- allocator: (default is context.allocator) + +Returns: +- str: A combined string from the slice of strings `a` separated with the `sep` string +- err: An error if allocation failed, otherwise `nil` +*/ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) { if len(a) == 0 { return "", nil @@ -316,14 +638,32 @@ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> ( } return string(b), nil } - /* - returns a combined string from the slice of strings `a` without a seperator - allocates the string using the `allocator` - +Returns a combined string from the slice of strings `a` without a separator + +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to concatenate +- allocator: (default is context.allocator) + +Returns: +The concatenated string + +Example: + + import "core:fmt" + import "core:strings" + + concatenate_example :: proc() { + a := [?]string { "a", "b", "c" } + fmt.println(strings.concatenate(a[:])) + } + +Output: + + abc - a := [?]string { "a", "b", "c" } - b := strings.concatenate(a[:]) -> "abc" */ concatenate :: proc(a: []string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -341,7 +681,18 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string { } return string(b) } +/* +Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to concatenate +- allocator: (default is context.allocator) + +Returns: +The concatenated string, and an error if allocation fails +*/ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { if len(a) == 0 { return "", nil @@ -358,14 +709,37 @@ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: s } return string(b), nil } - /* - `rune_offset` and `rune_length` are in runes, not bytes. - If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`. +Returns a substring of the input string `s` with the specified rune offset and length + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to cut +- rune_offset: The starting rune index (default is 0). In runes, not bytes. +- rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes. +- allocator: (default is context.allocator) + +Returns: +The substring + +Example: + + import "core:fmt" + import "core:strings" + + cut_example :: proc() { + fmt.println(strings.cut("some example text", 0, 4)) // -> "some" + fmt.println(strings.cut("some example text", 2, 2)) // -> "me" + fmt.println(strings.cut("some example text", 5, 7)) // -> "example" + } + +Output: + + some + me + example - strings.cut("some example text", 0, 4) -> "some" - strings.cut("some example text", 2, 2) -> "me" - strings.cut("some example text", 5, 7) -> "example" */ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) { s := s; rune_length := rune_length @@ -418,7 +792,25 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator : } return string(buf[:byte_offset]) } +/* +Splits the input string `s` into a slice of substrings separated by the specified `sep` string +*Allocates Using Provided Allocator* + +*Used Internally - Private Function* + +Inputs: +- s: The input string to split +- sep: The separator string +- sep_save: A flag determining if the separator should be saved in the resulting substrings +- n: The maximum number of substrings to return, returns `nil` without alloc when `n=0` +- allocator: (default is context.allocator) + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Returns: +A slice of substrings +*/ @private _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string { s, n := s_, n_ @@ -466,58 +858,155 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato return res[:i+1] } - /* - Splits a string into parts, based on a separator. - Returned strings are substrings of 's'. - ``` - s := "aaa.bbb.ccc.ddd.eee" // 5 parts - ss := split(s, ".") - fmt.println(ss) // [aaa, bbb, ccc, ddd, eee] - ``` +Splits a string into parts based on a separator. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (default is context.allocator). + +Returns: A slice of strings, each representing a part of the split string. + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_example :: proc() { + s := "aaa.bbb.ccc.ddd.eee" // 5 parts + ss := strings.split(s, ".") + fmt.println(ss) + } + +Output: + + ["aaa", "bbb", "ccc", "ddd", "eee"] + */ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) } - /* - Splits a string into a total of 'n' parts, based on a separator. - Returns fewer parts if there wasn't enough occurrences of the separator. - Returned strings are substrings of 's'. - ``` - s := "aaa.bbb.ccc.ddd.eee" // 5 parts present - ss := split_n(s, ".", 3) // total of 3 wanted - fmt.println(ss) // [aaa, bbb, ccc.ddd.eee] - ``` +Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (default is context.allocator) + +Returns: A slice of strings, each representing a part of the split string. + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_n_example :: proc() { + s := "aaa.bbb.ccc.ddd.eee" // 5 parts present + ss := strings.split_n(s, ".",3) // total of 3 wanted + fmt.println(ss) + } + +Output: + + ["aaa", "bbb", "ccc.ddd.eee"] + */ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, 0, n, allocator) } - /* - splits the string `s` after the seperator string `sep` appears - returns the slice of split strings allocated using `allocator` +Splits a string into parts after the separator, retaining it in the substrings. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (default is context.allocator). + +Returns: +A slice of strings, each representing a part of the split string after the separator. + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_after_example :: proc() { + a := "aaa.bbb.ccc.ddd.eee" // 5 parts + aa := strings.split_after(a, ".") + fmt.println(aa) + } + +Output: + + ["aaa.", "bbb.", "ccc.", "ddd.", "eee"] - a := "aaa.bbb.ccc.ddd.eee" - aa := strings.split_after(a, ".") - fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] */ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), -1, allocator) } - /* - splits the string `s` after the seperator string `sep` appears into a total of `n` parts - returns the slice of split strings allocated using `allocator` +Splits a string into a total of `n` parts after the separator. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- n: The maximum number of parts to split the string into. +- allocator: (default is context.allocator) + +Returns: +A slice of strings with `n` parts or fewer if there weren't + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_after_n_example :: proc() { + a := "aaa.bbb.ccc.ddd.eee" + aa := strings.split_after_n(a, ".", 3) + fmt.println(aa) + } + +Output: + + ["aaa.", "bbb.", "ccc.ddd.eee"] - a := "aaa.bbb.ccc.ddd.eee" - aa := strings.split_after(a, ".") - fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) } +/* +Searches for the first occurrence of `sep` in the given string and returns the substring +up to (but not including) the separator, as well as a boolean indicating success. +*Used Internally - Private Function* + +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. +- sep_save: Number of characters from the separator to include in the result. + +Returns: +A tuple containing the resulting substring and a boolean indicating success. +*/ @private _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { // stop once the string is empty or nil @@ -545,15 +1034,36 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, } return } - /* - split the ^string `s` by the byte seperator `sep` in an iterator fashion - consumes the original string till the end, leaving the string `s` with len == 0 +Splits the input string by the byte separator in an iterator fashion. - text := "a.b.c.d.e" - for str in strings.split_by_byte_iterator(&text, '.') { - fmt.eprintln(str) // every loop -> a b c d e +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The byte separator to search for. + +Returns: +A tuple containing the resulting substring and a boolean indicating success. + +Example: + + import "core:fmt" + import "core:strings" + + split_by_byte_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_by_byte_iterator(&text, '.') { + fmt.println(str) // every loop -> a b c d e + } } + +Output: + + a + b + c + d + e + */ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) @@ -569,34 +1079,87 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { } return } - /* - split the ^string `s` by the seperator string `sep` in an iterator fashion - consumes the original string till the end +Splits the input string by the separator string in an iterator fashion. +Destructively consumes the original string until the end. - text := "a.b.c.d.e" - for str in strings.split_iterator(&text, ".") { - fmt.eprintln(str) // every loop -> a b c d e +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. + +Returns: +A tuple containing the resulting substring and a boolean indicating success. + +Example: + + import "core:fmt" + import "core:strings" + + split_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_iterator(&text, ".") { + fmt.println(str) + } } + +Output: + + a + b + c + d + e + */ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, 0) } - /* - split the ^string `s` after every seperator string `sep` in an iterator fashion - consumes the original string till the end +Splits the input string after every separator string in an iterator fashion. +Destructively consumes the original string until the end. - text := "a.b.c.d.e" - for str in strings.split_after_iterator(&text, ".") { - fmt.eprintln(str) // every loop -> a. b. c. d. e +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. + +Returns: +A tuple containing the resulting substring and a boolean indicating success. + +Example: + + import "core:fmt" + import "core:strings" + + split_after_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_after_iterator(&text, ".") { + fmt.println(str) + } } + +Output: + + a. + b. + c. + d. + e + */ split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, len(sep)) } +/* +Trims the carriage return character from the end of the input string. +*Used Internally - Private Function* +Inputs: +- s: The input string to trim. + +Returns: +The trimmed string as a slice of the original. +*/ @(private) _trim_cr :: proc(s: string) -> string { n := len(s) @@ -607,14 +1170,33 @@ _trim_cr :: proc(s: string) -> string { } return s } - /* - split the string `s` at every line break '\n' - return an allocated slice of strings +Splits the input string at every line break `\n`. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- allocator: (default is context.allocator) + +Returns: +A slice (allocated) of the split string (slices into original string) + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines(a) + fmt.println(b) + } + +Output: + + ["a", "b", "c", "d", "e"] - a := "a\nb\nc\nd\ne" - b := strings.split_lines(a) - fmt.eprintln(b) // [a, b, c, d, e] */ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -624,14 +1206,36 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string { } return lines } - /* - split the string `s` at every line break '\n' for `n` parts - return an allocated slice of strings +Splits the input string at every line break `\n` for `n` parts. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- n: The number of parts to split into. +- allocator: (default is context.allocator) + +Returns: +A slice (allocated) of the split string (slices into original string) + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_n_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_n(a, 3) + fmt.println(b) + } + +Output: + + ["a", "b", "c\nd\ne"] - a := "a\nb\nc\nd\ne" - b := strings.split_lines_n(a, 3) - fmt.eprintln(b) // [a, b, c, d\ne\n] */ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -641,14 +1245,35 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st } return lines } - /* - split the string `s` at every line break '\n' leaving the '\n' in the resulting strings - return an allocated slice of strings +Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- allocator: (default is context.allocator) + +Returns: +A slice (allocated) of the split string (slices into original string), with `\n` included. + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_after_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after(a) + fmt.println(b) + } + +Output: + + ["a\n", "b\n", "c\n", "d\n", "e"] - a := "a\nb\nc\nd\ne" - b := strings.split_lines_after(a) - fmt.eprintln(b) // [a\n, b\n, c\n, d\n, e\n] */ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -658,15 +1283,37 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string } return lines } - /* - split the string `s` at every line break '\n' leaving the '\n' in the resulting strings - only runs for `n` parts - return an allocated slice of strings +Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. +Only runs for n parts. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- n: The number of parts to split into. +- allocator: (default is context.allocator) + +Returns: +A slice (allocated) of the split string (slices into original string), with `\n` included. + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_after_n_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after_n(a, 3) + fmt.println(b) + } + +Output: + + ["a\n", "b\n", "c\nd\ne"] - a := "a\nb\nc\nd\ne" - b := strings.split_lines_after_n(a, 3) - fmt.eprintln(b) // [a\n, b\n, c\n, d\ne\n] */ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -676,45 +1323,105 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - } return lines } - /* - split the string `s` at every line break '\n' - returns the current split string every iteration till the string is consumed +Splits the input string at every line break `\n`. +Returns the current split string every iteration until the string is consumed. - text := "a\nb\nc\nd\ne" - for str in strings.split_lines_iterator(&text) { - fmt.eprintln(text) // every loop -> a b c d e +Inputs: +- s: Pointer to the input string, which is modified during the search. + +Returns: +A tuple containing the resulting substring and a boolean indicating success. + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_iterator_example :: proc() { + text := "a\nb\nc\nd\ne" + for str in strings.split_lines_iterator(&text) { + fmt.print(str) // every loop -> a b c d e + } + fmt.print("\n") } + +Output: + + abcde + */ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, 0) or_return return _trim_cr(line), true } - /* - split the string `s` at every line break '\n' - returns the current split string every iteration till the string is consumed +Splits the input string at every line break `\n`. +Returns the current split string with line breaks included every iteration until the string is consumed. - text := "a\nb\nc\nd\ne" - for str in strings.split_lines_after_iterator(&text) { - fmt.eprintln(text) // every loop -> a\n b\n c\n d\n e\n +Inputs: +- s: Pointer to the input string, which is modified during the search. + +Returns: +A tuple containing the resulting substring with line breaks included and a boolean indicating success. + +Example: + + import "core:fmt" + import "core:strings" + + split_lines_after_iterator_example :: proc() { + text := "a\nb\nc\nd\ne\n" + for str in strings.split_lines_after_iterator(&text) { + fmt.print(str) // every loop -> a\n b\n c\n d\n e\n + } } + +Output: + + a + b + c + d + e + */ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, len(sep)) or_return return _trim_cr(line), true } - /* - returns the byte offset of the first byte `c` in the string `s` it finds, -1 when not found - can't find utf8 based runes +Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found. +NOTE: Can't find UTF-8 based runes. + +Inputs: +- s: The input string to search in. +- c: The byte to search for. + +Returns: +The byte offset of the first occurrence of `c` in `s`, or -1 if not found. + +Example: + + import "core:fmt" + import "core:strings" + + index_byte_example :: proc() { + fmt.println(strings.index_byte("test", 't')) + fmt.println(strings.index_byte("test", 'e')) + fmt.println(strings.index_byte("test", 'x')) + fmt.println(strings.index_byte("teäst", 'ä')) + } + +Output: + + 0 + 1 + -1 + -1 - strings.index_byte("test", 't') -> 0 - strings.index_byte("test", 'e') -> 1 - strings.index_byte("test", 'x') -> -1 - strings.index_byte("teäst", 'ä') -> -1 */ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { @@ -724,15 +1431,37 @@ index_byte :: proc(s: string, c: byte) -> int { } return -1 } - /* - returns the byte offset of the last byte `c` in the string `s` it finds, -1 when not found - can't find utf8 based runes +Returns the byte offset of the last byte `c` in the string `s`, -1 when not found. + +Inputs: +- s: The input string to search in. +- c: The byte to search for. + +Returns: +The byte offset of the last occurrence of `c` in `s`, or -1 if not found. + +NOTE: Can't find UTF-8 based runes. + +Example: + + import "core:fmt" + import "core:strings" + + last_index_byte_example :: proc() { + fmt.println(strings.last_index_byte("test", 't')) + fmt.println(strings.last_index_byte("test", 'e')) + fmt.println(strings.last_index_byte("test", 'x')) + fmt.println(strings.last_index_byte("teäst", 'ä')) + } + +Output: + + 3 + 1 + -1 + -1 - strings.index_byte("test", 't') -> 3 - strings.index_byte("test", 'e') -> 1 - strings.index_byte("test", 'x') -> -1 - strings.index_byte("teäst", 'ä') -> -1 */ last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { @@ -742,20 +1471,44 @@ last_index_byte :: proc(s: string, c: byte) -> int { } return -1 } - - /* - returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found - avoids invalid runes +Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found. +Invalid runes return -1 + +Inputs: +- s: The input string to search in. +- r: The rune to search for. + +Returns: +The byte offset of the first occurrence of `r` in `s`, or -1 if not found. + +Example: + + import "core:fmt" + import "core:strings" + + index_rune_example :: proc() { + fmt.println(strings.index_rune("abcädef", 'x')) + fmt.println(strings.index_rune("abcädef", 'a')) + fmt.println(strings.index_rune("abcädef", 'b')) + fmt.println(strings.index_rune("abcädef", 'c')) + fmt.println(strings.index_rune("abcädef", 'ä')) + fmt.println(strings.index_rune("abcädef", 'd')) + fmt.println(strings.index_rune("abcädef", 'e')) + fmt.println(strings.index_rune("abcädef", 'f')) + } + +Output: + + -1 + 0 + 1 + 2 + 3 + 5 + 6 + 7 - strings.index_rune("abcädef", 'x') -> -1 - strings.index_rune("abcädef", 'a') -> 0 - strings.index_rune("abcädef", 'b') -> 1 - strings.index_rune("abcädef", 'c') -> 2 - strings.index_rune("abcädef", 'ä') -> 3 - strings.index_rune("abcädef", 'd') -> 5 - strings.index_rune("abcädef", 'e') -> 6 - strings.index_rune("abcädef", 'f') -> 7 */ index_rune :: proc(s: string, r: rune) -> int { switch { @@ -779,14 +1532,35 @@ index_rune :: proc(s: string, r: rune) -> int { } @private PRIME_RABIN_KARP :: 16777619 - /* - returns the byte offset of the string `substr` in the string `s`, -1 when not found - - strings.index("test", "t") -> 0 - strings.index("test", "te") -> 0 - strings.index("test", "st") -> 2 - strings.index("test", "tt") -> -1 +Returns the byte offset of the string `substr` in the string `s`, -1 when not found. + +Inputs: +- s: The input string to search in. +- substr: The substring to search for. + +Returns: +The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. + +Example: + + import "core:fmt" + import "core:strings" + + index_example :: proc() { + fmt.println(strings.index("test", "t")) + fmt.println(strings.index("test", "te")) + fmt.println(strings.index("test", "st")) + fmt.println(strings.index("test", "tt")) + } + +Output: + + 0 + 0 + 2 + -1 + */ index :: proc(s, substr: string) -> int { hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -837,14 +1611,35 @@ index :: proc(s, substr: string) -> int { } return -1 } - /* - returns the last byte offset of the string `substr` in the string `s`, -1 when not found - - strings.index("test", "t") -> 3 - strings.index("test", "te") -> 0 - strings.index("test", "st") -> 2 - strings.index("test", "tt") -> -1 +Returns the last byte offset of the string `substr` in the string `s`, -1 when not found. + +Inputs: +- s: The input string to search in. +- substr: The substring to search for. + +Returns: +The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. + +Example: + + import "core:fmt" + import "core:strings" + + last_index_example :: proc() { + fmt.println(strings.last_index("test", "t")) + fmt.println(strings.last_index("test", "te")) + fmt.println(strings.last_index("test", "st")) + fmt.println(strings.last_index("test", "tt")) + } + +Output: + + 3 + 0 + 2 + -1 + */ last_index :: proc(s, substr: string) -> int { hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -893,15 +1688,37 @@ last_index :: proc(s, substr: string) -> int { } return -1 } - /* - returns the index of any first char of `chars` found in `s`, -1 if not found - - strings.index_any("test", "s") -> 2 - strings.index_any("test", "se") -> 1 - strings.index_any("test", "et") -> 0 - strings.index_any("test", "set") -> 0 - strings.index_any("test", "x") -> -1 +Returns the index of any first char of `chars` found in `s`, -1 if not found. + +Inputs: +- s: The input string to search in. +- chars: The characters to look for + +Returns: +The index of the first character of `chars` found in `s`, or -1 if not found. + +Example: + + import "core:fmt" + import "core:strings" + + index_any_example :: proc() { + fmt.println(strings.index_any("test", "s")) + fmt.println(strings.index_any("test", "se")) + fmt.println(strings.index_any("test", "et")) + fmt.println(strings.index_any("test", "set")) + fmt.println(strings.index_any("test", "x")) + } + +Output: + + 2 + 1 + 0 + 0 + -1 + */ index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -934,16 +1751,37 @@ index_any :: proc(s, chars: string) -> int { } return -1 } - /* - returns the last matching index in `s` of any char in `chars` found in `s`, -1 if not found - iterates the string in reverse +Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse. + +Inputs: +- s: The string to search in +- chars: The characters to look for + +Returns: +The index of the last matching character, or -1 if not found + +Example: + + import "core:fmt" + import "core:strings" + + last_index_any_example :: proc() { + fmt.println(strings.last_index_any("test", "s")) + fmt.println(strings.last_index_any("test", "se")) + fmt.println(strings.last_index_any("test", "et")) + fmt.println(strings.last_index_any("test", "set")) + fmt.println(strings.last_index_any("test", "x")) + } + +Output: + + 2 + 2 + 3 + 3 + -1 - strings.last_index_any("test", "s") -> 2 - strings.last_index_any("test", "se") -> 2 - strings.last_index_any("test", "et") -> 3 - strings.last_index_any("test", "set") -> 3 - strings.last_index_any("test", "x") -> -1 */ last_index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -993,7 +1831,16 @@ last_index_any :: proc(s, chars: string) -> int { } return -1 } +/* +Finds the first occurrence of any substring in `substrs` within `s` +Inputs: +- s: The string to search in +- substrs: The substrings to look for + +Returns: +A tuple containing the index of the first matching substring, and its length (width) +*/ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { idx = -1 if s == "" || len(substrs) <= 0 { @@ -1023,16 +1870,37 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { } return } - /* - returns the count of the string `substr` found in the string `s` - returns the rune_count + 1 of the string `s` on empty `substr` +Counts the number of non-overlapping occurrences of `substr` in `s` + +Inputs: +- s: The string to search in +- substr: The substring to count + +Returns: +The number of occurrences of `substr` in `s`, returns the rune_count + 1 of the string `s` on empty `substr` + +Example: + + import "core:fmt" + import "core:strings" + + count_example :: proc() { + fmt.println(strings.count("abbccc", "a")) + fmt.println(strings.count("abbccc", "b")) + fmt.println(strings.count("abbccc", "c")) + fmt.println(strings.count("abbccc", "ab")) + fmt.println(strings.count("abbccc", " ")) + } + +Output: + + 1 + 2 + 3 + 1 + 0 - strings.count("abbccc", "a") -> 1 - strings.count("abbccc", "b") -> 2 - strings.count("abbccc", "c") -> 3 - strings.count("abbccc", "ab") -> 1 - strings.count("abbccc", " ") -> 0 */ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case @@ -1068,12 +1936,34 @@ count :: proc(s, substr: string) -> int { } return n } - /* - repeats the string `s` multiple `count` times and returns the allocated string - panics when `count` is below 0 +Repeats the string `s` `count` times, concatenating the result + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to repeat +- count: The number of times to repeat `s` +- allocator: (default is context.allocator) + +Returns: +The concatenated repeated string + +WARNING: Panics if count < 0 + +Example: + + import "core:fmt" + import "core:strings" + + repeat_example :: proc() { + fmt.println(strings.repeat("abc", 2)) + } + +Output: + + abcabc - strings.repeat("abc", 2) -> "abcabc" */ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string { if count < 0 { @@ -1090,28 +1980,75 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string } return string(b) } - /* - replaces all instances of `old` in the string `s` with the `new` string - returns the `output` string and true when an a allocation through a replace happened +Replaces all occurrences of `old` in `s` with `new` + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to modify +- old: The substring to replace +- new: The substring to replace `old` with +- allocator: The allocator to use for the new string (default is context.allocator) + +Returns: +A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement + +Example: + + import "core:fmt" + import "core:strings" + + replace_all_example :: proc() { + fmt.println(strings.replace_all("xyzxyz", "xyz", "abc")) + fmt.println(strings.replace_all("xyzxyz", "abc", "xyz")) + fmt.println(strings.replace_all("xyzxyz", "xy", "z")) + } + +Output: + + abcabc true + xyzxyz false + zzzz true - strings.replace_all("xyzxyz", "xyz", "abc") -> "abcabc", true - strings.replace_all("xyzxyz", "abc", "xyz") -> "xyzxyz", false - strings.replace_all("xyzxyz", "xy", "z") -> "zzzz", true */ replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) } - /* - replaces `n` instances of `old` in the string `s` with the `new` string - if n < 0, no limit on the number of replacements - returns the `output` string and true when an a allocation through a replace happened +Replaces n instances of old in the string s with the new string + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- old: The substring to be replaced +- new: The replacement string +- n: The number of instances to replace (if `n < 0`, no limit on the number of replacements) +- allocator: (default: context.allocator) + +Returns: +A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement + +Example: + + import "core:fmt" + import "core:strings" + + replace_example :: proc() { + fmt.println(strings.replace("xyzxyz", "xyz", "abc", 2)) + fmt.println(strings.replace("xyzxyz", "xyz", "abc", 1)) + fmt.println(strings.replace("xyzxyz", "abc", "xyz", -1)) + fmt.println(strings.replace("xyzxyz", "xy", "z", -1)) + } + +Output: + + abcabc true + abcxyz true + xyzxyz false + zzzz true - strings.replace("xyzxyz", "xyz", "abc", 2) -> "abcabc", true - strings.replace("xyzxyz", "xyz", "abc", 1) -> "abcxyz", true - strings.replace("xyzxyz", "abc", "xyz", -1) -> "xyzxyz", false - strings.replace("xyzxyz", "xy", "z", -1) -> "zzzz", true */ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { if old == new || n == 0 { @@ -1152,44 +2089,88 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> output = string(t[0:w]) return } - /* - removes the `key` string `n` times from the `s` string - if n < 0, no limit on the number of removes - returns the `output` string and true when an a allocation through a remove happened +Removes the key string `n` times from the `s` string + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- key: The substring to be removed +- n: The number of instances to remove (if `n < 0`, no limit on the number of removes) +- allocator: (default: context.allocator) + +Returns: +A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal + +Example: + + import "core:fmt" + import "core:strings" + + remove_example :: proc() { + fmt.println(strings.remove("abcabc", "abc", 1)) + fmt.println(strings.remove("abcabc", "abc", -1)) + fmt.println(strings.remove("abcabc", "a", -1)) + fmt.println(strings.remove("abcabc", "x", -1)) + } + +Output: + + abc true + true + bcbc true + abcabc false - strings.remove("abcabc", "abc", 1) -> "abc", true - strings.remove("abcabc", "abc", -1) -> "", true - strings.remove("abcabc", "a", -1) -> "bcbc", true - strings.remove("abcabc", "x", -1) -> "abcabc", false */ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) } - /* - removes all the `key` string instanes from the `s` string - returns the `output` string and true when an a allocation through a remove happened +Removes all the `key` string instances from the `s` string + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- key: The substring to be removed +- allocator: (default: context.allocator) + +Returns: +A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal + +Example: + + import "core:fmt" + import "core:strings" + + remove_all_example :: proc() { + fmt.println(strings.remove_all("abcabc", "abc")) + fmt.println(strings.remove_all("abcabc", "a")) + fmt.println(strings.remove_all("abcabc", "x")) + } + +Output: + + true + bcbc true + abcabc false - strings.remove("abcabc", "abc") -> "", true - strings.remove("abcabc", "a") -> "bcbc", true - strings.remove("abcabc", "x") -> "abcabc", false */ remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) } - +// Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} -// return true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' +// Returns true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' is_ascii_space :: proc(r: rune) -> bool { if r < utf8.RUNE_SELF { return _ascii_space[u8(r)] } return false } - -// returns true when the `r` rune is any asci or utf8 based whitespace +// Returns true if the `r` rune is any ASCII or UTF-8 based whitespace character is_space :: proc(r: rune) -> bool { if r < 0x2000 { switch r { @@ -1207,24 +2188,45 @@ is_space :: proc(r: rune) -> bool { } return false } - -// returns true when the `r` rune is a nul byte +// Returns true if the `r` rune is a null-byte (`0x0`) is_null :: proc(r: rune) -> bool { return r == 0x0000 } - /* - runs trough the `s` string linearly and watches wether the `p` procedure matches the `truth` bool - returns the rune offset or -1 when no match was found +Find the index of the first rune `r` in string `s` for which procedure `p` returns the same as truth, or -1 if no such rune appears. - call :: proc(r: rune) -> bool { - return r == 'a' +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean +- truth: The boolean value to be matched (default: `true`) + +Returns: +The index of the first matching rune, or -1 if no match was found + +Example: + + import "core:fmt" + import "core:strings" + + index_proc_example :: proc() { + call :: proc(r: rune) -> bool { + return r == 'a' + } + fmt.println(strings.index_proc("abcabc", call)) + fmt.println(strings.index_proc("cbacba", call)) + fmt.println(strings.index_proc("cbacba", call, false)) + fmt.println(strings.index_proc("abcabc", call, false)) + fmt.println(strings.index_proc("xyz", call)) } - strings.index_proc("abcabc", call) -> 0 - strings.index_proc("cbacba", call) -> 2 - strings.index_proc("cbacba", call, false) -> 0 - strings.index_proc("abcabc", call, false) -> 1 - strings.index_proc("xyz", call) -> -1 + +Output: + + 0 + 2 + 0 + 1 + -1 + */ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { @@ -1234,8 +2236,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { } return -1 } - -// same as `index_proc` but with a `p` procedure taking a rawptr for state +// Same as `index_proc`, but the procedure p takes a raw pointer for state index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { for r, i in s { if p(state, r) == truth { @@ -1244,8 +2245,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r } return -1 } - -// same as `index_proc` but runs through the string in reverse +// Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -1257,8 +2257,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int } return -1 } - -// same as `index_proc_with_state` but runs through the string in reverse +// Same as `index_proc_with_state`, runs through the string in reverse last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -1270,16 +2269,32 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return -1 } - /* - trims the input string `s` until the procedure `p` returns false - does not allocate - only returns a cut variant of the input string - returns an empty string when no match was found at all +Trims the input string `s` from the left until the procedure `p` returns false - find :: proc(r: rune) -> bool { - return r != 'i' +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean + +Returns: +The trimmed string as a slice of the original + +Example: + + import "core:fmt" + import "core:strings" + + trim_left_proc_example :: proc() { + find :: proc(r: rune) -> bool { + return r == 'x' + } + fmt.println(strings.trim_left_proc("xxxxxxtesting", find)) } - strings.trim_left_proc("testing", find) -> "ing" + +Output: + + testing + */ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := index_proc(s, p, false) @@ -1288,10 +2303,16 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { } return s[i:] } - /* - trims the input string `s` until the procedure `p` with state returns false - returns an empty string when no match was found at all +Trims the input string `s` from the left until the procedure `p` with state returns false + +Inputs: +- s: The input string +- p: A procedure that takes a raw pointer and a rune and returns a boolean +- state: The raw pointer to be passed to the procedure `p` + +Returns: +The trimmed string as a slice of the original */ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := index_proc_with_state(s, p, state, false) @@ -1300,16 +2321,32 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat } return s[i:] } - /* - trims the input string `s` from the right until the procedure `p` returns false - does not allocate - only returns a cut variant of the input string - returns an empty string when no match was found at all +Trims the input string `s` from the right until the procedure `p` returns `false` - find :: proc(r: rune) -> bool { - return r != 't' +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean + +Returns: +The trimmed string as a slice of the original + +Example: + + import "core:fmt" + import "core:strings" + + trim_right_proc_example :: proc() { + find :: proc(r: rune) -> bool { + return r != 't' + } + fmt.println(strings.trim_right_proc("testing", find)) } - strings.trim_left_proc("testing", find) -> "test" + +Output: + + test + */ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := last_index_proc(s, p, false) @@ -1321,10 +2358,16 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { } return s[0:i] } - /* - trims the input string `s` from the right until the procedure `p` with state returns false - returns an empty string when no match was found at all +Trims the input string `s` from the right until the procedure `p` with state returns `false` + +Inputs: +- s: The input string +- p: A procedure that takes a raw pointer and a rune and returns a boolean +- state: The raw pointer to be passed to the procedure `p` + +Returns: +The trimmed string as a slice of the original, empty when no match */ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := last_index_proc_with_state(s, p, state, false) @@ -1336,8 +2379,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return s[0:i] } - -// procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison +// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison is_in_cutset :: proc(state: rawptr, r: rune) -> bool { if state == nil { return false @@ -1350,8 +2392,16 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { } return false } +/* +Trims the cutset string from the `s` string -// trims the `cutset` string from the `s` string +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from the left of the input string + +Returns: +The trimmed string as a slice of the original +*/ trim_left :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -1359,8 +2409,16 @@ trim_left :: proc(s: string, cutset: string) -> string { state := cutset return trim_left_proc_with_state(s, is_in_cutset, &state) } +/* +Trims the cutset string from the `s` string from the right -// trims the `cutset` string from the `s` string from the right +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from the right of the input string + +Returns: +The trimmed string as a slice of the original +*/ trim_right :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -1368,48 +2426,115 @@ trim_right :: proc(s: string, cutset: string) -> string { state := cutset return trim_right_proc_with_state(s, is_in_cutset, &state) } +/* +Trims the cutset string from the `s` string, both from left and right -// trims the `cutset` string from the `s` string, both from left and right +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from both sides of the input string + +Returns: +The trimmed string as a slice of the original +*/ trim :: proc(s: string, cutset: string) -> string { return trim_right(trim_left(s, cutset), cutset) } +/* +Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" -// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t" +Inputs: +- s: The input string + +Returns: +The trimmed string as a slice of the original +*/ trim_left_space :: proc(s: string) -> string { return trim_left_proc(s, is_space) } +/* +Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" -// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz" +Inputs: +- s: The input string + +Returns: +The trimmed string as a slice of the original +*/ trim_right_space :: proc(s: string) -> string { return trim_right_proc(s, is_space) } +/* +Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" -// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz" +Inputs: +- s: The input string + +Returns: +The trimmed string as a slice of the original +*/ trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)) } +/* +Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" -// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00" +Inputs: +- s: The input string + +Returns: +The trimmed string as a slice of the original +*/ trim_left_null :: proc(s: string) -> string { return trim_left_proc(s, is_null) } +/* +Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" -// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing" +Inputs: +- s: The input string + +Returns: +The trimmed string as a slice of the original +*/ trim_right_null :: proc(s: string) -> string { return trim_right_proc(s, is_null) } +/* +Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" -// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing" +Inputs: +- s: The input string +Returns: +The trimmed string as a slice of the original +*/ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) } - /* - trims a `prefix` string from the start of the `s` string and returns the trimmed string - returns the input string `s` when no prefix was found +Trims a `prefix` string from the start of the `s` string and returns the trimmed string + +Inputs: +- s: The input string +- prefix: The prefix string to be removed + +Returns: +The trimmed string as a slice of original, or the input string if no prefix was found + +Example: + + import "core:fmt" + import "core:strings" + + trim_prefix_example :: proc() { + fmt.println(strings.trim_prefix("testing", "test")) + fmt.println(strings.trim_prefix("testing", "abc")) + } + +Output: + + ing + testing - strings.trim_prefix("testing", "test") -> "ing" - strings.trim_prefix("testing", "abc") -> "testing" */ trim_prefix :: proc(s, prefix: string) -> string { if has_prefix(s, prefix) { @@ -1417,13 +2542,31 @@ trim_prefix :: proc(s, prefix: string) -> string { } return s } - /* - trims a `suffix` string from the end of the `s` string and returns the trimmed string - returns the input string `s` when no suffix was found +Trims a `suffix` string from the end of the `s` string and returns the trimmed string + +Inputs: +- s: The input string +- suffix: The suffix string to be removed + +Returns: +The trimmed string as a slice of original, or the input string if no suffix was found + +Example: + + import "core:fmt" + import "core:strings" + + trim_suffix_example :: proc() { + fmt.println(strings.trim_suffix("todo.txt", ".txt")) + fmt.println(strings.trim_suffix("todo.doc", ".txt")) + } + +Output: + + todo + todo.doc - strings.trim_suffix("todo.txt", ".txt") -> "todo" - strings.trim_suffix("todo.doc", ".txt") -> "todo.doc" */ trim_suffix :: proc(s, suffix: string) -> string { if has_suffix(s, suffix) { @@ -1431,14 +2574,36 @@ trim_suffix :: proc(s, suffix: string) -> string { } return s } - /* - splits the input string `s` by all possible `substrs` []string - returns the allocated []string, nil on any empty substring or no matches +Splits the input string `s` by all possible `substrs` and returns an allocated array of strings + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- substrs: An array of substrings used for splitting +- allocator: (default is context.allocator) + +Returns: +An array of strings, or nil on empty substring or no matches + +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +Example: + + import "core:fmt" + import "core:strings" + + split_multi_example :: proc() { + splits := [?]string { "---", "~~~", ".", "_", "," } + res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) + fmt.println(res) // -> [testing, this, out, nice, done, last] + } + +Output: + + ["testing", "this", "out", "nice", "done", "last"] - splits := [?]string { "---", "~~~", ".", "_", "," } - res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) - fmt.eprintln(res) // -> [testing, this, out, nice, done, last] */ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check { if s == "" || len(substrs) <= 0 { @@ -1480,15 +2645,38 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator assert(len(results) == n) return results[:] } - /* - splits the input string `s` by all possible `substrs` []string in an iterator fashion - returns the split string every iteration, the full string on no match - splits := [?]string { "---", "~~~", ".", "_", "," } - it := "testing,this.out_nice---done~~~last" - for str in strings.split_multi_iterate(&it, splits[:]) { - fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last] +Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match. + +Inputs: +- it: A pointer to the input string +- substrs: An array of substrings used for splitting + +Returns: +A tuple containing the split string and a boolean indicating success or failure + +Example: + + import "core:fmt" + import "core:strings" + + split_multi_iterate_example :: proc() { + it := "testing,this.out_nice---done~~~last" + splits := [?]string { "---", "~~~", ".", "_", "," } + for str in strings.split_multi_iterate(&it, splits[:]) { + fmt.println(str) + } } + +Output: + + testing + this + out + nice + done + last + */ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { if it == nil || len(it) == 0 || len(substrs) <= 0 { @@ -1515,9 +2703,34 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: ok = true return } +/* +Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once. -// scrub scruvs invalid utf-8 characters and replaces them with the replacement string -// Adjacent invalid bytes are only replaced once +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- replacement: The string used to replace invalid UTF-8 characters +- allocator: (default is context.allocator) + +Returns: +A new string with invalid UTF-8 characters replaced + +Example: + + import "core:fmt" + import "core:strings" + + scrub_example :: proc() { + text := "Hello\xC0\x80World" + fmt.println(strings.scrub(text, "?")) // -> "Hello?World" + } + +Output: + + Hello? + +*/ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s b: Builder @@ -1549,13 +2762,33 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> return to_string(b) } - /* - returns a reversed version of the `s` string +Reverses the input string `s` + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- allocator: (default is context.allocator) + +Returns: +A reversed version of the input string + +Example: + + import "core:fmt" + import "core:strings" + + reverse_example :: proc() { + a := "abcxyz" + b := strings.reverse(a) + fmt.println(a, b) + } + +Output: + + abcxyz zyxcba - a := "abcxyz" - b := strings.reverse(a) - fmt.eprintln(a, b) // abcxyz zyxcba */ reverse :: proc(s: string, allocator := context.allocator) -> string { str := s @@ -1571,14 +2804,35 @@ reverse :: proc(s: string, allocator := context.allocator) -> string { } return string(buf) } - /* - expands the string to a grid spaced by `tab_size` whenever a `\t` character appears - returns the tabbed string, panics on tab_size <= 0 +Expands the input string by replacing tab characters with spaces to align to a specified tab size + +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- tab_size: The number of spaces to use for each tab character +- allocator: (default is context.allocator) + +Returns: +A new string with tab characters expanded to the specified tab size + +WARNING: Panics if tab_size <= 0 + +Example: + + import "core:fmt" + import "core:strings" + + expand_tabs_example :: proc() { + text := "abc1\tabc2\tabc3" + fmt.println(strings.expand_tabs(text, 4)) + } + +Output: + + abc1 abc2 abc3 - strings.expand_tabs("abc1\tabc2\tabc3", 4) -> abc1 abc2 abc3 - strings.expand_tabs("abc1\tabc2\tabc3", 5) -> abc1 abc2 abc3 - strings.expand_tabs("abc1\tabc2\tabc3", 6) -> abc1 abc2 abc3 */ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { if tab_size <= 0 { @@ -1621,16 +2875,41 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return to_string(b) } - /* - splits the `str` string by the seperator `sep` string and returns 3 parts - `head`: before the split, `match`: the seperator, `tail`: the end of the split - returns the input string when the `sep` was not found +Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string. + +Inputs: +- str: The input string +- sep: The separator string + +Returns: +A tuple with `head` (before the split), `match` (the separator), and `tail` (the end of the split) strings + +Example: + + import "core:fmt" + import "core:strings" + + partition_example :: proc() { + text := "testing this out" + head, match, tail := strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out" + fmt.println(head, match, tail) + head, match, tail = strings.partition(text, "hi") // -> head: "testing t", match: "hi", tail: "s out" + fmt.println(head, match, tail) + head, match, tail = strings.partition(text, "xyz") // -> head: "testing this out", match: "", tail: "" + fmt.println(head) + fmt.println(match == "") + fmt.println(tail == "") + } + +Output: + + testing this out + testing t hi s out + testing this out + true + true - text := "testing this out" - strings.partition(text, " this ") -> head: "testing", match: " this ", tail: "out" - strings.partition(text, "hi") -> head: "testing t", match: "hi", tail: "s out" - strings.partition(text, "xyz") -> head: "testing this out", match: "", tail: "" */ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) @@ -1644,10 +2923,22 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) { tail = str[i+len(sep):] return } - +// Alias for centre_justify center_justify :: centre_justify // NOTE(bill): Because Americans exist +/* +Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length. -// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the centered string, in runes +- pad: The string used for padding on both sides +- allocator: (default is context.allocator) + +Returns: +A new string centered within a field of the specified length +*/ centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1669,8 +2960,20 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte return to_string(b) } +/* +Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length. -// left_justify returns a string with a pad string at right side if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the left-justified string +- pad: The string used for padding on the right side +- allocator: (default is context.allocator) + +Returns: +A new string left-justified within a field of the specified length +*/ left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1691,8 +2994,20 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context return to_string(b) } +/* +Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length. -// right_justify returns a string with a pad string at left side if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the right-justified string +- pad: The string used for padding on the left side +- allocator: (default is context.allocator) + +Returns: +A new string right-justified within a field of the specified length +*/ right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1713,10 +3028,15 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex return to_string(b) } +/* +Writes a given pad string a specified number of times to an `io.Writer` - - - +Inputs: +- w: The io.Writer to write the pad string to +- pad: The pad string to be written +- pad_len: The length of the pad string, in runes +- remains: The number of times to write the pad string, in runes +*/ @private write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { repeats := remains / pad_len @@ -1734,10 +3054,18 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { p = p[width:] } } +/* +Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space` +*Allocates Using Provided Allocator* -// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space -// returning a slice of substrings of s or an empty slice if s only contains white space +Inputs: +- s: The input string +- allocator: (default is context.allocator) + +Returns: +A slice of substrings of the input string, or an empty slice if the input string only contains white space +*/ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { n := 0 was_space := 1 @@ -1786,14 +3114,21 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds } return a } +/* +Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)` +*Allocates Using Provided Allocator* -// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch) -// returns a slice of substrings of s -// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned -// -// fields_proc makes no guarantee about the order in which it calls f(ch) -// it assumes that `f` always returns the same value for a given ch +Inputs: +- s: The input string +- f: A predicate function to determine the split points +- allocator: (default is context.allocator) + +NOTE: fields_proc makes no guarantee about the order in which it calls `f(r)`, it assumes that `f` always returns the same value for a given `r` + +Returns: +A slice of substrings of the input string, or an empty slice if all code points in the input string satisfy the predicate or if the input string is empty +*/ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator) @@ -1820,10 +3155,16 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc return substrings[:] } +/* +Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters +Inputs: +- s: A mutable string reference to be iterated -// `fields_iterator` returns the first run of characters in `s` that does not contain white space, defined by `unicode.is_space` -// `s` will then start from any space after the substring, or be an empty string if the substring was the remaining characters +Returns: +- field: The first non-space substring found +- ok: A boolean indicating if a non-space substring was found +*/ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { start, end := -1, -1 for r, offset in s { @@ -1852,10 +3193,22 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { s^ = s[len(s):] return } +/* +Computes the Levenshtein edit distance between two strings -// `levenshtein_distance` returns the Levenshtein edit distance between 2 strings. -// This is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. -// Note: allocator isn't used if the length of string b in runes is smaller than 64. +*Allocates Using Provided Allocator (deletion occurs internal to proc)* + +NOTE: Does not perform internal allocation if length of string `b`, in runes, is smaller than 64 + +Inputs: +- a, b: The two strings to compare +- allocator: (default is context.allocator) + +Returns: +The Levenshtein edit distance between the two strings + +NOTE: This implementation is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. +*/ levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int { LEVENSHTEIN_DEFAULT_COSTS: []int : { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 13ffda371..1bbf910bb 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -393,6 +393,8 @@ foreign kernel32 { 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 --- GetDiskFreeSpaceExW :: proc( lpDirectoryName: LPCWSTR, diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin index 2ac84fbba..0a6f90a44 100644 --- a/core/sys/windows/shell32.odin +++ b/core/sys/windows/shell32.odin @@ -23,6 +23,8 @@ foreign shell32 { SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int --- SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT --- SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR --- + + Shell_NotifyIconW :: proc(dwMessage: DWORD, lpData: ^NOTIFYICONDATAW) -> BOOL --- } APPBARDATA :: struct { diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 514998a43..cd8bb4060 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -145,8 +145,6 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE PLARGE_INTEGER :: ^LARGE_INTEGER PSRWLOCK :: ^SRWLOCK -MMRESULT :: UINT - CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001 CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002 @@ -261,26 +259,6 @@ GET_FILEEX_INFO_LEVELS :: distinct i32 GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0 GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1 -// String resource number bases (internal use) - -MMSYSERR_BASE :: 0 -WAVERR_BASE :: 32 -MIDIERR_BASE :: 64 -TIMERR_BASE :: 96 -JOYERR_BASE :: 160 -MCIERR_BASE :: 256 -MIXERR_BASE :: 1024 - -MCI_STRING_OFFSET :: 512 -MCI_VD_OFFSET :: 1024 -MCI_CD_OFFSET :: 1088 -MCI_WAVE_OFFSET :: 1152 -MCI_SEQ_OFFSET :: 1216 - -// timer error return values -TIMERR_NOERROR :: 0 // no error -TIMERR_NOCANDO :: TIMERR_BASE + 1 // request not completed -TIMERR_STRUCT :: TIMERR_BASE + 33 // time struct size DIAGNOSTIC_REASON_VERSION :: 0 @@ -724,6 +702,14 @@ CWPRETSTRUCT :: struct { hwnd: HWND, } +MSLLHOOKSTRUCT :: struct { + pt: POINT, + mouseData: DWORD, + flags: DWORD, + time: DWORD, + dwExtraInfo: ULONG_PTR, +} + KBDLLHOOKSTRUCT :: struct { vkCode: DWORD, scanCode: DWORD, @@ -732,6 +718,59 @@ KBDLLHOOKSTRUCT :: struct { dwExtraInfo: ULONG_PTR, } +MOUSEINPUT :: struct { + dx: LONG, + dy: LONG, + mouseData: DWORD, + dwFlags: DWORD, + time: DWORD, + dwExtraInfo: ULONG_PTR, +} + +KEYBDINPUT :: struct { + wVk: WORD, + wScan: WORD, + dwFlags: DWORD, + time: DWORD, + dwExtraInfo: ULONG_PTR, +} + +HARDWAREINPUT :: struct { + uMsg: DWORD, + wParamL: WORD, + wParamH: WORD, +} + +INPUT_TYPE :: enum DWORD { + MOUSE = 0, + KEYBOARD = 1, + HARDWARE = 2, +} + +INPUT :: struct { + type: INPUT_TYPE, + using _: struct #raw_union { + mi: MOUSEINPUT, + ki: KEYBDINPUT, + hi: HARDWAREINPUT, + }, +} + +MOUSEEVENTF_MOVE :: 0x0001 +MOUSEEVENTF_LEFTDOWN :: 0x0002 +MOUSEEVENTF_LEFTUP :: 0x0004 +MOUSEEVENTF_RIGHTDOWN :: 0x0008 +MOUSEEVENTF_RIGHTUP :: 0x0010 +MOUSEEVENTF_MIDDLEDOWN :: 0x0020 +MOUSEEVENTF_MIDDLEUP :: 0x0040 +MOUSEEVENTF_XDOWN :: 0x0080 +MOUSEEVENTF_XUP :: 0x0100 +MOUSEEVENTF_WHEEL :: 0x0800 +MOUSEEVENTF_HWHEEL :: 0x1000 +MOUSEEVENTF_MOVE_NOCOALESCE :: 0x2000 +MOUSEEVENTF_VIRTUALDESK :: 0x4000 +MOUSEEVENTF_ABSOLUTE :: 0x8000 + WNDCLASSA :: struct { style: UINT, lpfnWndProc: WNDPROC, @@ -799,6 +838,104 @@ MSG :: struct { LPMSG :: ^MSG +NOTIFYICONDATAW :: struct { + cbSize: DWORD, + hWnd: HWND, + uID: UINT, + uFlags: UINT, + uCallbackMessage: UINT, + hIcon: HICON, + szTip: [128]WCHAR, + dwState: DWORD, + dwStateMask: DWORD, + szInfo: [256]WCHAR, + using _: struct #raw_union { + uTimeout: UINT, + uVersion: UINT, + }, + szInfoTitle: [64]WCHAR, + dwInfoFlags: DWORD, + guidItem: GUID, + hBalloonIcon: HICON, +} + +NIF_MESSAGE :: 0x00000001 +NIF_ICON :: 0x00000002 +NIF_TIP :: 0x00000004 +NIF_STATE :: 0x00000008 +NIF_INFO :: 0x00000010 +NIF_GUID :: 0x00000020 +NIF_REALTIME :: 0x00000040 +NIF_SHOWTIP :: 0x00000080 + +NIM_ADD :: 0x00000000 +NIM_MODIFY :: 0x00000001 +NIM_DELETE :: 0x00000002 +NIM_SETFOCUS :: 0x00000003 +NIM_SETVERSION :: 0x00000004 + +// Menu flags for Add/Check/EnableMenuItem() +MF_INSERT :: 0x00000000 +MF_CHANGE :: 0x00000080 +MF_APPEND :: 0x00000100 +MF_DELETE :: 0x00000200 +MF_REMOVE :: 0x00001000 + +MF_BYCOMMAND :: 0x00000000 +MF_BYPOSITION :: 0x00000400 + +MF_SEPARATOR :: 0x00000800 + +MF_ENABLED :: 0x00000000 +MF_GRAYED :: 0x00000001 +MF_DISABLED :: 0x00000002 + +MF_UNCHECKED :: 0x00000000 +MF_CHECKED :: 0x00000008 +MF_USECHECKBITMAPS :: 0x00000200 + +MF_STRING :: 0x00000000 +MF_BITMAP :: 0x00000004 +MF_OWNERDRAW :: 0x00000100 + +MF_POPUP :: 0x00000010 +MF_MENUBARBREAK :: 0x00000020 +MF_MENUBREAK :: 0x00000040 + +MF_UNHILITE :: 0x00000000 +MF_HILITE :: 0x00000080 + +MF_DEFAULT :: 0x00001000 +MF_SYSMENU :: 0x00002000 +MF_HELP :: 0x00004000 +MF_RIGHTJUSTIFY :: 0x00004000 + +MF_MOUSESELECT :: 0x00008000 +MF_END :: 0x00000080 // Obsolete -- only used by old RES files + +// Flags for TrackPopupMenu +TPM_LEFTBUTTON :: 0x0000 +TPM_RIGHTBUTTON :: 0x0002 +TPM_LEFTALIGN :: 0x0000 +TPM_CENTERALIGN :: 0x0004 +TPM_RIGHTALIGN :: 0x0008 +TPM_TOPALIGN :: 0x0000 +TPM_VCENTERALIGN :: 0x0010 +TPM_BOTTOMALIGN :: 0x0020 + +TPM_HORIZONTAL :: 0x0000 /* Horz alignment matters more */ +TPM_VERTICAL :: 0x0040 /* Vert alignment matters more */ +TPM_NONOTIFY :: 0x0080 /* Don't send any notification msgs */ +TPM_RETURNCMD :: 0x0100 +TPM_RECURSE :: 0x0001 +TPM_HORPOSANIMATION :: 0x0400 +TPM_HORNEGANIMATION :: 0x0800 +TPM_VERPOSANIMATION :: 0x1000 +TPM_VERNEGANIMATION :: 0x2000 +TPM_NOANIMATION :: 0x4000 +TPM_LAYOUTRTL :: 0x8000 +TPM_WORKAREA :: 0x10000 + // WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes HTERROR :: -2 HTTRANSPARENT :: -1 @@ -3782,8 +3919,14 @@ CONSOLE_SCREEN_BUFFER_INFO :: struct { dwMaximumWindowSize: COORD, } +CONSOLE_CURSOR_INFO :: struct { + dwSize: DWORD, + bVisible: BOOL, +} + PCONSOLE_SCREEN_BUFFER_INFO :: ^CONSOLE_SCREEN_BUFFER_INFO +PCONSOLE_CURSOR_INFO :: ^CONSOLE_CURSOR_INFO // // Networking @@ -4018,4 +4161,4 @@ DNS_SRV_DATAA :: struct { SOCKADDR :: struct { sa_family: ADDRESS_FAMILY, sa_data: [14]CHAR, -} \ No newline at end of file +} diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index f59bdf9e3..c1a6791cc 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -109,6 +109,12 @@ foreign user32 { GetDlgCtrlID :: proc(hWnd: HWND) -> c_int --- GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND --- + CreatePopupMenu :: proc() -> HMENU --- + DestroyMenu :: proc(hMenu: HMENU) -> BOOL --- + AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL --- + TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x: int, y: int, nReserved: int, hWnd: HWND, prcRect: ^RECT) -> i32 --- + RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT --- + GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL --- ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL --- InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL --- @@ -206,6 +212,8 @@ foreign user32 { GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT --- RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL --- + SendInput :: proc(cInputs: UINT, pInputs: [^]INPUT, cbSize: ^c_int) -> UINT --- + SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL --- FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int --- @@ -469,4 +477,4 @@ WINDOWINFO :: struct { atomWindowType: ATOM, wCreatorVersion: WORD, } -PWINDOWINFO :: ^WINDOWINFO \ No newline at end of file +PWINDOWINFO :: ^WINDOWINFO diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin index 64ace19fc..445470f6e 100644 --- a/core/sys/windows/winmm.odin +++ b/core/sys/windows/winmm.odin @@ -3,9 +3,170 @@ package sys_windows foreign import winmm "system:Winmm.lib" +MMRESULT :: UINT + @(default_calling_convention="stdcall") foreign winmm { + timeGetDevCaps :: proc(ptc: LPTIMECAPS, cbtc: UINT) -> MMRESULT --- timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT --- timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT --- timeGetTime :: proc() -> DWORD --- } + +LPTIMECAPS :: ^TIMECAPS +TIMECAPS :: struct { + wPeriodMin: UINT, + wPeriodMax: UINT, +} + +// String resource number bases (internal use) +MMSYSERR_BASE :: 0 +WAVERR_BASE :: 32 +MIDIERR_BASE :: 64 +TIMERR_BASE :: 96 +JOYERR_BASE :: 160 +MCIERR_BASE :: 256 +MIXERR_BASE :: 1024 + +MCI_STRING_OFFSET :: 512 +MCI_VD_OFFSET :: 1024 +MCI_CD_OFFSET :: 1088 +MCI_WAVE_OFFSET :: 1152 +MCI_SEQ_OFFSET :: 1216 + +/* general error return values */ +MMSYSERR_NOERROR :: 0 /* no error */ +MMSYSERR_ERROR :: MMSYSERR_BASE + 1 /* unspecified error */ +MMSYSERR_BADDEVICEID :: MMSYSERR_BASE + 2 /* device ID out of range */ +MMSYSERR_NOTENABLED :: MMSYSERR_BASE + 3 /* driver failed enable */ +MMSYSERR_ALLOCATED :: MMSYSERR_BASE + 4 /* device already allocated */ +MMSYSERR_INVALHANDLE :: MMSYSERR_BASE + 5 /* device handle is invalid */ +MMSYSERR_NODRIVER :: MMSYSERR_BASE + 6 /* no device driver present */ +MMSYSERR_NOMEM :: MMSYSERR_BASE + 7 /* memory allocation error */ +MMSYSERR_NOTSUPPORTED :: MMSYSERR_BASE + 8 /* function isn't supported */ +MMSYSERR_BADERRNUM :: MMSYSERR_BASE + 9 /* error value out of range */ +MMSYSERR_INVALFLAG :: MMSYSERR_BASE + 10 /* invalid flag passed */ +MMSYSERR_INVALPARAM :: MMSYSERR_BASE + 11 /* invalid parameter passed */ +MMSYSERR_HANDLEBUSY :: MMSYSERR_BASE + 12 /* handle being used simultaneously on another thread (eg callback) */ +MMSYSERR_INVALIDALIAS :: MMSYSERR_BASE + 13 /* specified alias not found */ +MMSYSERR_BADDB :: MMSYSERR_BASE + 14 /* bad registry database */ +MMSYSERR_KEYNOTFOUND :: MMSYSERR_BASE + 15 /* registry key not found */ +MMSYSERR_READERROR :: MMSYSERR_BASE + 16 /* registry read error */ +MMSYSERR_WRITEERROR :: MMSYSERR_BASE + 17 /* registry write error */ +MMSYSERR_DELETEERROR :: MMSYSERR_BASE + 18 /* registry delete error */ +MMSYSERR_VALNOTFOUND :: MMSYSERR_BASE + 19 /* registry value not found */ +MMSYSERR_NODRIVERCB :: MMSYSERR_BASE + 20 /* driver does not call DriverCallback */ +MMSYSERR_MOREDATA :: MMSYSERR_BASE + 21 /* more data to be returned */ +MMSYSERR_LASTERROR :: MMSYSERR_BASE + 21 /* last error in range */ + +/* waveform audio error return values */ +WAVERR_BADFORMAT :: WAVERR_BASE + 0 /* unsupported wave format */ +WAVERR_STILLPLAYING :: WAVERR_BASE + 1 /* still something playing */ +WAVERR_UNPREPARED :: WAVERR_BASE + 2 /* header not prepared */ +WAVERR_SYNC :: WAVERR_BASE + 3 /* device is synchronous */ +WAVERR_LASTERROR :: WAVERR_BASE + 3 /* last error in range */ + +/* MIDI error return values */ +MIDIERR_UNPREPARED :: MIDIERR_BASE + 0 /* header not prepared */ +MIDIERR_STILLPLAYING :: MIDIERR_BASE + 1 /* still something playing */ +MIDIERR_NOMAP :: MIDIERR_BASE + 2 /* no configured instruments */ +MIDIERR_NOTREADY :: MIDIERR_BASE + 3 /* hardware is still busy */ +MIDIERR_NODEVICE :: MIDIERR_BASE + 4 /* port no longer connected */ +MIDIERR_INVALIDSETUP :: MIDIERR_BASE + 5 /* invalid MIF */ +MIDIERR_BADOPENMODE :: MIDIERR_BASE + 6 /* operation unsupported w/ open mode */ +MIDIERR_DONT_CONTINUE :: MIDIERR_BASE + 7 /* thru device 'eating' a message */ +MIDIERR_LASTERROR :: MIDIERR_BASE + 7 /* last error in range */ + +/* timer error return values */ +TIMERR_NOERROR :: 0 /* no error */ +TIMERR_NOCANDO :: TIMERR_BASE + 1 /* request not completed */ +TIMERR_STRUCT :: TIMERR_BASE + 33 /* time struct size */ + +/* joystick error return values */ +JOYERR_NOERROR :: 0 /* no error */ +JOYERR_PARMS :: JOYERR_BASE + 5 /* bad parameters */ +JOYERR_NOCANDO :: JOYERR_BASE + 6 /* request not completed */ +JOYERR_UNPLUGGED :: JOYERR_BASE + 7 /* joystick is unplugged */ + +/* MCI error return values */ +MCIERR_INVALID_DEVICE_ID :: MCIERR_BASE + 1 +MCIERR_UNRECOGNIZED_KEYWORD :: MCIERR_BASE + 3 +MCIERR_UNRECOGNIZED_COMMAND :: MCIERR_BASE + 5 +MCIERR_HARDWARE :: MCIERR_BASE + 6 +MCIERR_INVALID_DEVICE_NAME :: MCIERR_BASE + 7 +MCIERR_OUT_OF_MEMORY :: MCIERR_BASE + 8 +MCIERR_DEVICE_OPEN :: MCIERR_BASE + 9 +MCIERR_CANNOT_LOAD_DRIVER :: MCIERR_BASE + 10 +MCIERR_MISSING_COMMAND_STRING :: MCIERR_BASE + 11 +MCIERR_PARAM_OVERFLOW :: MCIERR_BASE + 12 +MCIERR_MISSING_STRING_ARGUMENT :: MCIERR_BASE + 13 +MCIERR_BAD_INTEGER :: MCIERR_BASE + 14 +MCIERR_PARSER_INTERNAL :: MCIERR_BASE + 15 +MCIERR_DRIVER_INTERNAL :: MCIERR_BASE + 16 +MCIERR_MISSING_PARAMETER :: MCIERR_BASE + 17 +MCIERR_UNSUPPORTED_FUNCTION :: MCIERR_BASE + 18 +MCIERR_FILE_NOT_FOUND :: MCIERR_BASE + 19 +MCIERR_DEVICE_NOT_READY :: MCIERR_BASE + 20 +MCIERR_INTERNAL :: MCIERR_BASE + 21 +MCIERR_DRIVER :: MCIERR_BASE + 22 +MCIERR_CANNOT_USE_ALL :: MCIERR_BASE + 23 +MCIERR_MULTIPLE :: MCIERR_BASE + 24 +MCIERR_EXTENSION_NOT_FOUND :: MCIERR_BASE + 25 +MCIERR_OUTOFRANGE :: MCIERR_BASE + 26 +MCIERR_FLAGS_NOT_COMPATIBLE :: MCIERR_BASE + 28 +MCIERR_FILE_NOT_SAVED :: MCIERR_BASE + 30 +MCIERR_DEVICE_TYPE_REQUIRED :: MCIERR_BASE + 31 +MCIERR_DEVICE_LOCKED :: MCIERR_BASE + 32 +MCIERR_DUPLICATE_ALIAS :: MCIERR_BASE + 33 +MCIERR_BAD_CONSTANT :: MCIERR_BASE + 34 +MCIERR_MUST_USE_SHAREABLE :: MCIERR_BASE + 35 +MCIERR_MISSING_DEVICE_NAME :: MCIERR_BASE + 36 +MCIERR_BAD_TIME_FORMAT :: MCIERR_BASE + 37 +MCIERR_NO_CLOSING_QUOTE :: MCIERR_BASE + 38 +MCIERR_DUPLICATE_FLAGS :: MCIERR_BASE + 39 +MCIERR_INVALID_FILE :: MCIERR_BASE + 40 +MCIERR_NULL_PARAMETER_BLOCK :: MCIERR_BASE + 41 +MCIERR_UNNAMED_RESOURCE :: MCIERR_BASE + 42 +MCIERR_NEW_REQUIRES_ALIAS :: MCIERR_BASE + 43 +MCIERR_NOTIFY_ON_AUTO_OPEN :: MCIERR_BASE + 44 +MCIERR_NO_ELEMENT_ALLOWED :: MCIERR_BASE + 45 +MCIERR_NONAPPLICABLE_FUNCTION :: MCIERR_BASE + 46 +MCIERR_ILLEGAL_FOR_AUTO_OPEN :: MCIERR_BASE + 47 +MCIERR_FILENAME_REQUIRED :: MCIERR_BASE + 48 +MCIERR_EXTRA_CHARACTERS :: MCIERR_BASE + 49 +MCIERR_DEVICE_NOT_INSTALLED :: MCIERR_BASE + 50 +MCIERR_GET_CD :: MCIERR_BASE + 51 +MCIERR_SET_CD :: MCIERR_BASE + 52 +MCIERR_SET_DRIVE :: MCIERR_BASE + 53 +MCIERR_DEVICE_LENGTH :: MCIERR_BASE + 54 +MCIERR_DEVICE_ORD_LENGTH :: MCIERR_BASE + 55 +MCIERR_NO_INTEGER :: MCIERR_BASE + 56 +MCIERR_WAVE_OUTPUTSINUSE :: MCIERR_BASE + 64 +MCIERR_WAVE_SETOUTPUTINUSE :: MCIERR_BASE + 65 +MCIERR_WAVE_INPUTSINUSE :: MCIERR_BASE + 66 +MCIERR_WAVE_SETINPUTINUSE :: MCIERR_BASE + 67 +MCIERR_WAVE_OUTPUTUNSPECIFIED :: MCIERR_BASE + 68 +MCIERR_WAVE_INPUTUNSPECIFIED :: MCIERR_BASE + 69 +MCIERR_WAVE_OUTPUTSUNSUITABLE :: MCIERR_BASE + 70 +MCIERR_WAVE_SETOUTPUTUNSUITABLE :: MCIERR_BASE + 71 +MCIERR_WAVE_INPUTSUNSUITABLE :: MCIERR_BASE + 72 +MCIERR_WAVE_SETINPUTUNSUITABLE :: MCIERR_BASE + 73 +MCIERR_SEQ_DIV_INCOMPATIBLE :: MCIERR_BASE + 80 +MCIERR_SEQ_PORT_INUSE :: MCIERR_BASE + 81 +MCIERR_SEQ_PORT_NONEXISTENT :: MCIERR_BASE + 82 +MCIERR_SEQ_PORT_MAPNODEVICE :: MCIERR_BASE + 83 +MCIERR_SEQ_PORT_MISCERROR :: MCIERR_BASE + 84 +MCIERR_SEQ_TIMER :: MCIERR_BASE + 85 +MCIERR_SEQ_PORTUNSPECIFIED :: MCIERR_BASE + 86 +MCIERR_SEQ_NOMIDIPRESENT :: MCIERR_BASE + 87 +MCIERR_NO_WINDOW :: MCIERR_BASE + 90 +MCIERR_CREATEWINDOW :: MCIERR_BASE + 91 +MCIERR_FILE_READ :: MCIERR_BASE + 92 +MCIERR_FILE_WRITE :: MCIERR_BASE + 93 +MCIERR_NO_IDENTITY :: MCIERR_BASE + 94 + +/* MMRESULT error return values specific to the mixer API */ +MIXERR_INVALLINE :: (MIXERR_BASE + 0) +MIXERR_INVALCONTROL :: (MIXERR_BASE + 1) +MIXERR_INVALVALUE :: (MIXERR_BASE + 2) +MIXERR_LASTERROR :: (MIXERR_BASE + 2) \ No newline at end of file diff --git a/core/text/table/doc.odin b/core/text/table/doc.odin new file mode 100644 index 000000000..9b5c1f932 --- /dev/null +++ b/core/text/table/doc.odin @@ -0,0 +1,100 @@ +/* + package table implements ascii/markdown/html/custom rendering of tables. + + --- + + Custom rendering example: + + ```odin + tbl := init(&Table{}) + padding(tbl, 0, 1) + row(tbl, "A_LONG_ENUM", "= 54,", "// A comment about A_LONG_ENUM") + row(tbl, "AN_EVEN_LONGER_ENUM", "= 1,", "// A comment about AN_EVEN_LONGER_ENUM") + build(tbl) + for row in 0.. + Made available under Odin's BSD-3 license. + + List of contributors: + oskarnp: Initial implementation. +*/ + +package text_table + +import "core:io" +import "core:os" +import "core:fmt" +import "core:mem" +import "core:mem/virtual" +import "core:runtime" +import "core:strings" + +Cell :: struct { + text: string, + alignment: Cell_Alignment, +} + +Cell_Alignment :: enum { + Left, + Center, + Right, +} + +Table :: struct { + lpad, rpad: int, // Cell padding (left/right) + cells: [dynamic]Cell, + caption: string, + nr_rows, nr_cols: int, + has_header_row: bool, + table_allocator: runtime.Allocator, // Used for allocating cells/colw + format_allocator: runtime.Allocator, // Used for allocating Cell.text when applicable + + dirty: bool, // True if build() needs to be called before rendering + + // The following are computed on build() + colw: [dynamic]int, // Width of each column (including padding, excluding borders) + tblw: int, // Width of entire table (including padding, excluding borders) +} + +init :: proc{init_with_allocator, init_with_virtual_arena, init_with_mem_arena} + +init_with_allocator :: proc(tbl: ^Table, format_allocator := context.temp_allocator, table_allocator := context.allocator) -> ^Table { + tbl.table_allocator = table_allocator + tbl.cells = make([dynamic]Cell, tbl.table_allocator) + tbl.colw = make([dynamic]int, tbl.table_allocator) + tbl.format_allocator = format_allocator + return tbl +} +init_with_virtual_arena :: proc(tbl: ^Table, format_arena: ^virtual.Arena, table_allocator := context.allocator) -> ^Table { + return init_with_allocator(tbl, virtual.arena_allocator(format_arena), table_allocator) +} +init_with_mem_arena :: proc(tbl: ^Table, format_arena: ^mem.Arena, table_allocator := context.allocator) -> ^Table { + return init_with_allocator(tbl, mem.arena_allocator(format_arena), table_allocator) +} + +destroy :: proc(tbl: ^Table) { + free_all(tbl.format_allocator) + delete(tbl.cells) + delete(tbl.colw) +} + +caption :: proc(tbl: ^Table, value: string) { + tbl.caption = value + tbl.dirty = true +} + +padding :: proc(tbl: ^Table, lpad, rpad: int) { + tbl.lpad = lpad + tbl.rpad = rpad + tbl.dirty = true +} + +get_cell :: proc(tbl: ^Table, row, col: int, loc := #caller_location) -> ^Cell { + assert(col >= 0 && col < tbl.nr_cols, "cell column out of range", loc) + assert(row >= 0 && row < tbl.nr_rows, "cell row out of range", loc) + resize(&tbl.cells, tbl.nr_cols * tbl.nr_rows) + return &tbl.cells[row*tbl.nr_cols + col] +} + +set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: string, alignment: Cell_Alignment) { + cell := get_cell(tbl, row, col) + cell.text = format(tbl, "%v", value) + cell.alignment = alignment + tbl.dirty = true +} + +set_cell_value :: proc(tbl: ^Table, row, col: int, value: any, loc := #caller_location) { + cell := get_cell(tbl, row, col, loc) + switch val in value { + case nil: + cell.text = "" + case string: + cell.text = string(val) + case cstring: + cell.text = string(val) + case: + cell.text = format(tbl, "%v", val) + if cell.text == "" { + fmt.eprintf("{} text/table: format() resulted in empty string (arena out of memory?)\n", loc) + } + } + tbl.dirty = true +} + +set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment, loc := #caller_location) { + cell := get_cell(tbl, row, col, loc) + cell.alignment = alignment + tbl.dirty = true +} + +format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string { + context.allocator = tbl.format_allocator + return fmt.aprintf(fmt = _fmt, args = args) +} + +header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) { + if (tbl.has_header_row && tbl.nr_rows != 1) || (!tbl.has_header_row && tbl.nr_rows != 0) { + panic("Cannot add headers after rows have been added", loc) + } + + if tbl.nr_rows == 0 { + tbl.nr_rows += 1 + tbl.has_header_row = true + } + + col := tbl.nr_cols + tbl.nr_cols += len(values) + for val in values { + set_cell_value(tbl, header_row(tbl), col, val, loc) + col += 1 + } + + tbl.dirty = true +} + +row :: proc(tbl: ^Table, values: ..any, loc := #caller_location) { + if tbl.nr_cols == 0 { + if len(values) == 0 { + panic("Cannot create row without values unless knowing amount of columns in advance") + } else { + tbl.nr_cols = len(values) + } + } + tbl.nr_rows += 1 + for col in 0.. int { + return tbl.nr_rows - 1 +} + +header_row :: proc(tbl: ^Table) -> int { + return 0 if tbl.has_header_row else -1 +} + +first_row :: proc(tbl: ^Table) -> int { + return header_row(tbl)+1 if tbl.has_header_row else 0 +} + +build :: proc(tbl: ^Table) { + tbl.dirty = false + + resize(&tbl.colw, tbl.nr_cols) + mem.zero_slice(tbl.colw[:]) + + for row in 0.. tbl.colw[col] { + tbl.colw[col] = w + } + } + } + + colw_sum := 0 + for v in tbl.colw { + colw_sum += v + } + + tbl.tblw = max(colw_sum, len(tbl.caption) + tbl.lpad + tbl.rpad) + + // Resize columns to match total width of table + remain := tbl.tblw-colw_sum + for col := 0; remain > 0; col = (col + 1) % tbl.nr_cols { + tbl.colw[col] += 1 + remain -= 1 + } + + return +} + +write_html_table :: proc(w: io.Writer, tbl: ^Table) { + if tbl.dirty { + build(tbl) + } + + io.write_string(w, "\n") + if tbl.caption != "" { + io.write_string(w, "\n") + } + + align_attribute :: proc(cell: ^Cell) -> string { + switch cell.alignment { + case .Left: return ` align="left"` + case .Center: return ` align="center"` + case .Right: return ` align="right"` + } + unreachable() + } + + if tbl.has_header_row { + io.write_string(w, "\n") + io.write_string(w, " \n") + for col in 0..") + io.write_string(w, cell.text) + io.write_string(w, "\n") + } + io.write_string(w, " \n") + io.write_string(w, "\n") + } + + io.write_string(w, "\n") + for row in 0..\n") + for col in 0..") + io.write_string(w, cell.text) + io.write_string(w, "\n") + } + io.write_string(w, " \n") + } + io.write_string(w, " \n") + + io.write_string(w, "
") + io.write_string(w, tbl.caption) + io.write_string(w, "
\n") +} + +write_ascii_table :: proc(w: io.Writer, tbl: ^Table) { + if tbl.dirty { + build(tbl) + } + + write_caption_separator :: proc(w: io.Writer, tbl: ^Table) { + io.write_byte(w, '+') + write_byte_repeat(w, tbl.tblw + tbl.nr_cols - 1, '-') + io.write_byte(w, '+') + io.write_byte(w, '\n') + } + + write_table_separator :: proc(w: io.Writer, tbl: ^Table) { + for col in 0.. io.Writer { + return io.to_writer(os.stream_from_handle(os.stdout)) +} + +strings_builder_writer :: proc(b: ^strings.Builder) -> io.Writer { + return strings.to_writer(b) +} diff --git a/core/thread/thread_js.odin b/core/thread/thread_js.odin new file mode 100644 index 000000000..5821ab238 --- /dev/null +++ b/core/thread/thread_js.odin @@ -0,0 +1,55 @@ +//+build js +package thread + +import "core:intrinsics" +import "core:sync" +import "core:mem" + +Thread_State :: enum u8 { + Started, + Joined, + Done, +} + +Thread_Os_Specific :: struct { + flags: bit_set[Thread_State; u8], +} + +_thread_priority_map := [Thread_Priority]i32{ + .Normal = 0, + .Low = -2, + .High = +2, +} + +_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread { + unimplemented("core:thread procedure not supported on js target") +} + +_start :: proc(t: ^Thread) { + unimplemented("core:thread procedure not supported on js target") +} + +_is_done :: proc(t: ^Thread) -> bool { + unimplemented("core:thread procedure not supported on js target") +} + +_join :: proc(t: ^Thread) { + unimplemented("core:thread procedure not supported on js target") +} + +_join_multiple :: proc(threads: ..^Thread) { + unimplemented("core:thread procedure not supported on js target") +} + +_destroy :: proc(thread: ^Thread) { + unimplemented("core:thread procedure not supported on js target") +} + +_terminate :: proc(using thread : ^Thread, exit_code: int) { + unimplemented("core:thread procedure not supported on js target") +} + +_yield :: proc() { + unimplemented("core:thread procedure not supported on js target") +} + diff --git a/misc/old_demos/demo001.odin b/misc/old_demos/demo001.odin deleted file mode 100644 index a3aea1cb7..000000000 --- a/misc/old_demos/demo001.odin +++ /dev/null @@ -1,337 +0,0 @@ -import "core:fmt.odin"; -import "core:os.odin"; -import "core:mem.odin"; -// import "http_test.odin" as ht; -// import "game.odin" as game; -// import "punity.odin" as pn; - -main :: proc() { - struct_padding(); - bounds_checking(); - type_introspection(); - any_type(); - crazy_introspection(); - namespaces_and_files(); - miscellany(); - - /* - ht.run(); - game.run(); - { - init :: proc(c: ^pn.Core) {} - step :: proc(c: ^pn.Core) {} - - pn.run(init, step); - } - */ -} - -struct_padding :: proc() { - { - A :: struct { - a: u8, - b: u32, - c: u16, - } - - B :: struct { - a: [7]u8, - b: [3]u16, - c: u8, - d: u16, - } - - fmt.println("size_of(A):", size_of(A)); - fmt.println("size_of(B):", size_of(B)); - - // n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html - } - { - A :: struct #ordered { - a: u8, - b: u32, - c: u16, - } - - B :: struct #ordered { - a: [7]u8, - b: [3]u16, - c: u8, - d: u16, - } - - fmt.println("size_of(A):", size_of(A)); - fmt.println("size_of(B):", size_of(B)); - - // C-style structure layout - } - { - A :: struct #packed { - a: u8, - b: u32, - c: u16, - } - - B :: struct #packed { - a: [7]u8, - b: [3]u16, - c: u8, - d: u16, - } - - fmt.println("size_of(A):", size_of(A)); - fmt.println("size_of(B):", size_of(B)); - - // Useful for explicit layout - } - - // Member sorting by priority - // Alignment desc. - // Size desc. - // source order asc. - - /* - A :: struct { - a: u8 - b: u32 - c: u16 - } - - B :: struct { - a: [7]u8 - b: [3]u16 - c: u8 - d: u16 - } - - Equivalent too - - A :: struct #ordered { - b: u32 - c: u16 - a: u8 - } - - B :: struct #ordered { - b: [3]u16 - d: u16 - a: [7]u8 - c: u8 - } - */ -} - -bounds_checking :: proc() { - x: [4]int; - // x[-1] = 0; // Compile Time - // x[4] = 0; // Compile Time - - { - a, b := -1, 4; - // x[a] = 0; // Runtime Time - // x[b] = 0; // Runtime Time - } - - // Works for arrays, strings, slices, and related procedures & operations - - { - base: [10]int; - s := base[2..6]; - a, b := -1, 6; - - #no_bounds_check { - s[a] = 0; - // #bounds_check s[b] = 0; - } - - #no_bounds_check - if s[a] == 0 { - // Do whatever - } - - // Bounds checking can be toggled explicit - // on a per statement basis. - // _any statement_ - } -} - -type_introspection :: proc() { - { - info: ^Type_Info; - x: int; - - info = type_info_of(int); // by type - info = type_info_of(x); // by value - // See: runtime.odin - - match i in info.variant { - case Type_Info_Integer: - fmt.println("integer!"); - case Type_Info_Float: - fmt.println("float!"); - case: - fmt.println("potato!"); - } - - // Unsafe cast - integer_info := cast(^Type_Info_Integer)cast(rawptr)info; - } - - { - Vector2 :: struct { x, y: f32 } - Vector3 :: struct { x, y, z: f32 } - - v1: Vector2; - v2: Vector3; - v3: Vector3; - - t1 := type_info_of(v1); - t2 := type_info_of(v2); - t3 := type_info_of(v3); - - fmt.println(); - fmt.print("Type of v1 is:\n\t", t1); - - fmt.println(); - fmt.print("Type of v2 is:\n\t", t2); - - fmt.println("\n"); - fmt.println("t1 == t2:", t1 == t2); - fmt.println("t2 == t3:", t2 == t3); - } -} - -any_type :: proc() { - a: any; - - x: int = 123; - y: f64 = 6.28; - z: string = "Yo-Yo Ma"; - // All types can be implicit cast to `any` - a = x; - a = y; - a = z; - a = a; // This the "identity" type, it doesn't get converted - - a = 123; // Literals are copied onto the stack first - - // any has two members - // data - rawptr to the data - // type_info - pointer to the type info - - fmt.println(x, y, z); - // See: fmt.odin - // For variadic any procedures in action -} - -crazy_introspection :: proc() { - { - Fruit :: enum { - APPLE, - BANANA, - GRAPE, - MELON, - PEACH, - TOMATO, - } - - s: string; - // s = enum_to_string(Fruit.PEACH); - fmt.println(s); - - f := Fruit.GRAPE; - // s = enum_to_string(f); - fmt.println(s); - - fmt.println(f); - // See: runtime.odin - } - - - { - // NOTE(bill): This is not safe code and I would not recommend this at all - // I'd recommend you use `match type` to get the subtype rather than - // casting pointers - - Fruit :: enum { - APPLE, - BANANA, - GRAPE, - MELON, - PEACH, - TOMATO, - } - - fruit_ti := type_info_of(Fruit); - name := fruit_ti.variant.(Type_Info_Named).name; - info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum); - - fmt.printf("%s :: enum %T {\n", name, info.base); - for _, i in info.values { - fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]); - } - fmt.printf("}\n"); - - // NOTE(bill): look at that type-safe printf! - } - - { - Vector3 :: struct {x, y, z: f32} - - a := Vector3{x = 1, y = 4, z = 9}; - fmt.println(a); - b := Vector3{x = 9, y = 3, z = 1}; - fmt.println(b); - - // NOTE(bill): See fmt.odin - } - - // n.b. This pretty much "solves" serialization (to strings) -} - -// #import "test.odin" - -namespaces_and_files :: proc() { - - // test.thing() - // test.format.println() - // test.println() - /* - // Non-exporting import - #import "file.odin" - #import "file.odin" as file - #import "file.odin" as . - #import "file.odin" as _ - - // Exporting import - #include "file.odin" - */ - - // Talk about scope rules and diagram -} - -miscellany :: proc() { - /* - win32 `__imp__` prefix - #dll_import - #dll_export - - Change exported name/symbol for linking - #link_name - - Custom calling conventions - #stdcall - #fastcall - - Runtime stuff - #shared_global_scope - */ - - // assert(false) - // #assert(false) - // panic("Panic message goes here") -} - - - - diff --git a/misc/old_demos/demo002.odin b/misc/old_demos/demo002.odin deleted file mode 100644 index a790aadf3..000000000 --- a/misc/old_demos/demo002.odin +++ /dev/null @@ -1,879 +0,0 @@ -// Demo 002 -export "core:fmt.odin"; -export "core:math.odin"; -export "core:mem.odin"; -// export "game.odin" - -#thread_local tls_int: int; - -main :: proc() { - // Forenotes - - // Semicolons are now optional - // Rule for when a semicolon is expected after a statement - // - If the next token is not on the same line - // - if the next token is a closing brace } - // - Otherwise, a semicolon is needed - // - // Expections: - // for, if, match - // if x := thing(); x < 123 {} - // for i := 0; i < 123; i++ {} - - // Q: Should I use the new rule or go back to the old one without optional semicolons? - - - // #thread_local - see runtime.odin and above at `tls_int` - // #foreign_system_library - see win32.odin - - // struct_compound_literals(); - // enumerations(); - // variadic_procedures(); - // new_builtins(); - // match_statement(); - // namespacing(); - // subtyping(); - // tagged_unions(); -} - -struct_compound_literals :: proc() { - Thing :: struct { - id: int, - x: f32, - name: string, - }; - { - t1: Thing; - t1.id = 1; - - t3 := Thing{}; - t4 := Thing{1, 2, "Fred"}; - // t5 := Thing{1, 2}; - - t6 := Thing{ - name = "Tom", - x = 23, - }; - } -} - -enumerations :: proc() { - { - Fruit :: enum { - APPLE, // 0 - BANANA, // 1 - PEAR, // 2 - }; - - f := Fruit.APPLE; - // g12: int = Fruit.BANANA - g: int = cast(int)Fruit.BANANA; - // However, you can use enums are index values as _any_ integer allowed - } - { - Fruit1 :: enum int { - APPLE, - BANANA, - PEAR, - } - - Fruit2 :: enum u8 { - APPLE, - BANANA, - PEAR, - } - - Fruit3 :: enum u8 { - APPLE = 1, - BANANA, // 2 - PEAR = 5, - TOMATO, // 6 - } - } - - // Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)? -} - -variadic_procedures :: proc() { - print_ints :: proc(args: ..int) { - for arg, i in args { - if i > 0 do print(", "); - print(arg); - } - } - - print_ints(); // nl() - print_ints(1); nl(); - print_ints(1, 2, 3); nl(); - - print_prefix_f32s :: proc(prefix: string, args: ..f32) { - print(prefix); - print(": "); - for arg, i in args { - if i > 0 do print(", "); - print(arg); - } - } - - print_prefix_f32s("a"); nl(); - print_prefix_f32s("b", 1); nl(); - print_prefix_f32s("c", 1, 2, 3); nl(); - - // Internally, the variadic procedures get allocated to an array on the stack, - // and this array is passed a slice - - // This is first step for a `print` procedure but I do not have an `any` type - // yet as this requires a few other things first - i.e. introspection - - // NOTE(bill): I haven't yet added the feature of expanding a slice or array into - // a variadic a parameter but it's pretty trivial to add -} - -new_builtins :: proc() { - { - a := new(int); - b := make([]int, 12); - c := make([]int, 12, 16); - - defer free(a); - defer free(b); - defer free(c); - - // NOTE(bill): These use the current context's allocator not the default allocator - // see runtime.odin - - // Q: Should this be `free` rather than `free` and should I overload it for slices too? - - push_allocator default_allocator() { - a := new(int); - defer free(a); - - // Do whatever - - } - } - - { - a: int = 123; - b: type_of(a) = 321; - - // NOTE(bill): This matches the current naming scheme - // size_of - // align_of - // offset_of - // - // size_of_val - // align_of_val - // offset_of_val - // type_of_val - } - - { - // Compile time assert - COND :: true; - #assert(COND); - // #assert(!COND) - - // Runtime assert - x := true; - assert(x); - // assert(!x); - } - - { - x: ^u32 = nil; - y := x+100; - z := y-x; - w := slice_ptr(x, 12); - t := slice_ptr(x, 12, 16); - - // NOTE(bill): These are here because I've removed: - // pointer arithmetic - // pointer indexing - // pointer slicing - - // Reason - - a: [16]int; - a[1] = 1; - b := &a; - // Auto pointer deref - // consistent with record members - assert(b[1] == 1); - - // Q: Should I add them back in at the cost of inconsitency? - } - - { - a, b := -1, 2; - print(min(a, b)); nl(); - print(max(a, b)); nl(); - print(abs(a)); nl(); - - // These work at compile time too - A :: -1; - B :: 2; - C :: min(A, B); - D :: max(A, B); - E :: abs(A); - - print(C); nl(); - print(D); nl(); - print(E); nl(); - } -} - - -match_statement :: proc() { - // NOTE(bill): `match` statements are similar to `switch` statements - // in other languages but there are few differences - - { - match x := 5; x { - case 1: // cases must be constant expression - print("1!\n"); - // break by default - - case 2: - s := "2!\n"; // Each case has its own scope - print(s); - break; // explicit break - - case 3, 4: // multiple cases - print("3 or 4!\n"); - - case 5: - print("5!\n"); - fallthrough; // explicit fallthrough - - case: - print("default!\n"); - } - - - - match x := 1.5; x { - case 1.5: - print("1.5!\n"); - // break by default - case TAU: - print("Ï„!\n"); - case: - print("default!\n"); - } - - - - match x := "Hello"; x { - case "Hello": - print("greeting\n"); - // break by default - case "Goodbye": - print("farewell\n"); - case: - print("???\n"); - } - - - - - - - a := 53; - match { - case a == 1: - print("one\n"); - case a == 2: - print("a couple\n"); - case a < 7, a == 7: - print("a few\n"); - case a < 12: // intentional bug - print("several\n"); - case a >= 12 && a < 100: - print("dozens\n"); - case a >= 100 && a < 1000: - print("hundreds\n"); - case: - print("a fuck ton\n"); - } - - // Identical to this - - b := 53; - if b == 1 { - print("one\n"); - } else if b == 2 { - print("a couple\n"); - } else if b < 7 || b == 7 { - print("a few\n"); - } else if b < 12 { // intentional bug - print("several\n"); - } else if b >= 12 && b < 100 { - print("dozens\n"); - } else if b >= 100 && b < 1000 { - print("hundreds\n"); - } else { - print("a fuck ton\n"); - } - - // However, match statements allow for `break` and `fallthrough` unlike - // an if statement - } -} - -Vector3 :: struct {x, y, z: f32} - -print_floats :: proc(args: ..f32) { - for arg, i in args { - if i > 0 do print(", "); - print(arg); - } - println(); -} - -namespacing :: proc() { - { - Thing :: #type struct { - x: f32, - name: string, - }; - - a: Thing; - a.x = 3; - { - Thing :: #type struct { - y: int, - test: bool, - }; - - b: Thing; // Uses this scope's Thing - b.test = true; - } - } -/* - { - Entity :: struct { - Guid :: int - Nested :: struct { - MyInt :: int - i: int - } - - CONSTANT :: 123 - - - guid: Guid - name: string - pos: Vector3 - vel: Vector3 - nested: Nested - } - - guid: Entity.Guid = Entity.CONSTANT - i: Entity.Nested.MyInt - - - - { - using Entity - guid: Guid = CONSTANT - using Nested - i: MyInt - } - - - { - using Entity.Nested - guid: Entity.Guid = Entity.CONSTANT - i: MyInt - } - - - { - e: Entity - using e - guid = 27832 - name = "Bob" - - print(e.guid as int); nl() - print(e.name); nl() - } - - { - using e: Entity - guid = 78456 - name = "Thing" - - print(e.guid as int); nl() - print(e.name); nl() - } - } - - { - Entity :: struct { - Guid :: int - Nested :: struct { - MyInt :: int - i: int - } - - CONSTANT :: 123 - - - guid: Guid - name: string - using pos: Vector3 - vel: Vector3 - using nested: ^Nested - } - - e := Entity{nested = new(Entity.Nested)} - e.x = 123 - e.i = Entity.CONSTANT - } - -*/ - - { - Entity :: struct { - position: Vector3 - } - - print_pos_1 :: proc(entity: ^Entity) { - print("print_pos_1: "); - print_floats(entity.position.x, entity.position.y, entity.position.z); - } - - print_pos_2 :: proc(entity: ^Entity) { - using entity; - print("print_pos_2: "); - print_floats(position.x, position.y, position.z); - } - - print_pos_3 :: proc(using entity: ^Entity) { - print("print_pos_3: "); - print_floats(position.x, position.y, position.z); - } - - print_pos_4 :: proc(using entity: ^Entity) { - using position; - print("print_pos_4: "); - print_floats(x, y, z); - } - - e := Entity{position = Vector3{1, 2, 3}}; - print_pos_1(&e); - print_pos_2(&e); - print_pos_3(&e); - print_pos_4(&e); - - // This is similar to C++'s `this` pointer that is implicit and only available in methods - } -} - -subtyping :: proc() { - { - // C way for subtyping/subclassing - - Entity :: struct { - position: Vector3, - } - - Frog :: struct { - entity: Entity, - jump_height: f32, - } - - f: Frog; - f.entity.position = Vector3{1, 2, 3}; - - using f.entity; - position = Vector3{1, 2, 3}; - - } - - { - // C++ way for subtyping/subclassing - - Entity :: struct { - position: Vector3 - } - - Frog :: struct { - using entity: Entity, - jump_height: f32, - } - - f: Frog; - f.position = Vector3{1, 2, 3}; - - - print_pos :: proc(using entity: Entity) { - print("print_pos: "); - print_floats(position.x, position.y, position.z); - } - - print_pos(f.entity); - // print_pos(f); - - // Subtype Polymorphism - } - - { - // More than C++ way for subtyping/subclassing - - Entity :: struct { - position: Vector3, - } - - Frog :: struct { - jump_height: f32, - using entity: ^Entity, // Doesn't have to be first member! - } - - f: Frog; - f.entity = new(Entity); - f.position = Vector3{1, 2, 3}; - - - print_pos :: proc(using entity: ^Entity) { - print("print_pos: "); - print_floats(position.x, position.y, position.z); - } - - print_pos(f.entity); - // print_pos(^f); - // print_pos(f); - } - - { - // More efficient subtyping - - Entity :: struct { - position: Vector3, - } - - Frog :: struct { - jump_height: f32, - using entity: ^Entity, - } - - MAX_ENTITES :: 64; - entities: [MAX_ENTITES]Entity; - entity_count := 0; - - next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity { - e := &entities[entity_count^]; - entity_count^ += 1; - return e; - } - - f: Frog; - f.entity = next_entity(entities[..], &entity_count); - f.position = Vector3{3, 4, 6}; - - using f.position; - print_floats(x, y, z); - } - - /*{ - // Down casting - - Entity :: struct { - position: Vector3, - } - - Frog :: struct { - jump_height: f32, - using entity: Entity, - } - - f: Frog; - f.jump_height = 564; - e := ^f.entity; - - frog := down_cast(^Frog)e; - print("down_cast: "); - print(frog.jump_height); nl(); - - // NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time - // Q: Should I completely remove `down_cast` as I added it in about 30 minutes - }*/ - - { - // Multiple "inheritance"/subclassing - - Entity :: struct { - position: Vector3, - } - Climber :: struct { - speed: f32, - } - - Frog :: struct { - using entity: Entity, - using climber: Climber, - } - } -} - -tagged_unions :: proc() { - { - Entity_Kind :: enum { - INVALID, - FROG, - GIRAFFE, - HELICOPTER, - } - - Entity :: struct { - kind: Entity_Kind - using data: struct #raw_union { - frog: struct { - jump_height: f32, - colour: u32, - }, - giraffe: struct { - neck_length: f32, - spot_count: int, - }, - helicopter: struct { - blade_count: int, - weight: f32, - pilot_name: string, - }, - } - } - - e: Entity; - e.kind = Entity_Kind.FROG; - e.frog.jump_height = 12; - - f: type_of(e.frog); - - // But this is very unsafe and extremely cumbersome to write - // In C++, I use macros to alleviate this but it's not a solution - } - - { - Frog :: struct { - jump_height: f32, - colour: u32, - } - Giraffe :: struct { - neck_length: f32, - spot_count: int, - } - Helicopter :: struct { - blade_count: int, - weight: f32, - pilot_name: string, - } - Entity :: union {Frog, Giraffe, Helicopter}; - - f1: Frog = Frog{12, 0xff9900}; - f2: Entity = Frog{12, 0xff9900}; // Implicit cast - f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast - - // f3.Frog.jump_height = 12 // There are "members" of a union - - - - e, f, g, h: Entity; - f = Frog{12, 0xff9900}; - g = Giraffe{2.1, 23}; - h = Helicopter{4, 1000, "Frank"}; - - - - - // Requires a pointer to the union - // `x` will be a pointer to type of the case - - match x in &f { - case Frog: - print("Frog!\n"); - print(x.jump_height); nl(); - // x.jump_height = 3; - print(x.jump_height); nl(); - case Giraffe: - print("Giraffe!\n"); - case Helicopter: - print("ROFLCOPTER!\n"); - case: - print("invalid entity\n"); - } - - - // Q: Allow for a non pointer version with takes a copy instead? - // Or it takes the pointer the data and not a copy - - - // fp := cast(^Frog)^f; // Unsafe - // print(fp.jump_height); nl(); - - - // Internals of a tagged union - /* - struct { - data: [size_of_biggest_tag]u8, - tag_index: int, - } - */ - // This is to allow for pointer casting if needed - - - // Advantage over subtyping version - MAX_ENTITES :: 64; - entities: [MAX_ENTITES]Entity; - - entities[0] = Frog{}; - entities[1] = Helicopter{}; - // etc. - } - - - { - // Transliteration of code from this actual compiler - // Some stuff is missing - Type :: struct {}; - Scope :: struct {}; - Token :: struct {}; - AstNode :: struct {}; - ExactValue :: struct {}; - - Entity_Kind :: enum { - Invalid, - Constant, - Variable, - Using_Variable, - TypeName, - Procedure, - Builtin, - Count, - } - - Guid :: i64; - Entity :: struct { - - kind: Entity_Kind, - guid: Guid, - - scope: ^Scope, - token: Token, - type_: ^Type, - - using data: struct #raw_union { - Constant: struct { - value: ExactValue, - }, - Variable: struct { - visited: bool, // Cycle detection - used: bool, // Variable is used - is_field: bool, // Is struct field - anonymous: bool, // Variable is an anonymous - }, - Using_Variable: struct { - }, - TypeName: struct { - }, - Procedure: struct { - used: bool, - }, - Builtin: struct { - id: int, - }, - }, - } - - // Plus all the constructing procedures that go along with them!!!! - // It's a nightmare - } - - { - Type :: struct {}; - Scope :: struct {}; - Token :: struct {}; - AstNode :: struct {}; - ExactValue :: struct {}; - - - Guid :: i64; - Entity_Base :: struct { - } - - - Constant :: struct { - value: ExactValue, - } - Variable :: struct { - visited: bool, // Cycle detection - used: bool, // Variable is used - is_field: bool, // Is struct field - anonymous: bool, // Variable is an anonymous - } - Using_Variable :: struct { - } - TypeName :: struct { - } - Procedure :: struct { - used: bool, - } - Builtin :: struct { - id: int, - } - - Entity :: struct { - guid: Guid, - - scope: ^Scope, - token: Token, - type_: ^Type, - - variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin}, - } - - e := Entity{ - variant = Variable{ - used = true, - anonymous = false, - }, - }; - - - - // Q: Allow a "base" type to be added to a union? - // Or even `using` on union to get the same properties? - } - - - { - // `Raw` unions still have uses, especially for mathematic types - - Vector2 :: struct #raw_union { - using xy_: struct { x, y: f32 }, - e: [2]f32, - v: [vector 2]f32, - } - - Vector3 :: struct #raw_union { - using xyz_: struct { x, y, z: f32 }, - xy: Vector2, - e: [3]f32, - v: [vector 3]f32, - } - - v2: Vector2; - v2.x = 1; - v2.e[0] = 1; - v2.v[0] = 1; - - v3: Vector3; - v3.x = 1; - v3.e[0] = 1; - v3.v[0] = 1; - v3.xy.x = 1; - } -} - -nl :: proc() { println(); } diff --git a/misc/old_demos/demo004.odin b/misc/old_demos/demo004.odin deleted file mode 100644 index c9acc9a15..000000000 --- a/misc/old_demos/demo004.odin +++ /dev/null @@ -1,66 +0,0 @@ -import "core:fmt.odin"; -import "core:utf8.odin"; -import "core:hash.odin"; -import "core:mem.odin"; - -main :: proc() { - { // New Standard Library stuff - s := "Hello"; - fmt.println(s, - utf8.valid_string(s), - hash.murmur64(cast([]u8)s)); - - // utf8.odin - // hash.odin - // - crc, fnv, fnva, murmur - // mem.odin - // - Custom allocators - // - Helpers - } - - { - arena: mem.Arena; - mem.init_arena_from_context(&arena, mem.megabytes(16)); // Uses default allocator - defer mem.destroy_arena(&arena); - - push_allocator mem.arena_allocator(&arena) { - x := new(int); - x^ = 1337; - - fmt.println(x^); - } - - /* - push_allocator x { - .. - } - - is equivalent to: - - { - prev_allocator := __context.allocator - __context.allocator = x - defer __context.allocator = prev_allocator - - .. - } - */ - - // You can also "push" a context - - c := context; // Create copy of the allocator - c.allocator = mem.arena_allocator(&arena); - - push_context c { - x := new(int); - x^ = 365; - - fmt.println(x^); - } - } - - // Backend improvements - // - Minimal dependency building (only build what is needed) - // - Numerous bugs fixed - // - Mild parsing recovery after bad syntax error -} diff --git a/misc/old_demos/demo005.odin b/misc/old_demos/demo005.odin deleted file mode 100644 index c8273b03b..000000000 --- a/misc/old_demos/demo005.odin +++ /dev/null @@ -1,283 +0,0 @@ -import "core:fmt.odin"; -import "core:utf8.odin"; -// import "core:atomic.odin"; -// import "core:hash.odin"; -// import "core:math.odin"; -// import "core:mem.odin"; -// import "core:opengl.odin"; -// import "core:os.odin"; -// import "core:sync.odin"; -// import win32 "core:sys/windows.odin"; - -main :: proc() { - // syntax(); - procedure_overloading(); -} - -syntax :: proc() { - // Cyclic type checking - // Uncomment to see the error - // A :: struct {b: B}; - // B :: struct {a: A}; - - x: int; - y := cast(f32)x; - z := transmute(u32)y; - // down_cast, union_cast are similar too - - - - // Basic directives - fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure); - // NOTE: new and improved `printf` - // TODO: It does need accurate float printing - - - - // record fields use the same syntax a procedure signatures - Thing1 :: struct { - x: f32, - y: int, - z: ^[]int, - }; - Thing2 :: struct {x: f32, y: int, z: ^[]int}; - - // Slice interals are now just a `ptr+len+cap` - slice: []int; #assert(size_of(slice) == 3*size_of(int)); - - // Helper type - Help the reader understand what it is quicker - My_Int :: #type int; - My_Proc :: #type proc(int) -> f32; - - - // All declarations with : are either variable or constant - // To make these declarations syntactically consistent - v_variable := 123; - c_constant :: 123; - c_type1 :: int; - c_type2 :: []int; - c_proc :: proc() { /* code here */ }; - - -/* - x += 1; - x -= 1; - // ++ and -- have been removed - // x++; - // x--; - // Question: Should they be added again? - // They were removed as they are redundant and statements, not expressions - // like in C/C++ -*/ - - // You can now build files as a `.dll` - // `odin build_dll demo.odin` - - - // New vector syntax - u, v: [vector 3]f32; - v[0] = 123; - v.x = 123; // valid for all vectors with count 1 to 4 - - // Next part - prefixes(); -} - - -Prefix_Type :: struct {x: int, y: f32, z: rawptr}; - -#thread_local my_tls: Prefix_Type; - -prefixes :: proc() { - using var: Prefix_Type; - var.x = 123; - x = 123; - - - foo :: proc(using pt: Prefix_Type) { - } - - - - // Same as C99's `restrict` - bar :: proc(#no_alias a, b: ^int) { - // Assumes a never equals b so it can perform optimizations with that fact - } - - - when_statements(); -} - - - - - -when_statements :: proc() { - X :: 123 + 12; - Y :: X/5; - COND :: Y > 0; - - when COND { - fmt.println("Y > 0"); - } else { - fmt.println("Y <= 0"); - } - - - when false { - this_code_does_not_exist(123, 321); - but_its_syntax_is_valid(); - x :: ^^^^int; - } - - foreign_procedures(); -} - -when ODIN_OS == "windows" { - foreign_system_library win32_user "user32.lib"; -} -// NOTE: This is done on purpose for two reasons: -// * Makes it clear where the platform specific stuff is -// * Removes the need to solve the travelling salesman problem when importing files :P - -foreign_procedures :: proc() { - foreign win32_user { - ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---; - show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "ShowWindow" ---; - } - // NOTE: If that library doesn't get used, it doesn't get linked with - // NOTE: There is not link checking yet to see if that procedure does come from that library - - // See sys/windows.odin for more examples - - special_expressions(); -} - -special_expressions :: proc() { -/* - // Block expression - x := { - a: f32 = 123; - b := a-123; - c := b/a; - give c; - }; // semicolon is required as it's an expression - - y := if x < 50 { - give x; - } else { - // TODO: Type cohesion is not yet finished - give 123; - }; // semicolon is required as it's an expression -*/ - - // This is allows for inline blocks of code and will be a useful feature to have when - // macros will be implemented into the language - - loops(); -} - -loops :: proc() { - // The C-style for loop - for i := 0; i < 123; i += 1 { - break; - } - for i := 0; i < 123; { - break; - } - for false { - break; - } - for { - break; - } - - for i in 0..123 { // 123 exclusive - } - - for i in 0..123-1 { // 122 inclusive - } - - for val, idx in 12..16 { - fmt.println(val, idx); - } - - primes := [?]int{2, 3, 5, 7, 11, 13, 17, 19}; - - for p in primes { - fmt.println(p); - } - - // Pointers to arrays, slices, or strings are allowed - for _ in &primes { - // ignore the value and just iterate across it - } - - - - name := "你好,世界"; - fmt.println(name); - for r in name { - #assert(type_of(r) == rune); - fmt.printf("%r\n", r); - } - - when false { - for i, size := 0; i < name.count; i += size { - r: rune; - r, size = utf8.decode_rune(name[i..]); - fmt.printf("%r\n", r); - } - } - - procedure_overloading(); -} - - -procedure_overloading :: proc() { - THINGF :: 14451.1; - THINGI :: 14451; - - foo :: proc() { - fmt.printf("Zero args\n"); - } - foo :: proc(i: int) { - fmt.printf("int arg, i=%d\n", i); - } - foo :: proc(f: f64) { - i := cast(int)f; - fmt.printf("f64 arg, f=%d\n", i); - } - - foo(); - foo(THINGF); - // foo(THINGI); // 14451 is just a number so it could go to either procedures - foo(cast(int)THINGI); - - - - - foo :: proc(x: ^i32) -> (int, int) { - fmt.println("^int"); - return 123, cast(int)(x^); - } - foo :: proc(x: rawptr) { - fmt.println("rawptr"); - } - - - a: i32 = 123; - b: f32; - c: rawptr; - fmt.println(foo(&a)); - foo(&b); - foo(c); - // foo(nil); // nil could go to numerous types thus the ambiguity - - f: proc(); - f = foo; // The correct `foo` to chosen - f(); - - - // See math.odin and atomic.odin for more examples -} diff --git a/misc/old_demos/demo006.odin b/misc/old_demos/demo006.odin deleted file mode 100644 index c2f64151b..000000000 --- a/misc/old_demos/demo006.odin +++ /dev/null @@ -1,310 +0,0 @@ -// import "core:atomic.odin"; -import "core:hash.odin"; -import "core:mem.odin"; -import "core:opengl.odin"; -import "core:strconv.odin"; -import "core:sync.odin"; -import win32 "core:sys/windows.odin"; - -import "core:fmt.odin"; -import "core:os.odin"; -import "core:math.odin"; - - -main :: proc() { -when true { -/* - Added: - * Unexported entities and fields using an underscore prefix - - See `sync.odin` and explain - - Removed: - * Maybe/option types - * Remove `type` keyword and other "reserved" keywords - * ..< and .. removed and replace with .. (half-closed range) - - Changed: - * `#assert` and `assert` return the value of the condition for semantic reasons - * thread_local -> #thread_local - * #include -> #load - * Files only get checked if they are actually used - * match x in y {} // For type match statements - * Version numbering now starts from 0.1.0 and uses the convention: - - major.minor.patch - * Core library additions to Windows specific stuff - */ - - { - Fruit :: enum { - APPLE, - BANANA, - COCONUT, - } - fmt.println(Fruit.names); - } - - { - A :: struct {x, y: f32}; - B :: struct #align 16 {x, y: f32}; - fmt.println("align_of(A) =", align_of(A)); - fmt.println("align_of(B) =", align_of(B)); - } - - { - // Removal of ..< and .. - for i in 0..16 { - } - // Is similar to - for i := 0; i < 16; i += 1 { - } - } - - { - thing: for i in 0..10 { - for j in i+1..10 { - if j == 2 { - fmt.println(i, j); - continue thing; - } - if j == 3 { - break thing; - } - } - } - - // Works with, `for`, `for in`, `match`, `match in` - // NOTE(bill): This solves most of the problems I need `goto` for - } - - { - t := type_info_of(int); - match i in t.variant { - case Type_Info_Integer, Type_Info_Float: - fmt.println("It's a number"); - } - - - x: any = 123; - foo: match i in x { - case int, f32: - fmt.println("It's an int or f32"); - break foo; - } - } - - { - cond := true; - x: int; - if cond { - x = 3; - } else { - x = 4; - } - - - // Ternary operator - y := cond ? 3 : 4; - - FOO :: true ? 123 : 432; // Constant ternary expression - fmt.println("Ternary values:", y, FOO); - } - - { - // Slices now store a capacity - buf: [256]u8; - s: []u8; - s = buf[..0]; // == buf[0..0]; - fmt.println("count =", len(s)); - fmt.println("capacity =", cap(s)); - append(&s, 1, 2, 3); - fmt.println(s); - - s = buf[1..2..3]; - fmt.println("count =", len(s)); - fmt.println("capacity =", cap(s)); - fmt.println(s); - - clear(&s); // Sets count to zero - } - - { - Foo :: struct { - x, y, z: f32, - ok: bool, - flags: u32, - } - foo_array: [256]Foo; - foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]); - // Useful for things like - // os.write(handle, foo_as_bytes); - - foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/size_of(Foo)); - // Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone? - // And if so what would the syntax be? - // slice_transmute([]Foo, foo_as_bytes); - } - - { - Vec3 :: [vector 3]f32; - - x := Vec3{1, 2, 3}; - y := Vec3{4, 5, 6}; - fmt.println(x < y); - fmt.println(x + y); - fmt.println(x - y); - fmt.println(x * y); - fmt.println(x / y); - - for i in x { - fmt.println(i); - } - - #assert(size_of([vector 7]bool) >= size_of([7]bool)); - #assert(size_of([vector 7]i32) >= size_of([7]i32)); - // align_of([vector 7]i32) != align_of([7]i32) // this may be the case - } - - { - // fmt.* changes - // bprint* returns `string` - - data: [256]u8; - str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!'); - fmt.println(str); - } - - { - x: [dynamic]f64; - reserve(&x, 16); - defer free(x); // `free` is overloaded for numerous types - // Number literals can have underscores in them for readability - append(&x, 2_000_000.500_000, 123, 5, 7); // variadic append - - for p, i in x { - if i > 0 { fmt.print(", "); } - fmt.print(p); - } - fmt.println(); - } - - { - // Dynamic array "literals" - x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; - defer free(x); - fmt.println(x); // fmt.print* supports printing of dynamic types - clear(&x); - fmt.println(x); - } - - { - m: map[f32]int; - reserve(&m, 16); - defer free(m); - - m[1.0] = 1278; - m[2.0] = 7643; - m[3.0] = 564; - _, ok := m[3.0]; - c := m[3.0]; - assert(ok && c == 564); - - fmt.print("map["); - i := 0; - for val, key in m { - if i > 0 { - fmt.print(", "); - } - fmt.printf("%v=%v", key, val); - i += 1; - } - fmt.println("]"); - } - { - m := map[string]u32{ - "a" = 56, - "b" = 13453, - "c" = 7654, - }; - defer free(m); - - c := m["c"]; - _, ok := m["c"]; - assert(ok && c == 7654); - fmt.println(m); - - delete(&m, "c"); // deletes entry with key "c" - _, found := m["c"]; - assert(!found); - - fmt.println(m); - clear(&m); - fmt.println(m); - - // NOTE: Fixed size maps are planned but we have not yet implemented - // them as we have had no need for them as of yet - } - - { - Vector3 :: struct{x, y, z: f32}; - Quaternion :: struct{x, y, z, w: f32}; - - // Variants - Frog :: struct { - ribbit_volume: f32, - jump_height: f32, - } - Door :: struct { - openness: f32, - } - Map :: struct { - width, height: f32, - place_positions: []Vector3, - place_names: []string, - } - - Entity :: struct { - // Common Fields - id: u64, - name: string, - using position: Vector3, - orientation: Quaternion, - flags: u32, - - variant: union { Frog, Door, Map }, - } - - entity: Entity; - entity.id = 1337; - // implicit conversion from variant to base type - entity.variant = Frog{ - ribbit_volume = 0.5, - jump_height = 2.1, - /*other data */ - }; - - entity.name = "Frank"; - entity.position = Vector3{1, 4, 9}; - - match e in entity.variant { - case Frog: - fmt.println("Ribbit"); - case Door: - fmt.println("Creak"); - case Map: - fmt.println("Rustle"); - case: - fmt.println("Just a normal entity"); - } - - if frog, ok := entity.variant.(Frog); ok { - fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position); - } - - // Panics if not the correct type - frog: Frog; - frog = entity.variant.(Frog); - frog, _ = entity.variant.(Frog); // ignore error and force cast - } -} -} - diff --git a/misc/old_demos/demo007.odin b/misc/old_demos/demo007.odin deleted file mode 100644 index d19446ecb..000000000 --- a/misc/old_demos/demo007.odin +++ /dev/null @@ -1,570 +0,0 @@ -import "core:fmt.odin" -import "core:strconv.odin" -import "core:mem.odin" -import "core:bits.odin" -import "core:hash.odin" -import "core:math.odin" -import "core:os.odin" -import "core:raw.odin" -import "core:sort.odin" -import "core:strings.odin" -import "core:types.odin" -import "core:utf16.odin" -import "core:utf8.odin" - -when ODIN_OS == "windows" { - import "core:atomics.odin" - import "core:opengl.odin" - import "core:thread.odin" - import win32 "core:sys/windows.odin" -} - -general_stuff :: proc() { - { // `do` for inline statmes rather than block - foo :: proc() do fmt.println("Foo!"); - if false do foo(); - for false do foo(); - when false do foo(); - - if false do foo(); - else do foo(); - } - - { // Removal of `++` and `--` (again) - x: int; - x += 1; - x -= 1; - } - { // Casting syntaxes - i := i32(137); - ptr := &i; - - fp1 := (^f32)(ptr); - // ^f32(ptr) == ^(f32(ptr)) - fp2 := cast(^f32)ptr; - - f1 := (^f32)(ptr)^; - f2 := (cast(^f32)ptr)^; - - // Questions: Should there be two ways to do it? - } - - /* - * Remove *_val_of built-in procedures - * size_of, align_of, offset_of - * type_of, type_info_of - */ - - { // `expand_to_tuple` built-in procedure - Foo :: struct { - x: int, - b: bool, - } - f := Foo{137, true}; - x, b := expand_to_tuple(f); - fmt.println(f); - fmt.println(x, b); - fmt.println(expand_to_tuple(f)); - } - - { - // .. half-closed range - // .. open range - - for in 0..2 {} // 0, 1 - for in 0..2 {} // 0, 1, 2 - } -} - -default_struct_values :: proc() { - { - Vector3 :: struct { - x: f32, - y: f32, - z: f32, - } - v: Vector3; - fmt.println(v); - } - { - // Default values must be constants - Vector3 :: struct { - x: f32 = 1, - y: f32 = 4, - z: f32 = 9, - } - v: Vector3; - fmt.println(v); - - v = Vector3{}; - fmt.println(v); - - // Uses the same semantics as a default values in a procedure - v = Vector3{137}; - fmt.println(v); - - v = Vector3{z = 137}; - fmt.println(v); - } - - { - Vector3 :: struct { - x := 1.0, - y := 4.0, - z := 9.0, - } - stack_default: Vector3; - stack_literal := Vector3{}; - heap_one := new(Vector3); defer free(heap_one); - heap_two := new_clone(Vector3{}); defer free(heap_two); - - fmt.println("stack_default - ", stack_default); - fmt.println("stack_literal - ", stack_literal); - fmt.println("heap_one - ", heap_one^); - fmt.println("heap_two - ", heap_two^); - - - N :: 4; - stack_array: [N]Vector3; - heap_array := new([N]Vector3); defer free(heap_array); - heap_slice := make([]Vector3, N); defer free(heap_slice); - fmt.println("stack_array[1] - ", stack_array[1]); - fmt.println("heap_array[1] - ", heap_array[1]); - fmt.println("heap_slice[1] - ", heap_slice[1]); - } -} - - - - -union_type :: proc() { - { - val: union{int, bool}; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - { - // There is a duality between `any` and `union` - // An `any` has a pointer to the data and allows for any type (open) - // A `union` has as binary blob to store the data and allows only certain types (closed) - // The following code is with `any` but has the same syntax - val: any; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - - Vector3 :: struct {x, y, z: f32}; - Quaternion :: struct {x, y, z: f32, w: f32 = 1}; - - // More realistic examples - { - // NOTE(bill): For the above basic examples, you may not have any - // particular use for it. However, my main use for them is not for these - // simple cases. My main use is for hierarchical types. Many prefer - // subtyping, embedding the base data into the derived types. Below is - // an example of this for a basic game Entity. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: any, - } - - Frog :: struct { - using entity: Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t^; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - } - - { - // NOTE(bill): A union can be used to achieve something similar. Instead - // of embedding the base data into the derived types, the derived data - // in embedded into the base type. Below is the same example of the - // basic game Entity but using an union. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: union {Frog, Monster}, - } - - Frog :: struct { - using entity: ^Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: ^Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(Entity); - t.derived = T{entity = t}; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - - // NOTE(bill): As you can see, the usage code has not changed, only its - // memory layout. Both approaches have their own advantages but they can - // be used together to achieve different results. The subtyping approach - // can allow for a greater control of the memory layout and memory - // allocation, e.g. storing the derivatives together. However, this is - // also its disadvantage. You must either preallocate arrays for each - // derivative separation (which can be easily missed) or preallocate a - // bunch of "raw" memory; determining the maximum size of the derived - // types would require the aid of metaprogramming. Unions solve this - // particular problem as the data is stored with the base data. - // Therefore, it is possible to preallocate, e.g. [100]Entity. - - // It should be noted that the union approach can have the same memory - // layout as the any and with the same type restrictions by using a - // pointer type for the derivatives. - - /* - Entity :: struct { - .. - derived: union{^Frog, ^Monster}; - } - - Frog :: struct { - using entity: Entity; - .. - } - Monster :: struct { - using entity: Entity; - .. - - } - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t; - return t; - } - */ - } -} - -parametric_polymorphism :: proc() { - print_value :: proc(value: $T) { - fmt.printf("print_value: %T %v\n", value, value); - } - - v1: int = 1; - v2: f32 = 2.1; - v3: f64 = 3.14; - v4: string = "message"; - - print_value(v1); - print_value(v2); - print_value(v3); - print_value(v4); - - fmt.println(); - - add :: proc(p, q: $T) -> T { - x: T = p + q; - return x; - } - - a := add(3, 4); - fmt.printf("a: %T = %v\n", a, a); - - b := add(3.2, 4.3); - fmt.printf("b: %T = %v\n", b, b); - - // This is how `new` is implemented - alloc_type :: proc(T: type) -> ^T { - t := cast(^T)alloc(size_of(T), align_of(T)); - t^ = T{}; // Use default initialization value - return t; - } - - copy_slice :: proc(dst, src: []$T) -> int { - n := min(len(dst), len(src)); - if n > 0 { - mem.copy(&dst[0], &src[0], n*size_of(T)); - } - return n; - } - - double_params :: proc(a: $A, b: $B) -> A { - return a + A(b); - } - - fmt.println(double_params(12, 1.345)); - - - - { // Polymorphic Types and Type Specialization - Table_Slot :: struct(Key, Value: type) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - } - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: type) { - count: int, - allocator: Allocator, - slots: []Table_Slot(Key, Value), - } - - // Only allow types that are specializations of a (polymorphic) slice - make_slice :: proc(T: type/[]$E, len: int) -> T { - return make(T, len); - } - - - // Only allow types that are specializations of `Table` - allocate :: proc(table: ^$T/Table, capacity: int) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - push_context c { - table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); - } - } - - expand :: proc(table: ^$T/Table) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - push_context c { - old_slots := table.slots; - - cap := max(2*cap(table.slots), TABLE_SIZE_MIN); - allocate(table, cap); - - for s in old_slots do if s.occupied { - put(table, s.key, s.value); - } - - free(old_slots); - } - } - - // Polymorphic determination of a polymorphic struct - // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { - put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { - hash := get_hash(key); // Ad-hoc method which would fail in a different scope - index := find_index(table, key, hash); - if index < 0 { - if f64(table.count) >= 0.75*f64(cap(table.slots)) { - expand(table); - } - assert(table.count <= cap(table.slots)); - - hash := get_hash(key); - index = int(hash % u32(cap(table.slots))); - - for table.slots[index].occupied { - if index += 1; index >= cap(table.slots) { - index = 0; - } - } - - table.count += 1; - } - - slot := &table.slots[index]; - slot.occupied = true; - slot.hash = hash; - slot.key = key; - slot.value = value; - } - - - // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { - find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { - hash := get_hash(key); - index := find_index(table, key, hash); - if index < 0 { - return Value{}, false; - } - return table.slots[index].value, true; - } - - find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { - if cap(table.slots) <= 0 do return -1; - - index := int(hash % u32(cap(table.slots))); - for table.slots[index].occupied { - if table.slots[index].hash == hash { - if table.slots[index].key == key { - return index; - } - } - - if index += 1; index >= cap(table.slots) { - index = 0; - } - } - - return -1; - } - - get_hash :: proc(s: string) -> u32 { // fnv32a - h: u32 = 0x811c9dc5; - for i in 0..len(s) { - h = (h ~ u32(s[i])) * 0x01000193; - } - return h; - } - - - table: Table(string, int); - - for i in 0..36 do put(&table, "Hellope", i); - for i in 0..42 do put(&table, "World!", i); - - found, _ := find(&table, "Hellope"); - fmt.printf("`found` is %v\n", found); - - found, _ = find(&table, "World!"); - fmt.printf("`found` is %v\n", found); - - // I would not personally design a hash table like this in production - // but this is a nice basic example - // A better approach would either use a `u64` or equivalent for the key - // and let the user specify the hashing function or make the user store - // the hashing procedure with the table - } -} - - - - -prefix_table := [?]string{ - "White", - "Red", - "Green", - "Blue", - "Octarine", - "Black", -}; - -threading_example :: proc() { - when ODIN_OS == "windows" { - unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - array[index] = array[len(array)-1]; - pop(array); - } - ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - copy(array[index..], array[index+1..]); - pop(array); - } - - worker_proc :: proc(t: ^thread.Thread) -> int { - for iteration in 1..5 { - fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration); - fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); - // win32.sleep(1); - } - return 0; - } - - threads := make([]^thread.Thread, 0, len(prefix_table)); - defer free(threads); - - for i in 0..len(prefix_table) { - if t := thread.create(worker_proc); t != nil { - t.init_context = context; - t.use_init_context = true; - t.user_index = len(threads); - append(&threads, t); - thread.start(t); - } - } - - for len(threads) > 0 { - for i := 0; i < len(threads); /**/ { - if t := threads[i]; thread.is_done(t) { - fmt.printf("Thread %d is done\n", t.user_index); - thread.destroy(t); - - ordered_remove(&threads, i); - } else { - i += 1; - } - } - } - } -} - -main :: proc() { - when false { - fmt.println("\n# general_stuff"); general_stuff(); - fmt.println("\n# default_struct_values"); default_struct_values(); - fmt.println("\n# union_type"); union_type(); - fmt.println("\n# parametric_polymorphism"); parametric_polymorphism(); - fmt.println("\n# threading_example"); threading_example(); - } -} - diff --git a/misc/old_demos/demo008.odin b/misc/old_demos/demo008.odin deleted file mode 100644 index a122414e7..000000000 --- a/misc/old_demos/demo008.odin +++ /dev/null @@ -1,778 +0,0 @@ -import "core:fmt.odin" -import "core:strconv.odin" -import "core:mem.odin" -import "core:bits.odin" -import "core:hash.odin" -import "core:math.odin" -import "core:math/rand.odin" -import "core:os.odin" -import "core:raw.odin" -import "core:sort.odin" -import "core:strings.odin" -import "core:types.odin" -import "core:utf16.odin" -import "core:utf8.odin" - -// File scope `when` statements -when ODIN_OS == "windows" { - import "core:atomics.odin" - import "core:thread.odin" - import win32 "core:sys/windows.odin" -} - -@(link_name="general_stuff") -general_stuff :: proc() { - fmt.println("# general_stuff"); - { // `do` for inline statements rather than block - foo :: proc() do fmt.println("Foo!"); - if false do foo(); - for false do foo(); - when false do foo(); - - if false do foo(); - else do foo(); - } - - { // Removal of `++` and `--` (again) - x: int; - x += 1; - x -= 1; - } - { // Casting syntaxes - i := i32(137); - ptr := &i; - - _ = (^f32)(ptr); - // ^f32(ptr) == ^(f32(ptr)) - _ = cast(^f32)ptr; - - _ = (^f32)(ptr)^; - _ = (cast(^f32)ptr)^; - - // Questions: Should there be two ways to do it? - } - - /* - * Remove *_val_of built-in procedures - * size_of, align_of, offset_of - * type_of, type_info_of - */ - - { // `expand_to_tuple` built-in procedure - Foo :: struct { - x: int, - b: bool, - } - f := Foo{137, true}; - x, b := expand_to_tuple(f); - fmt.println(f); - fmt.println(x, b); - fmt.println(expand_to_tuple(f)); - } - - { - // .. half-closed range - // .. open range - - for in 0..2 {} // 0, 1 - for in 0..2 {} // 0, 1, 2 - } - - { // Multiple sized booleans - - x0: bool; // default - x1: b8 = true; - x2: b16 = false; - x3: b32 = true; - x4: b64 = false; - - fmt.printf("x1: %T = %v;\n", x1, x1); - fmt.printf("x2: %T = %v;\n", x2, x2); - fmt.printf("x3: %T = %v;\n", x3, x3); - fmt.printf("x4: %T = %v;\n", x4, x4); - - // Having specific sized booleans is very useful when dealing with foreign code - // and to enforce specific alignment for a boolean, especially within a struct - } - - { // `distinct` types - // Originally, all type declarations would create a distinct type unless #type_alias was present. - // Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present. - // If the type expression is `struct`, `union`, `enum`, or `proc`, the types will always been distinct. - - Int32 :: i32; - #assert(Int32 == i32); - - My_Int32 :: distinct i32; - #assert(My_Int32 != i32); - - My_Struct :: struct{x: int}; - #assert(My_Struct != struct{x: int}); - } -} - -default_struct_values :: proc() { - fmt.println("# default_struct_values"); - { - Vector3 :: struct { - x: f32, - y: f32, - z: f32, - } - v: Vector3; - fmt.println(v); - } - { - // Default values must be constants - Vector3 :: struct { - x: f32 = 1, - y: f32 = 4, - z: f32 = 9, - } - v: Vector3; - fmt.println(v); - - v = Vector3{}; - fmt.println(v); - - // Uses the same semantics as a default values in a procedure - v = Vector3{137}; - fmt.println(v); - - v = Vector3{z = 137}; - fmt.println(v); - } - - { - Vector3 :: struct { - x := 1.0, - y := 4.0, - z := 9.0, - } - stack_default: Vector3; - stack_literal := Vector3{}; - heap_one := new(Vector3); defer free(heap_one); - heap_two := new_clone(Vector3{}); defer free(heap_two); - - fmt.println("stack_default - ", stack_default); - fmt.println("stack_literal - ", stack_literal); - fmt.println("heap_one - ", heap_one^); - fmt.println("heap_two - ", heap_two^); - - - N :: 4; - stack_array: [N]Vector3; - heap_array := new([N]Vector3); defer free(heap_array); - heap_slice := make([]Vector3, N); defer free(heap_slice); - fmt.println("stack_array[1] - ", stack_array[1]); - fmt.println("heap_array[1] - ", heap_array[1]); - fmt.println("heap_slice[1] - ", heap_slice[1]); - } -} - - - - -union_type :: proc() { - fmt.println("\n# union_type"); - { - val: union{int, bool}; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - { - // There is a duality between `any` and `union` - // An `any` has a pointer to the data and allows for any type (open) - // A `union` has as binary blob to store the data and allows only certain types (closed) - // The following code is with `any` but has the same syntax - val: any; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - - Vector3 :: struct {x, y, z: f32}; - Quaternion :: struct {x, y, z: f32, w: f32 = 1}; - - // More realistic examples - { - // NOTE(bill): For the above basic examples, you may not have any - // particular use for it. However, my main use for them is not for these - // simple cases. My main use is for hierarchical types. Many prefer - // subtyping, embedding the base data into the derived types. Below is - // an example of this for a basic game Entity. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: any, - } - - Frog :: struct { - using entity: Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t^; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - } - - { - // NOTE(bill): A union can be used to achieve something similar. Instead - // of embedding the base data into the derived types, the derived data - // in embedded into the base type. Below is the same example of the - // basic game Entity but using an union. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: union {Frog, Monster}, - } - - Frog :: struct { - using entity: ^Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: ^Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(Entity); - t.derived = T{entity = t}; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - - // NOTE(bill): As you can see, the usage code has not changed, only its - // memory layout. Both approaches have their own advantages but they can - // be used together to achieve different results. The subtyping approach - // can allow for a greater control of the memory layout and memory - // allocation, e.g. storing the derivatives together. However, this is - // also its disadvantage. You must either preallocate arrays for each - // derivative separation (which can be easily missed) or preallocate a - // bunch of "raw" memory; determining the maximum size of the derived - // types would require the aid of metaprogramming. Unions solve this - // particular problem as the data is stored with the base data. - // Therefore, it is possible to preallocate, e.g. [100]Entity. - - // It should be noted that the union approach can have the same memory - // layout as the any and with the same type restrictions by using a - // pointer type for the derivatives. - - /* - Entity :: struct { - .. - derived: union{^Frog, ^Monster}, - } - - Frog :: struct { - using entity: Entity, - .. - } - Monster :: struct { - using entity: Entity, - .. - - } - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t; - return t; - } - */ - } -} - -parametric_polymorphism :: proc() { - fmt.println("# parametric_polymorphism"); - - print_value :: proc(value: $T) { - fmt.printf("print_value: %T %v\n", value, value); - } - - v1: int = 1; - v2: f32 = 2.1; - v3: f64 = 3.14; - v4: string = "message"; - - print_value(v1); - print_value(v2); - print_value(v3); - print_value(v4); - - fmt.println(); - - add :: proc(p, q: $T) -> T { - x: T = p + q; - return x; - } - - a := add(3, 4); - fmt.printf("a: %T = %v\n", a, a); - - b := add(3.2, 4.3); - fmt.printf("b: %T = %v\n", b, b); - - // This is how `new` is implemented - alloc_type :: proc(T: type) -> ^T { - t := cast(^T)alloc(size_of(T), align_of(T)); - t^ = T{}; // Use default initialization value - return t; - } - - copy_slice :: proc(dst, src: []$T) -> int { - return mem.copy(&dst[0], &src[0], n*size_of(T)); - } - - double_params :: proc(a: $A, b: $B) -> A { - return a + A(b); - } - - fmt.println(double_params(12, 1.345)); - - - - { // Polymorphic Types and Type Specialization - Table_Slot :: struct(Key, Value: type) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - } - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: type) { - count: int, - allocator: Allocator, - slots: []Table_Slot(Key, Value), - } - - // Only allow types that are specializations of a (polymorphic) slice - make_slice :: proc(T: type/[]$E, len: int) -> T { - return make(T, len); - } - - - // Only allow types that are specializations of `Table` - allocate :: proc(table: ^$T/Table, capacity: int) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - context <- c { - table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); - } - } - - expand :: proc(table: ^$T/Table) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - context <- c { - old_slots := table.slots; - - cap := max(2*len(table.slots), TABLE_SIZE_MIN); - allocate(table, cap); - - for s in old_slots do if s.occupied { - put(table, s.key, s.value); - } - - free(old_slots); - } - } - - // Polymorphic determination of a polymorphic struct - // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { - put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { - hash := get_hash(key); // Ad-hoc method which would fail in a different scope - index := find_index(table, key, hash); - if index < 0 { - if f64(table.count) >= 0.75*f64(len(table.slots)) { - expand(table); - } - assert(table.count <= len(table.slots)); - - hash := get_hash(key); - index = int(hash % u32(len(table.slots))); - - for table.slots[index].occupied { - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - table.count += 1; - } - - slot := &table.slots[index]; - slot.occupied = true; - slot.hash = hash; - slot.key = key; - slot.value = value; - } - - - // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { - find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { - hash := get_hash(key); - index := find_index(table, key, hash); - if index < 0 { - return Value{}, false; - } - return table.slots[index].value, true; - } - - find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { - if len(table.slots) <= 0 do return -1; - - index := int(hash % u32(len(table.slots))); - for table.slots[index].occupied { - if table.slots[index].hash == hash { - if table.slots[index].key == key { - return index; - } - } - - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - return -1; - } - - get_hash :: proc(s: string) -> u32 { // fnv32a - h: u32 = 0x811c9dc5; - for i in 0..len(s) { - h = (h ~ u32(s[i])) * 0x01000193; - } - return h; - } - - - table: Table(string, int); - - for i in 0..36 do put(&table, "Hellope", i); - for i in 0..42 do put(&table, "World!", i); - - found, _ := find(&table, "Hellope"); - fmt.printf("`found` is %v\n", found); - - found, _ = find(&table, "World!"); - fmt.printf("`found` is %v\n", found); - - // I would not personally design a hash table like this in production - // but this is a nice basic example - // A better approach would either use a `u64` or equivalent for the key - // and let the user specify the hashing function or make the user store - // the hashing procedure with the table - } -} - - - - -prefix_table := [?]string{ - "White", - "Red", - "Green", - "Blue", - "Octarine", - "Black", -}; - -threading_example :: proc() { - when ODIN_OS == "windows" { - fmt.println("# threading_example"); - - unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - array[index] = array[len(array)-1]; - pop(array); - } - ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - copy(array[index..], array[index+1..]); - pop(array); - } - - worker_proc :: proc(t: ^thread.Thread) -> int { - for iteration in 1..5 { - fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration); - fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); - // win32.sleep(1); - } - return 0; - } - - threads := make([dynamic]^thread.Thread, 0, len(prefix_table)); - defer free(threads); - - for in prefix_table { - if t := thread.create(worker_proc); t != nil { - t.init_context = context; - t.use_init_context = true; - t.user_index = len(threads); - append(&threads, t); - thread.start(t); - } - } - - for len(threads) > 0 { - for i := 0; i < len(threads); /**/ { - if t := threads[i]; thread.is_done(t) { - fmt.printf("Thread %d is done\n", t.user_index); - thread.destroy(t); - - ordered_remove(&threads, i); - } else { - i += 1; - } - } - } - } -} - -array_programming :: proc() { - fmt.println("# array_programming"); - { - a := [3]f32{1, 2, 3}; - b := [3]f32{5, 6, 7}; - c := a * b; - d := a + b; - e := 1 + (c - d) / 2; - fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5] - } - - { - a := [3]f32{1, 2, 3}; - b := swizzle(a, 2, 1, 0); - assert(b == [3]f32{3, 2, 1}); - - c := swizzle(a, 0, 0); - assert(c == [2]f32{1, 1}); - assert(c == 1); - } - - { - Vector3 :: distinct [3]f32; - a := Vector3{1, 2, 3}; - b := Vector3{5, 6, 7}; - c := (a * b)/2 + 1; - d := c.x + c.y + c.z; - fmt.printf("%.1f\n", d); // 22.0 - - cross :: proc(a, b: Vector3) -> Vector3 { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return i - j; - } - - blah :: proc(a: Vector3) -> f32 { - return a.x + a.y + a.z; - } - - x := cross(a, b); - fmt.println(x); - fmt.println(blah(x)); - } -} - - -using println in import "core:fmt.odin" - -using_in :: proc() { - fmt.println("# using in"); - using print in fmt; - - println("Hellope1"); - print("Hellope2\n"); - - Foo :: struct { - x, y: int, - b: bool, - } - f: Foo; - f.x, f.y = 123, 321; - println(f); - using x, y in f; - x, y = 456, 654; - println(f); -} - -named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); - - foo0 :: proc() -> int { - return 123; - } - foo1 :: proc() -> (a: int) { - a = 123; - return; - } - foo2 :: proc() -> (a, b: int) { - // Named return values act like variables within the scope - a = 321; - b = 567; - return b, a; - } - fmt.println("foo0 =", foo0()); // 123 - fmt.println("foo1 =", foo1()); // 123 - fmt.println("foo2 =", foo2()); // 567 321 -} - - -enum_export :: proc() { - fmt.println("# enum #export"); - - Foo :: enum #export {A, B, C}; - - f0 := A; - f1 := B; - f2 := C; - fmt.println(f0, f1, f2); -} - -explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); - - add_ints :: proc(a, b: int) -> int { - x := a + b; - fmt.println("add_ints", x); - return x; - } - add_floats :: proc(a, b: f32) -> f32 { - x := a + b; - fmt.println("add_floats", x); - return x; - } - add_numbers :: proc(a: int, b: f32, c: u8) -> int { - x := int(a) + int(b) + int(c); - fmt.println("add_numbers", x); - return x; - } - - add :: proc[add_ints, add_floats, add_numbers]; - - add(int(1), int(2)); - add(f32(1), f32(2)); - add(int(1), f32(2), u8(3)); - - add(1, 2); // untyped ints coerce to int tighter than f32 - add(1.0, 2.0); // untyped floats coerce to f32 tighter than int - add(1, 2, 3); // three parameters - - // Ambiguous answers - // add(1.0, 2); - // add(1, 2.0); -} - -complete_switch :: proc() { - fmt.println("# complete_switch"); - { // enum - Foo :: enum #export { - A, - B, - C, - D, - } - - b := Foo.B; - f := Foo.A; - #complete switch f { - case A: fmt.println("A"); - case B: fmt.println("B"); - case C: fmt.println("C"); - case D: fmt.println("D"); - case: fmt.println("?"); - } - } - { // union - Foo :: union {int, bool}; - f: Foo = 123; - #complete switch in f { - case int: fmt.println("int"); - case bool: fmt.println("bool"); - case: - } - } -} - - -main :: proc() { - when true { - general_stuff(); - default_struct_values(); - union_type(); - parametric_polymorphism(); - threading_example(); - array_programming(); - using_in(); - named_proc_return_parameters(); - enum_export(); - explicit_procedure_overloading(); - complete_switch(); - } -} diff --git a/misc/old_demos/old_runtime.odin b/misc/old_demos/old_runtime.odin deleted file mode 100644 index e605e7820..000000000 --- a/misc/old_demos/old_runtime.odin +++ /dev/null @@ -1,412 +0,0 @@ -#include "win32.odin" - -assume :: proc(cond: bool) #foreign "llvm.assume" - -__debug_trap :: proc() #foreign "llvm.debugtrap" -__trap :: proc() #foreign "llvm.trap" -read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter" - -bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16" -bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32" -bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64" - -byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16" -byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32" -byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64" - -fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32" -fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64" - -// TODO(bill): make custom heap procedures -heap_alloc :: proc(len: int) -> rawptr #foreign "malloc" -heap_dealloc :: proc(ptr: rawptr) #foreign "free" - -memory_zero :: proc(data: rawptr, len: int) { - d := slice_ptr(data as ^byte, len) - for i := 0; i < len; i++ { - d[i] = 0 - } -} - -memory_compare :: proc(dst, src: rawptr, len: int) -> int { - s1, s2: ^byte = dst, src - for i := 0; i < len; i++ { - a := ptr_offset(s1, i)^ - b := ptr_offset(s2, i)^ - if a != b { - return (a - b) as int - } - } - return 0 -} - -memory_copy :: proc(dst, src: rawptr, n: int) #inline { - if dst == src { - return - } - - v128b :: type {4}u32 - #assert(align_of(v128b) == 16) - - d, s: ^byte = dst, src - - for ; s as uint % 16 != 0 && n != 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - - if d as uint % 16 == 0 { - for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 { - (d as ^v128b)^ = (s as ^v128b)^ - } - - if n&8 != 0 { - (d as ^u64)^ = (s as ^u64)^ - d, s = ptr_offset(d, 8), ptr_offset(s, 8) - } - if n&4 != 0 { - (d as ^u32)^ = (s as ^u32)^; - d, s = ptr_offset(d, 4), ptr_offset(s, 4) - } - if n&2 != 0 { - (d as ^u16)^ = (s as ^u16)^ - d, s = ptr_offset(d, 2), ptr_offset(s, 2) - } - if n&1 != 0 { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - return; - } - - // IMPORTANT NOTE(bill): Little endian only - LS :: proc(a, b: u32) -> u32 #inline { return a << b } - RS :: proc(a, b: u32) -> u32 #inline { return a >> b } - /* NOTE(bill): Big endian version - LS :: proc(a, b: u32) -> u32 #inline { return a >> b; } - RS :: proc(a, b: u32) -> u32 #inline { return a << b; } - */ - - w, x: u32 - - if d as uint % 4 == 1 { - w = (s as ^u32)^ - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - n -= 3 - - for n > 16 { - d32 := d as ^u32 - s32 := ptr_offset(s, 1) as ^u32 - x = s32^; d32^ = LS(w, 24) | RS(x, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 24) | RS(w, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 24) | RS(x, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 24) | RS(w, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - - } else if d as uint % 4 == 2 { - w = (s as ^u32)^ - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - n -= 2 - - for n > 17 { - d32 := d as ^u32 - s32 := ptr_offset(s, 2) as ^u32 - x = s32^; d32^ = LS(w, 16) | RS(x, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 16) | RS(w, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 16) | RS(x, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 16) | RS(w, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - - } else if d as uint % 4 == 3 { - w = (s as ^u32)^ - d^ = s^ - n -= 1 - - for n > 18 { - d32 := d as ^u32 - s32 := ptr_offset(s, 3) as ^u32 - x = s32^; d32^ = LS(w, 8) | RS(x, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 8) | RS(w, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 8) | RS(x, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 8) | RS(w, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - } - - if n&16 != 0 { - (d as ^v128b)^ = (s as ^v128b)^ - d, s = ptr_offset(d, 16), ptr_offset(s, 16) - } - if n&8 != 0 { - (d as ^u64)^ = (s as ^u64)^ - d, s = ptr_offset(d, 8), ptr_offset(s, 8) - } - if n&4 != 0 { - (d as ^u32)^ = (s as ^u32)^; - d, s = ptr_offset(d, 4), ptr_offset(s, 4) - } - if n&2 != 0 { - (d as ^u16)^ = (s as ^u16)^ - d, s = ptr_offset(d, 2), ptr_offset(s, 2) - } - if n&1 != 0 { - d^ = s^ - } -} - -memory_move :: proc(dst, src: rawptr, n: int) #inline { - d, s: ^byte = dst, src - if d == s { - return - } - if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s { - memory_copy(d, s, n) - return - } - - // TODO(bill): Vectorize the shit out of this - if d < s { - if s as int % size_of(int) == d as int % size_of(int) { - for d as int % size_of(int) != 0 { - if n == 0 { - return - } - n-- - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - di, si := d as ^int, s as ^int - for n >= size_of(int) { - di^ = si^ - di, si = ptr_offset(di, 1), ptr_offset(si, 1) - n -= size_of(int) - } - } - for ; n > 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - } else { - if s as int % size_of(int) == d as int % size_of(int) { - for ptr_offset(d, n) as int % size_of(int) != 0 { - if n == 0 { - return - } - n-- - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - for n >= size_of(int) { - n -= size_of(int) - di := ptr_offset(d, n) as ^int - si := ptr_offset(s, n) as ^int - di^ = si^ - } - for ; n > 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - } - for n > 0 { - n-- - dn := ptr_offset(d, n) - sn := ptr_offset(s, n) - dn^ = sn^ - } - } -} - -__string_eq :: proc(a, b: string) -> bool { - if len(a) != len(b) { - return false - } - if ^a[0] == ^b[0] { - return true - } - return memory_compare(^a[0], ^b[0], len(a)) == 0 -} - -__string_cmp :: proc(a, b : string) -> int { - min_len := len(a) - if len(b) < min_len { - min_len = len(b) - } - for i := 0; i < min_len; i++ { - x := a[i] - y := b[i] - if x < y { - return -1 - } else if x > y { - return +1 - } - } - - if len(a) < len(b) { - return -1 - } else if len(a) > len(b) { - return +1 - } - return 0 -} - -__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } -__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } -__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } -__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } -__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } - - - - -Allocation_Mode :: type enum { - ALLOC, - DEALLOC, - DEALLOC_ALL, - RESIZE, -} - - - -Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, flags: u64) -> rawptr - -Allocator :: type struct { - procedure: Allocator_Proc; - data: rawptr -} - - -Context :: type struct { - thread_ptr: rawptr - - user_data: rawptr - user_index: int - - allocator: Allocator -} - -#thread_local context: Context - -DEFAULT_ALIGNMENT :: 2*size_of(int) - - -__check_context :: proc() { - if context.allocator.procedure == null { - context.allocator = __default_allocator() - } - if context.thread_ptr == null { - // TODO(bill): - // context.thread_ptr = current_thread_pointer() - } -} - - -alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) } - -alloc_align :: proc(size, alignment: int) -> rawptr #inline { - __check_context() - a := context.allocator - return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0) -} - -dealloc :: proc(ptr: rawptr) #inline { - __check_context() - a := context.allocator - _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0) -} -dealloc_all :: proc(ptr: rawptr) #inline { - __check_context() - a := context.allocator - _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0) -} - - -resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) } -resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline { - __check_context() - a := context.allocator - return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0) -} - - - -default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr { - if old_memory == null { - return alloc_align(new_size, alignment) - } - - if new_size == 0 { - dealloc(old_memory) - return null - } - - if new_size == old_size { - return old_memory - } - - new_memory := alloc_align(new_size, alignment) - if new_memory == null { - return null - } - - memory_copy(new_memory, old_memory, min(old_size, new_size)); - dealloc(old_memory) - return new_memory -} - - -__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, flags: u64) -> rawptr { - using Allocation_Mode - match mode { - case ALLOC: - return heap_alloc(size) - case RESIZE: - return default_resize_align(old_memory, old_size, size, alignment) - case DEALLOC: - heap_dealloc(old_memory) - case DEALLOC_ALL: - // NOTE(bill): Does nothing - } - - return null -} - -__default_allocator :: proc() -> Allocator { - return Allocator{ - __default_allocator_proc, - null, - } -} - - - - -__assert :: proc(msg: string) { - file_write(file_get_standard(File_Standard.ERROR), msg as []byte) - // TODO(bill): Which is better? - // __trap() - __debug_trap() -} diff --git a/misc/old_stuff/demo_backup.odin b/misc/old_stuff/demo_backup.odin deleted file mode 100644 index b8bbbb02d..000000000 --- a/misc/old_stuff/demo_backup.odin +++ /dev/null @@ -1,430 +0,0 @@ -import ( - "fmt.odin"; - "atomics.odin"; - "bits.odin"; - "decimal.odin"; - "hash.odin"; - "math.odin"; - "mem.odin"; - "opengl.odin"; - "os.odin"; - "raw.odin"; - "strconv.odin"; - "strings.odin"; - "sync.odin"; - "sort.odin"; - "types.odin"; - "utf8.odin"; - "utf16.odin"; -/* -*/ -) - - -general_stuff :: proc() { - // Complex numbers - a := 3 + 4i; - b: complex64 = 3 + 4i; - c: complex128 = 3 + 4i; - d := complex(2, 3); - - e := a / conj(a); - fmt.println("(3+4i)/(3-4i) =", e); - fmt.println(real(e), "+", imag(e), "i"); - - - // C-style variadic procedures - foreign __llvm_core { - // The variadic part allows for extra type checking too which C does not provide - c_printf :: proc(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf" ---; - } - str := "%d\n\x00"; - // c_printf(&str[0], i32(789456123)); - - - Foo :: struct { - x: int; - y: f32; - z: string; - } - foo := Foo{123, 0.513, "A string"}; - x, y, z := expand_to_tuple(foo); - fmt.println(x, y, z); - #assert(type_of(x) == int); - #assert(type_of(y) == f32); - #assert(type_of(z) == string); - - - // By default, all variables are zeroed - // This can be overridden with the "uninitialized value" - // This is similar to `nil` but applied to everything - undef_int: int = ---; - - - // Context system is now implemented using Implicit Parameter Passing (IPP) - // The previous implementation was Thread Local Storage (TLS) - // IPP has the advantage that it works on systems without TLS and that you can - // link the context to the stack frame and thus look at previous contexts - // - // It does mean that a pointer is implicitly passed procedures with the default - // Odin calling convention (#cc_odin) - // This can be overridden with something like #cc_contextless or #cc_c if performance - // is worried about - -} - -foreign_blocks :: proc() { - // See sys/windows.odin -} - -default_arguments :: proc() { - hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b); - fmt.println("\nTesting default arguments:"); - hello(1, 2); - hello(1); - hello(); -} - -named_arguments :: proc() { - Colour :: enum { - Red, - Orange, - Yellow, - Green, - Blue, - Octarine, - }; - using Colour; - - make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) { - fmt.println(); - fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase); - } - - make_character("Frank", "¡Ay, caramba!", Blue, Green); - - - // As the procedures have more and more parameters, it is very easy - // to get many of the arguments in the wrong order especialy if the - // types are the same - make_character("¡Ay, caramba!", "Frank", Green, Blue); - - // Named arguments help to disambiguate this problem - make_character(catch_phrase = "¡Ay, caramba!", name = "Frank", - least_favourite_colour = Green, favourite_colour = Blue); - - - // The named arguments can be specifed in any order. - make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!", - least_favourite_colour = Green, name = "Dennis"); - - - // NOTE: You cannot mix named arguments with normal values - /* - make_character("Dennis", - favourite_colour = Octarine, catch_phrase = "U wot m8!", - least_favourite_colour = Green); - */ - - - // Named arguments can also aid with default arguments - numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14, - d := "The Best String!", e := false, f := 10.3/3.1, g := false) { - g_str := g ? "true" : "false"; - fmt.printf("How many?! %s: %v\n", s, g_str); - } - - numerous_things("First"); - numerous_things(s = "Second", g = true); - - - // Default values can be placed anywhere, not just at the end like in other languages - weird :: proc(pre: string, mid: int = 0, post: string) { - fmt.println(pre, mid, post); - } - - weird("How many things", 42, "huh?"); - weird(pre = "Prefix", post = "Pat"); - -} - - -default_return_values :: proc() { - foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") { - match x { - case 0: return; - case 1: return "Goodbye"; - case 2: return "Goodbye", "cruel world.."; - case 3: return second = "cruel world..", first = "Goodbye"; - } - - return second = "my old friend."; - } - - fmt.printf("%s %s\n", foo(0)); - fmt.printf("%s %s\n", foo(1)); - fmt.printf("%s %s\n", foo(2)); - fmt.printf("%s %s\n", foo(3)); - fmt.printf("%s %s\n", foo(4)); - fmt.println(); - - - // A more "real" example - Error :: enum { - None, - WhyTheNumberThree, - TenIsTooBig, - }; - - Entity :: struct { - name: string; - id: u32; - } - - some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) { - match { - case input == 3: return err = Error.WhyTheNumberThree; - case input >= 10: return err = Error.TenIsTooBig; - } - - e := new(Entity); - e.id = u32(input); - - return result = e; - } -} - -call_location :: proc() { - amazing :: proc(n: int, using loc := #caller_location) { - fmt.printf("%s(%d:%d) just asked to do something amazing.\n", - fully_pathed_filename, line, column); - fmt.printf("Normal -> %d\n", n); - fmt.printf("Amazing -> %d\n", n+1); - fmt.println(); - } - - loc := #location(main); - fmt.println("`main` is located at", loc); - - fmt.println("This line is located at", #location()); - fmt.println(); - - amazing(3); - amazing(4, #location(call_location)); - - // See _preload.odin for the implementations of `assert` and `panic` - -} - - -explicit_parametric_polymorphic_procedures :: proc() { - // This is how `new` is actually implemented, see _preload.odin - alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T)); - - int_ptr := alloc_type(int); - defer free(int_ptr); - int_ptr^ = 137; - fmt.println(int_ptr, int_ptr^); - - // Named arguments work too! - another_ptr := alloc_type(T = f32); - defer free(another_ptr); - - - add :: proc(T: type, args: ..T) -> T { - res: T; - for arg in args do res += arg; - return res; - } - - fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6)); - - swap :: proc(T: type, a, b: ^T) { - tmp := a^; - a^ = b^; - b^ = tmp; - } - - a, b: int = 3, 4; - fmt.println("Pre-swap:", a, b); - swap(int, &a, &b); - fmt.println("Post-swap:", a, b); - a, b = b, a; // Or use this syntax for this silly example case - - - Vector2 :: struct {x, y: f32;}; - { - // A more complicated example using subtyping - // Something like this could be used in a game - - Entity :: struct { - using position: Vector2; - flags: u64; - id: u64; - derived: any; - } - - Rock :: struct { - using entity: Entity; - heavy: bool; - } - Door :: struct { - using entity: Entity; - open: bool; - } - Monster :: struct { - using entity: Entity; - is_robot: bool; - is_zombie: bool; - } - - new_entity :: proc(T: type, x, y: f32) -> ^T { - result := new(T); - result.derived = result^; - result.x = x; - result.y = y; - - return result; - } - - entities: [dynamic]^Entity; - - rock := new_entity(Rock, 3, 5); - - // Named arguments work too! - door := new_entity(T = Door, x = 3, y = 6); - - // And named arguments can be any order - monster := new_entity( - y = 1, - x = 2, - T = Monster, - ); - - append(&entities, rock, door, monster); - - fmt.println("Subtyping"); - for entity in entities { - match e in entity.derived { - case Rock: fmt.println("Rock", e.x, e.y); - case Door: fmt.println("Door", e.x, e.y); - case Monster: fmt.println("Monster", e.x, e.y); - } - } - } - { - Entity :: struct { - using position: Vector2; - flags: u64; - id: u64; - variant: union { Rock, Door, Monster }; - } - - Rock :: struct { - using entity: ^Entity; - heavy: bool; - } - Door :: struct { - using entity: ^Entity; - open: bool; - } - Monster :: struct { - using entity: ^Entity; - is_robot: bool; - is_zombie: bool; - } - - new_entity :: proc(T: type, x, y: f32) -> ^T { - result := new(Entity); - result.variant = T{entity = result}; - result.x = x; - result.y = y; - - return cast(^T)&result.variant; - } - - entities: [dynamic]^Entity; - - rock := new_entity(Rock, 3, 5); - - // Named arguments work too! - door := new_entity(T = Door, x = 3, y = 6); - - // And named arguments can be any order - monster := new_entity( - y = 1, - x = 2, - T = Monster, - ); - - append(&entities, rock, door, monster); - - fmt.println("Union"); - for entity in entities { - match e in entity.variant { - case Rock: fmt.println("Rock", e.x, e.y); - case Door: fmt.println("Door", e.x, e.y); - case Monster: fmt.println("Monster", e.x, e.y); - } - } - } -} - - -implicit_polymorphic_assignment :: proc() { - yep :: proc(p: proc(x: int)) { - p(123); - } - - frank :: proc(x: $T) do fmt.println("frank ->", x); - tim :: proc(x, y: $T) do fmt.println("tim ->", x, y); - yep(frank); - // yep(tim); -} - - - - -main :: proc() { -/* - foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y); - foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y); - foo :: proc(x: type) do fmt.println("#3", type_info(x)); - - f :: foo; - - f(y = 3785.1546, x = 123); - f(x = int, y = 897.513); - f(x = f32); - - general_stuff(); - foreign_blocks(); - default_arguments(); - named_arguments(); - default_return_values(); - call_location(); - explicit_parametric_polymorphic_procedures(); - implicit_polymorphic_assignment(); - - - // Command line argument(s)! - // -opt=0,1,2,3 -*/ -/* - program := "+ + * - /"; - accumulator := 0; - - for token in program { - match token { - case '+': accumulator += 1; - case '-': accumulator -= 1; - case '*': accumulator *= 2; - case '/': accumulator /= 2; - case: // Ignore everything else - } - } - - fmt.printf("The program \"%s\" calculates the value %d\n", - program, accumulator); -*/ -} diff --git a/src/checker.cpp b/src/checker.cpp index 1bb437beb..696802e99 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4447,6 +4447,14 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) { ac->foreign_import_priority_index = exact_value_to_i64(ev); } return true; + } else if (name == "extra_linker_flags") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_String) { + error(elem, "Expected a string value for '%.*s'", LIT(name)); + } else { + ac->extra_linker_flags = ev.value_string; + } + return true; } return false; } @@ -4506,6 +4514,10 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) { if (ac.foreign_import_priority_index != 0) { e->LibraryName.priority_index = ac.foreign_import_priority_index; } + String extra_linker_flags = string_trim_whitespace(ac.extra_linker_flags); + if (extra_linker_flags.len != 0) { + e->LibraryName.extra_linker_flags = extra_linker_flags; + } if (has_asm_extension(fullpath)) { if (build_context.metrics.arch != TargetArch_amd64 || diff --git a/src/checker.hpp b/src/checker.hpp index b82612813..2918b7e83 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -121,6 +121,7 @@ struct AttributeContext { bool set_cold : 1; u32 optimization_mode; // ProcedureOptimizationMode i64 foreign_import_priority_index; + String extra_linker_flags; String objc_class; String objc_name; diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 814769f57..7488e955a 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -915,18 +915,20 @@ gb_internal void odin_doc_update_entities(OdinDocWriter *w) { auto entities = array_make(heap_allocator(), 0, w->entity_cache.count); defer (array_free(&entities)); - for (auto const &entry : w->entity_cache) { - array_add(&entities, entry.key); + for (u32 i = 0; i < w->entity_cache.count; i++) { + Entity *e = w->entity_cache.entries[i].key; + array_add(&entities, e); } for (Entity *e : entities) { + GB_ASSERT(e != nullptr); OdinDocTypeIndex type_index = odin_doc_type(w, e->type); gb_unused(type_index); } } - for (auto const &entry : w->entity_cache) { - Entity *e = entry.key; - OdinDocEntityIndex entity_index = entry.value; + for (u32 i = 0; i < w->entity_cache.count; i++) { + Entity *e = w->entity_cache.entries[i].key; + OdinDocEntityIndex entity_index = w->entity_cache.entries[i].value; OdinDocTypeIndex type_index = odin_doc_type(w, e->type); OdinDocEntityIndex foreign_library = 0; diff --git a/src/entity.cpp b/src/entity.cpp index 0c3629b2b..d6f4edece 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -259,6 +259,7 @@ struct Entity { Slice paths; String name; i64 priority_index; + String extra_linker_flags; } LibraryName; i32 Nil; struct { diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 1204050ef..dabdd6829 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -563,7 +563,7 @@ namespace lbAbiAmd64SysV { gb_internal void fixup(LLVMTypeRef t, Array *cls); gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention); gb_internal Array classify(LLVMTypeRef t); - gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes); + gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes, LLVMTypeRef type); gb_internal LB_ABI_INFO(abi_info) { lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType); @@ -662,7 +662,7 @@ namespace lbAbiAmd64SysV { // the ABI rules for SysV reg_type = type; } else { - reg_type = llreg(c, cls); + reg_type = llreg(c, cls, type); } return lb_arg_type_direct(type, reg_type, nullptr, nullptr); } @@ -785,67 +785,92 @@ namespace lbAbiAmd64SysV { } - gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes) { + gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes, LLVMTypeRef type) { auto types = array_make(heap_allocator(), 0, reg_classes.count); - for (isize i = 0; i < reg_classes.count; /**/) { - RegClass reg_class = reg_classes[i]; - switch (reg_class) { - case RegClass_Int: - array_add(&types, LLVMIntTypeInContext(c, 64)); - break; - case RegClass_SSEFv: - case RegClass_SSEDv: - case RegClass_SSEInt8: - case RegClass_SSEInt16: - case RegClass_SSEInt32: - case RegClass_SSEInt64: - { - unsigned elems_per_word = 0; - LLVMTypeRef elem_type = nullptr; - switch (reg_class) { - case RegClass_SSEFv: - elems_per_word = 2; - elem_type = LLVMFloatTypeInContext(c); - break; - case RegClass_SSEDv: - elems_per_word = 1; - elem_type = LLVMDoubleTypeInContext(c); - break; - case RegClass_SSEInt8: - elems_per_word = 64/8; - elem_type = LLVMIntTypeInContext(c, 8); - break; - case RegClass_SSEInt16: - elems_per_word = 64/16; - elem_type = LLVMIntTypeInContext(c, 16); - break; - case RegClass_SSEInt32: - elems_per_word = 64/32; - elem_type = LLVMIntTypeInContext(c, 32); - break; - case RegClass_SSEInt64: - elems_per_word = 64/64; - elem_type = LLVMIntTypeInContext(c, 64); - break; - } - unsigned vec_len = llvec_len(reg_classes, i+1); - LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); - array_add(&types, vec_type); - i += vec_len; - continue; - } + bool all_ints = true; + for (RegClass reg_class : reg_classes) { + if (reg_class != RegClass_Int) { + all_ints = false; break; - case RegClass_SSEFs: - array_add(&types, LLVMFloatTypeInContext(c)); - break; - case RegClass_SSEDs: - array_add(&types, LLVMDoubleTypeInContext(c)); - break; - default: - GB_PANIC("Unhandled RegClass"); } - i += 1; + } + + if (all_ints) { + i64 sz = lb_sizeof(type); + for_array(i, reg_classes) { + GB_ASSERT(sz != 0); + // TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!! + if (sz >= 8) { + array_add(&types, LLVMIntTypeInContext(c, 64)); + sz -= 8; + } else { + array_add(&types, LLVMIntTypeInContext(c, cast(unsigned)(sz*8))); + sz = 0; + } + } + } else { + for (isize i = 0; i < reg_classes.count; /**/) { + RegClass reg_class = reg_classes[i]; + switch (reg_class) { + case RegClass_Int: + // TODO(bill): is this even correct? BECAUSE LLVM DOES NOT DOCUMENT ANY OF THIS!!! + array_add(&types, LLVMIntTypeInContext(c, 64)); + break; + case RegClass_SSEFv: + case RegClass_SSEDv: + case RegClass_SSEInt8: + case RegClass_SSEInt16: + case RegClass_SSEInt32: + case RegClass_SSEInt64: + { + unsigned elems_per_word = 0; + LLVMTypeRef elem_type = nullptr; + switch (reg_class) { + case RegClass_SSEFv: + elems_per_word = 2; + elem_type = LLVMFloatTypeInContext(c); + break; + case RegClass_SSEDv: + elems_per_word = 1; + elem_type = LLVMDoubleTypeInContext(c); + break; + case RegClass_SSEInt8: + elems_per_word = 64/8; + elem_type = LLVMIntTypeInContext(c, 8); + break; + case RegClass_SSEInt16: + elems_per_word = 64/16; + elem_type = LLVMIntTypeInContext(c, 16); + break; + case RegClass_SSEInt32: + elems_per_word = 64/32; + elem_type = LLVMIntTypeInContext(c, 32); + break; + case RegClass_SSEInt64: + elems_per_word = 64/64; + elem_type = LLVMIntTypeInContext(c, 64); + break; + } + + unsigned vec_len = llvec_len(reg_classes, i+1); + LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); + array_add(&types, vec_type); + i += vec_len; + continue; + } + break; + case RegClass_SSEFs: + array_add(&types, LLVMFloatTypeInContext(c)); + break; + case RegClass_SSEDs: + array_add(&types, LLVMDoubleTypeInContext(c)); + break; + default: + GB_PANIC("Unhandled RegClass"); + } + i += 1; + } } if (types.count == 1) { diff --git a/src/main.cpp b/src/main.cpp index 82c20cfe6..5ab6ed66c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -278,6 +278,13 @@ gb_internal i32 linker_stage(lbGenerator *gen) { } } + for (Entity *e : gen->foreign_libraries) { + GB_ASSERT(e->kind == Entity_LibraryName); + if (e->LibraryName.extra_linker_flags.len != 0) { + lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags)); + } + } + if (build_context.build_mode == BuildMode_DynamicLibrary) { link_settings = gb_string_append_fmt(link_settings, " /DLL"); } else { @@ -449,6 +456,12 @@ gb_internal i32 linker_stage(lbGenerator *gen) { } } + for (Entity *e : gen->foreign_libraries) { + GB_ASSERT(e->kind == Entity_LibraryName); + if (e->LibraryName.extra_linker_flags.len != 0) { + lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags)); + } + } gbString object_files = gb_string_make(heap_allocator(), ""); defer (gb_string_free(object_files)); @@ -581,13 +594,13 @@ gb_internal Array setup_args(int argc, char const **argv) { gb_internal void print_usage_line(i32 indent, char const *fmt, ...) { while (indent --> 0) { - gb_printf_err("\t"); + gb_printf("\t"); } va_list va; va_start(va, fmt); - gb_printf_err_va(fmt, va); + gb_printf_va(fmt, va); va_end(va); - gb_printf_err("\n"); + gb_printf("\n"); } gb_internal void usage(String argv0) { diff --git a/src/parser.cpp b/src/parser.cpp index 0d9fad8c7..50a9ba766 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1411,7 +1411,7 @@ gb_internal Token expect_operator(AstFile *f) { LIT(p)); } if (f->curr_token.kind == Token_Ellipsis) { - syntax_warning(f->curr_token, "'..' for ranges has now be deprecated, prefer '..='"); + syntax_warning(f->curr_token, "'..' for ranges has now been deprecated, prefer '..='"); f->tokens[f->curr_token_index].flags |= TokenFlag_Replace; } @@ -1434,7 +1434,7 @@ gb_internal Token expect_closing_brace_of_field_list(AstFile *f) { return token; } bool ok = true; - if (!f->allow_newline) { + if (f->allow_newline) { ok = !skip_possible_newline(f); } if (ok && allow_token(f, Token_Semicolon)) { @@ -3191,6 +3191,15 @@ gb_internal Ast *parse_foreign_block(AstFile *f, Token token) { return decl; } +gb_internal void print_comment_group(CommentGroup *group) { + if (group) { + for (Token const &token : group->list) { + gb_printf_err("%.*s\n", LIT(token.string)); + } + gb_printf_err("\n"); + } +} + gb_internal Ast *parse_value_decl(AstFile *f, Array names, CommentGroup *docs) { bool is_mutable = true; @@ -3232,6 +3241,8 @@ gb_internal Ast *parse_value_decl(AstFile *f, Array names, CommentGroup * values.allocator = heap_allocator(); } + CommentGroup *end_comment = f->lead_comment; + if (f->expr_level >= 0) { if (f->curr_token.kind == Token_CloseBrace && f->curr_token.pos.line == f->prev_token.pos.line) { @@ -3252,7 +3263,7 @@ gb_internal Ast *parse_value_decl(AstFile *f, Array names, CommentGroup * } } - return ast_value_decl(f, names, type, values, is_mutable, docs, f->line_comment); + return ast_value_decl(f, names, type, values, is_mutable, docs, end_comment); } gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) { @@ -3682,9 +3693,11 @@ gb_internal bool allow_field_separator(AstFile *f) { if (allow_token(f, Token_Comma)) { return true; } - if (ALLOW_NEWLINE && token.kind == Token_Semicolon && !token_is_newline(token)) { - String p = token_to_string(token); - syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); + if (ALLOW_NEWLINE && token.kind == Token_Semicolon) { + if (!token_is_newline(token)) { + String p = token_to_string(token); + syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p)); + } advance_token(f); return true; } diff --git a/tests/core/build.bat b/tests/core/build.bat index 5a86e5b41..1d146c8a4 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -6,87 +6,82 @@ python3 download_assets.py echo --- echo Running core:image tests echo --- -%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe +%PATH_TO_ODIN% run image %COMMON% -out:test_core_image.exe || exit /b echo --- echo Running core:compress tests echo --- -%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe +%PATH_TO_ODIN% run compress %COMMON% -out:test_core_compress.exe || exit /b echo --- echo Running core:strings tests echo --- -%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe +%PATH_TO_ODIN% run strings %COMMON% -out:test_core_strings.exe || exit /b echo --- echo Running core:hash tests echo --- -%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe +%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_core_hash.exe || exit /b echo --- echo Running core:odin tests echo --- -%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe +%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_core_odin.exe || exit /b echo --- echo Running core:crypto hash tests echo --- -%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe +%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto_hash.exe || exit /b echo --- echo Running core:encoding tests echo --- -%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe -%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe -%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe -%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe +%PATH_TO_ODIN% run encoding/hxa %COMMON% %COLLECTION% -out:test_hxa.exe || exit /b +%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json.exe || exit /b +%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint.exe || exit /b +%PATH_TO_ODIN% run encoding/xml %COMMON% -out:test_xml.exe || exit /b echo --- echo Running core:math/noise tests echo --- -%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe +%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise.exe || exit /b echo --- echo Running core:math tests echo --- -%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe +%PATH_TO_ODIN% run math %COMMON% %COLLECTION% -out:test_core_math.exe || exit /b echo --- echo Running core:math/linalg/glsl tests echo --- -%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe +%PATH_TO_ODIN% run math/linalg/glsl %COMMON% %COLLECTION% -out:test_linalg_glsl.exe || exit /b echo --- echo Running core:path/filepath tests echo --- -%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe +%PATH_TO_ODIN% run path/filepath %COMMON% %COLLECTION% -out:test_core_filepath.exe || exit /b echo --- echo Running core:reflect tests echo --- -%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe +%PATH_TO_ODIN% run reflect %COMMON% %COLLECTION% -out:test_core_reflect.exe || exit /b echo --- echo Running core:text/i18n tests echo --- -%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe +%PATH_TO_ODIN% run text\i18n %COMMON% -out:test_core_i18n.exe || exit /b echo --- echo Running core:net echo --- -%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe - -echo --- -echo Running core:text/lua tests -echo --- -%PATH_TO_ODIN% run text\lua %COMMON% -out:test_core_lua_strlib.exe +%PATH_TO_ODIN% run net %COMMON% -out:test_core_net.exe || exit /b echo --- echo Running core:slice tests echo --- -%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe +%PATH_TO_ODIN% run slice %COMMON% -out:test_core_slice.exe || exit /b echo --- echo Running core:container tests echo --- -%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe +%PATH_TO_ODIN% run container %COMMON% %COLLECTION% -out:test_core_container.exe || exit /b diff --git a/tests/core/compress/test_core_compress.odin b/tests/core/compress/test_core_compress.odin index ee7233e52..ac7555e9a 100644 --- a/tests/core/compress/test_core_compress.odin +++ b/tests/core/compress/test_core_compress.odin @@ -151,6 +151,13 @@ shoco_test :: proc(t: ^testing.T) { } for v in Shoco_Tests { + when ODIN_OS == .Windows { + v := v + // Compressed source files are not encoded with carriage returns but git replaces raw files lf with crlf on commit (on windows only) + // So replace crlf with lf on windows + v.raw, _ = bytes.replace_all(v.raw, { 0xD, 0xA }, { 0xA }) + } + expected_raw := len(v.raw) expected_compressed := len(v.compressed) diff --git a/tests/core/strings/test_core_strings.odin b/tests/core/strings/test_core_strings.odin index e97734dda..00e53647f 100644 --- a/tests/core/strings/test_core_strings.odin +++ b/tests/core/strings/test_core_strings.odin @@ -4,6 +4,7 @@ import "core:strings" import "core:testing" import "core:fmt" import "core:os" +import "core:runtime" TEST_count := 0 TEST_fail := 0 @@ -33,6 +34,7 @@ main :: proc() { test_index_any_small_string_found(&t) test_index_any_larger_string_found(&t) test_cut(&t) + test_case_conversion(&t) fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count) if TEST_fail > 0 { @@ -89,4 +91,48 @@ test_cut :: proc(t: ^testing.T) { test.input, test.offset, test.length, test.output, res) expect(t, res == test.output, msg) } +} + +Case_Kind :: enum { + Lower_Space_Case, + Upper_Space_Case, + Lower_Snake_Case, + Upper_Snake_Case, + Lower_Kebab_Case, + Upper_Kebab_Case, + Camel_Case, + Pascal_Case, + Ada_Case, +} + +test_cases := [Case_Kind]struct{s: string, p: proc(r: string, allocator: runtime.Allocator) -> string}{ + .Lower_Space_Case = {"hellope world", to_lower_space_case}, + .Upper_Space_Case = {"HELLOPE WORLD", to_upper_space_case}, + .Lower_Snake_Case = {"hellope_world", strings.to_snake_case}, + .Upper_Snake_Case = {"HELLOPE_WORLD", strings.to_upper_snake_case}, + .Lower_Kebab_Case = {"hellope-world", strings.to_kebab_case}, + .Upper_Kebab_Case = {"HELLOPE-WORLD", strings.to_upper_kebab_case}, + .Camel_Case = {"hellopeWorld", strings.to_camel_case}, + .Pascal_Case = {"HellopeWorld", strings.to_pascal_case}, + .Ada_Case = {"Hellope_World", strings.to_ada_case}, +} + +to_lower_space_case :: proc(r: string, allocator: runtime.Allocator) -> string { + return strings.to_delimiter_case(r, ' ', false, allocator) +} +to_upper_space_case :: proc(r: string, allocator: runtime.Allocator) -> string { + return strings.to_delimiter_case(r, ' ', true, allocator) +} + +@test +test_case_conversion :: proc(t: ^testing.T) { + for entry in test_cases { + for test_case, case_kind in test_cases { + result := entry.p(test_case.s, context.allocator) + defer delete(result) + + msg := fmt.tprintf("ERROR: Input `{}` to converter {} does not match `{}`, got `{}`.\n", test_case.s, case_kind, entry.s, result) + expect(t, result == entry.s, msg) + } + } } \ No newline at end of file diff --git a/tests/documentation/build.bat b/tests/documentation/build.bat new file mode 100644 index 000000000..6ca90fbad --- /dev/null +++ b/tests/documentation/build.bat @@ -0,0 +1,13 @@ +@echo off +set PATH_TO_ODIN==..\..\odin + +echo --- +echo Building Documentation File +echo --- +%PATH_TO_ODIN% doc ..\..\examples\all -all-packages -doc-format || exit /b + + +echo --- +echo Running Documentation Tester +echo --- +%PATH_TO_ODIN% run documentation_tester.odin -file -vet -strict-style -- %PATH_TO_ODIN% || exit /b diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin new file mode 100644 index 000000000..09c565a51 --- /dev/null +++ b/tests/documentation/documentation_tester.odin @@ -0,0 +1,421 @@ +package documentation_tester + +import "core:os" +import "core:io" +import "core:fmt" +import "core:strings" +import "core:odin/ast" +import "core:odin/parser" +import "core:c/libc" +import doc "core:odin/doc-format" + +Example_Test :: struct { + entity_name: string, + package_name: string, + example_code: []string, + expected_output: []string, +} + +g_header: ^doc.Header +g_bad_doc: bool +g_examples_to_verify: [dynamic]Example_Test +g_path_to_odin: string + +array :: proc(a: $A/doc.Array($T)) -> []T { + return doc.from_array(g_header, a) +} + +str :: proc(s: $A/doc.String) -> string { + return doc.from_string(g_header, s) +} + +common_prefix :: proc(strs: []string) -> string { + if len(strs) == 0 { + return "" + } + n := max(int) + for str in strs { + n = min(n, len(str)) + } + + prefix := strs[0][:n] + for str in strs[1:] { + for len(prefix) != 0 && str[:len(prefix)] != prefix { + prefix = prefix[:len(prefix)-1] + } + if len(prefix) == 0 { + break + } + } + return prefix +} + +errorf :: proc(format: string, args: ..any) -> ! { + fmt.eprintf("%s ", os.args[0]) + fmt.eprintf(format, ..args) + fmt.eprintln() + os.exit(1) +} + +main :: proc() { + if len(os.args) != 2 { + errorf("expected path to odin executable") + } + g_path_to_odin = os.args[1] + data, ok := os.read_entire_file("all.odin-doc") + if !ok { + errorf("unable to read file: all.odin-doc") + } + err: doc.Reader_Error + g_header, err = doc.read_from_bytes(data) + switch err { + case .None: + case .Header_Too_Small: + errorf("file is too small for the file format") + case .Invalid_Magic: + errorf("invalid magic for the file format") + case .Data_Too_Small: + errorf("data is too small for the file format") + case .Invalid_Version: + errorf("invalid file format version") + } + pkgs := array(g_header.pkgs) + entities := array(g_header.entities) + + path_prefix: string + { + fullpaths: [dynamic]string + defer delete(fullpaths) + + for pkg in pkgs[1:] { + append(&fullpaths, str(pkg.fullpath)) + } + path_prefix = common_prefix(fullpaths[:]) + } + + for pkg in pkgs[1:] { + entries_array := array(pkg.entries) + fullpath := str(pkg.fullpath) + path := strings.trim_prefix(fullpath, path_prefix) + if ! strings.has_prefix(path, "core/") { + continue + } + trimmed_path := strings.trim_prefix(path, "core/") + if strings.has_prefix(trimmed_path, "sys") { + continue + } + if strings.contains(trimmed_path, "/_") { + continue + } + for entry in entries_array { + entity := entities[entry.entity] + find_and_add_examples( + docs = str(entity.docs), + package_name = str(pkg.name), + entity_name = str(entity.name), + ) + } + } + write_test_suite(g_examples_to_verify[:]) + if g_bad_doc { + errorf("We created bad documentation!") + } + + if ! run_test_suite() { + errorf("Test suite failed!") + } + fmt.println("Examples verified") +} + +// NOTE: this is a pretty close copy paste from the website pkg documentation on parsing the docs +find_and_add_examples :: proc(docs: string, package_name: string, entity_name: string) { + if docs == "" { + return + } + Block_Kind :: enum { + Other, + Example, + Output, + } + Block :: struct { + kind: Block_Kind, + lines: []string, + } + lines := strings.split_lines(docs) + curr_block_kind := Block_Kind.Other + start := 0 + + example_block: Block // when set the kind should be Example + output_block: Block // when set the kind should be Output + // rely on zii that the kinds have not been set + assert(example_block.kind != .Example) + assert(output_block.kind != .Output) + + insert_block :: proc(block: Block, example: ^Block, output: ^Block, name: string) { + switch block.kind { + case .Other: + case .Example: + if example.kind == .Example { + fmt.eprintf("The documentation for %q has multiple examples which is not allowed\n", name) + g_bad_doc = true + } + example^ = block + case .Output: output^ = block + if example.kind == .Output { + fmt.eprintf("The documentation for %q has multiple output which is not allowed\n", name) + g_bad_doc = true + } + output^ = block + } + } + + for line, i in lines { + text := strings.trim_space(line) + next_block_kind := curr_block_kind + + switch curr_block_kind { + case .Other: + switch { + case strings.has_prefix(line, "Example:"): next_block_kind = .Example + case strings.has_prefix(line, "Output:"): next_block_kind = .Output + } + case .Example: + switch { + case strings.has_prefix(line, "Output:"): next_block_kind = .Output + case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other + } + case .Output: + switch { + case strings.has_prefix(line, "Example:"): next_block_kind = .Example + case ! (text == "" || strings.has_prefix(line, "\t")): next_block_kind = .Other + } + } + + if i-start > 0 && (curr_block_kind != next_block_kind) { + insert_block(Block{curr_block_kind, lines[start:i]}, &example_block, &output_block, entity_name) + curr_block_kind, start = next_block_kind, i + } + } + + if start < len(lines) { + insert_block(Block{curr_block_kind, lines[start:]}, &example_block, &output_block, entity_name) + } + + if output_block.kind == .Output && example_block.kind != .Example { + fmt.eprintf("The documentation for %q has an output block but no example\n", entity_name) + g_bad_doc = true + } + + // Write example and output block if they're both present + if example_block.kind == .Example && output_block.kind == .Output { + { + // Example block starts with + // `Example:` and a number of white spaces, + lines := &example_block.lines + for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Example:")) { + lines^ = lines[1:] + } + } + { + // Output block starts with + // `Output:` and a number of white spaces, + lines := &output_block.lines + for len(lines) > 0 && (strings.trim_space(lines[0]) == "" || strings.has_prefix(lines[0], "Output:")) { + lines^ = lines[1:] + } + // Additionally we need to strip all empty lines at the end of output to not include those in the expected output + for len(lines) > 0 && (strings.trim_space(lines[len(lines) - 1]) == "") { + lines^ = lines[:len(lines) - 1] + } + } + // Remove first layer of tabs which are always present + for line in &example_block.lines { + line = strings.trim_prefix(line, "\t") + } + for line in &output_block.lines { + line = strings.trim_prefix(line, "\t") + } + append(&g_examples_to_verify, Example_Test { + entity_name = entity_name, + package_name = package_name, + example_code = example_block.lines, + expected_output = output_block.lines, + }) + } +} + + +write_test_suite :: proc(example_tests: []Example_Test) { + TEST_SUITE_DIRECTORY :: "verify" + os.remove_directory(TEST_SUITE_DIRECTORY) + os.make_directory(TEST_SUITE_DIRECTORY) + + example_build := strings.builder_make() + test_runner := strings.builder_make() + + strings.write_string(&test_runner, +`//+private +package documentation_verification + +import "core:os" +import "core:mem" +import "core:io" +import "core:fmt" +import "core:thread" +import "core:sync" +import "core:intrinsics" + +@(private="file") +_read_pipe: os.Handle +@(private="file") +_write_pipe: os.Handle +@(private="file") +_pipe_reader_semaphore: sync.Sema +@(private="file") +_out_data: string +@(private="file") +_out_buffer: [mem.Megabyte]byte +@(private="file") +_bad_test_found: bool + +@(private="file") +_spawn_pipe_reader :: proc() { + thread.create_and_start(proc(^thread.Thread) { + stream := os.stream_from_handle(_read_pipe) + reader := io.to_reader(stream) + sync.post(&_pipe_reader_semaphore) // notify thread is ready + for { + n_read := 0 + read_to_null_byte := 0 + finished_reading := false + for ! finished_reading { + just_read, err := io.read(reader, _out_buffer[n_read:], &n_read); if err != .None { + panic("We got an IO error!") + } + for b in _out_buffer[n_read - just_read: n_read] { + if b == 0 { + finished_reading = true + break + } + read_to_null_byte += 1 + } + } + intrinsics.volatile_store(&_out_data, transmute(string)_out_buffer[:read_to_null_byte]) + sync.post(&_pipe_reader_semaphore) // notify we read the null byte + } + }) + + sync.wait(&_pipe_reader_semaphore) // wait for thread to be ready +} + +@(private="file") +_check :: proc(test_name: string, expected: string) { + null_byte: [1]byte + os.write(_write_pipe, null_byte[:]) + os.flush(_write_pipe) + sync.wait(&_pipe_reader_semaphore) + output := intrinsics.volatile_load(&_out_data) // wait for thread to read null byte + if expected != output { + fmt.eprintf("Test %q got unexpected output:\n%q\n", test_name, output) + fmt.eprintf("Expected:\n%q\n", expected) + _bad_test_found = true + } +} + +main :: proc() { + _read_pipe, _write_pipe, _ = os.pipe() + os.stdout = _write_pipe + _spawn_pipe_reader() +`) + for test in example_tests { + strings.builder_reset(&example_build) + strings.write_string(&example_build, "package documentation_verification\n\n") + for line in test.example_code { + strings.write_string(&example_build, line) + strings.write_byte(&example_build, '\n') + } + + code_string := strings.to_string(example_build) + + example_ast := ast.File { src = code_string } + odin_parser := parser.default_parser() + + if ! parser.parse_file(&odin_parser, &example_ast) { + g_bad_doc = true + continue + } + if odin_parser.error_count > 0 { + fmt.eprintf("Errors on the following code generated for %q:\n%v\n", test.entity_name, code_string) + g_bad_doc = true + continue + } + + enforced_name := fmt.tprintf("%v_example", test.entity_name) + index_of_proc_name: int + code_test_name: string + + for d in example_ast.decls { + value_decl, is_value := d.derived.(^ast.Value_Decl); if ! is_value { + continue + } + if len(value_decl.values) != 1 { + continue + } + proc_lit, is_proc_lit := value_decl.values[0].derived_expr.(^ast.Proc_Lit); if ! is_proc_lit { + continue + } + if len(proc_lit.type.params.list) > 0 { + continue + } + this_procedure_name := code_string[value_decl.names[0].pos.offset:value_decl.names[0].end.offset] + if this_procedure_name != enforced_name { + continue + } + index_of_proc_name = value_decl.names[0].pos.offset + code_test_name = this_procedure_name + break + } + + if code_test_name == "" { + fmt.eprintf("We could not any find procedure literals with no arguments with the identifier %q for the example for %q\n", enforced_name, test.entity_name) + g_bad_doc = true + continue + } + + fmt.sbprintf(&test_runner, "\t%v_%v()\n", test.package_name, code_test_name) + fmt.sbprintf(&test_runner, "\t_check(%q, `", code_test_name) + for line in test.expected_output { + strings.write_string(&test_runner, line) + strings.write_string(&test_runner, "\n") + } + strings.write_string(&test_runner, "`)\n") + save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name) + + test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 { + fmt.eprintf("We could not open the file to the path %q for writing\n", save_path) + g_bad_doc = true + continue + } + defer os.close(test_file_handle) + stream := os.stream_from_handle(test_file_handle) + writer, ok := io.to_writer(stream); if ! ok { + fmt.eprintf("We could not make the writer for the path %q\n", save_path) + g_bad_doc = true + continue + } + fmt.wprintf(writer, "%v%v_%v", code_string[:index_of_proc_name], test.package_name, code_string[index_of_proc_name:]) + } + + strings.write_string(&test_runner, +` + if _bad_test_found { + fmt.eprintln("One or more tests failed") + os.exit(1) + } +}`) + os.write_entire_file("verify/main.odin", transmute([]byte)strings.to_string(test_runner)) +} + +run_test_suite :: proc() -> bool { + return libc.system(fmt.caprintf("%v run verify", g_path_to_odin)) == 0 +} \ No newline at end of file diff --git a/tests/internal/build.bat b/tests/internal/build.bat index 313e1dbb5..995086523 100644 --- a/tests/internal/build.bat +++ b/tests/internal/build.bat @@ -1,4 +1,4 @@ @echo off set PATH_TO_ODIN==..\..\odin -%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal +%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b rem -define:SEED=42 \ No newline at end of file diff --git a/tests/issues/run.bat b/tests/issues/run.bat index c526fd472..ea5feddaa 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -5,19 +5,14 @@ pushd build set COMMON=-collection:tests=..\.. -set ERROR_DID_OCCUR=0 - @echo on -..\..\..\odin test ..\test_issue_829.odin %COMMON% -file -..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file -..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file -..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug +..\..\..\odin test ..\test_issue_829.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_1592.odin %COMMON% -file || exit /b +..\..\..\odin test ..\test_issue_2087.odin %COMMON% -file || exit /b +..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b @echo off -if %ERRORLEVEL% NEQ 0 set ERROR_DID_OCCUR=1 - popd rmdir /S /Q build -if %ERROR_DID_OCCUR% NEQ 0 EXIT /B 1 diff --git a/tests/vendor/build.bat b/tests/vendor/build.bat index d92a5eaea..09754fc40 100644 --- a/tests/vendor/build.bat +++ b/tests/vendor/build.bat @@ -5,9 +5,9 @@ set PATH_TO_ODIN==..\..\odin echo --- echo Running vendor:botan tests echo --- -%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe +%PATH_TO_ODIN% run botan %COMMON% -out:vendor_botan.exe || exit /b echo --- echo Running vendor:glfw tests echo --- -%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe \ No newline at end of file +%PATH_TO_ODIN% run glfw %COMMON% -out:vendor_glfw.exe || exit /b \ No newline at end of file diff --git a/vendor/ENet/enet.odin b/vendor/ENet/enet.odin index 37e65b497..0e4493a17 100644 --- a/vendor/ENet/enet.odin +++ b/vendor/ENet/enet.odin @@ -343,58 +343,58 @@ foreign ENet { deinitialize :: proc() --- linked_version :: proc() -> Version --- time_get :: proc() -> u32 --- - time_set :: proc(u32) --- + time_set :: proc(newTimeBase: u32) --- socket_create :: proc(SocketType) -> Socket --- - socket_bind :: proc(Socket, ^Address) -> i32 --- - socket_get_address :: proc(Socket, ^Address) -> i32 --- - socket_listen :: proc(Socket, i32) -> i32 --- - socket_accept :: proc(Socket, ^Address) -> Socket --- - socket_connect :: proc(Socket, ^Address) -> i32 --- - socket_send :: proc(Socket, ^Address, ^Buffer, uint) -> i32 --- - socket_receive :: proc(Socket, ^Address, ^Buffer, uint) -> i32 --- - socket_wait :: proc(Socket, ^u32, u32) -> i32 --- - socket_set_option :: proc(Socket, SocketOption, i32) -> i32 --- - socket_get_option :: proc(Socket, SocketOption, ^i32) -> i32 --- - socket_shutdown :: proc(Socket, SocketShutdown) -> i32 --- - socket_destroy :: proc(Socket) --- - socketset_select :: proc(Socket, ^SocketSet, ^SocketSet, u32) -> i32 --- + socket_bind :: proc(socket: Socket, address: ^Address) -> i32 --- + socket_get_address :: proc(socket: Socket, address: ^Address) -> i32 --- + socket_listen :: proc(socket: Socket, backlog: i32) -> i32 --- + socket_accept :: proc(socket: Socket, address: ^Address) -> Socket --- + socket_connect :: proc(socket: Socket, address: ^Address) -> i32 --- + socket_send :: proc(socket: Socket, address: ^Address, buffers: [^]Buffer, bufferCount: uint) -> i32 --- + socket_receive :: proc(socket: Socket, address: ^Address, buffers: [^]Buffer, bufferCount: uint) -> i32 --- + socket_wait :: proc(socket: Socket, condition: ^u32, timeout: u32) -> i32 --- + socket_set_option :: proc(socket: Socket, option: SocketOption, value: i32) -> i32 --- + socket_get_option :: proc(socket: Socket, option: SocketOption, value: ^i32) -> i32 --- + socket_shutdown :: proc(socket: Socket, how: SocketShutdown) -> i32 --- + socket_destroy :: proc(socket: Socket) --- + socketset_select :: proc(socket: Socket, readSet: ^SocketSet, writeSet: ^SocketSet, timeout: u32) -> i32 --- address_set_host_ip :: proc(address: ^Address, hostName: cstring) -> i32 --- address_set_host :: proc(address: ^Address, hostName: cstring) -> i32 --- address_get_host_ip :: proc(address: ^Address, hostName: [^]u8, nameLength: uint) -> i32 --- address_get_host :: proc(address: ^Address, hostName: [^]u8, nameLength: uint) -> i32 --- - packet_create :: proc(rawptr, uint, u32) -> ^Packet --- - packet_destroy :: proc(^Packet) --- - packet_resize :: proc(^Packet, uint) -> i32 --- - crc32 :: proc(^Buffer, uint) -> u32 --- + packet_create :: proc(data: rawptr, dataLength: uint, flags: PacketFlag) -> ^Packet --- + packet_destroy :: proc(packet: ^Packet) --- + packet_resize :: proc(packet: ^Packet, dataLength: uint) -> i32 --- + crc32 :: proc(buffers: [^]Buffer, bufferCount: uint) -> u32 --- - host_create :: proc(^Address, uint, uint, u32, u32) -> ^Host --- - host_destroy :: proc(^Host) --- - host_connect :: proc(^Host, ^Address, uint, u32) -> ^Peer --- - host_check_events :: proc(^Host, ^Event) -> i32 --- - host_service :: proc(^Host, ^Event, u32) -> i32 --- - host_flush :: proc(^Host) --- - host_broadcast :: proc(^Host, u8, ^Packet) --- - host_compress :: proc(^Host, ^Compressor) --- - host_compress_with_range_coder :: proc(^Host) -> i32 --- - host_channel_limit :: proc(^Host, uint) --- - host_bandwidth_limit :: proc(^Host, u32, u32) --- + host_create :: proc(address: ^Address, peerCount: uint, channelLimit: uint, incomingBandwidth: u32, outgoingBandwidth: u32) -> ^Host --- + host_destroy :: proc(host: ^Host) --- + host_connect :: proc(host: ^Host, address: ^Address, channelCount: uint, data: u32) -> ^Peer --- + host_check_events :: proc(host: ^Host, event: ^Event) -> i32 --- + host_service :: proc(host: ^Host, event: ^Event, timeout: u32) -> i32 --- + host_flush :: proc(host: ^Host) --- + host_broadcast :: proc(host: ^Host, channelID: u8, packet: ^Packet) --- + host_compress :: proc(host: ^Host, compressor: ^Compressor) --- + host_compress_with_range_coder :: proc(host: ^Host) -> i32 --- + host_channel_limit :: proc(host: ^Host, channelLimit: uint) --- + host_bandwidth_limit :: proc(host: ^Host, incomingBandwidth: u32, outgoingBandwidth: u32) --- - peer_send :: proc(^Peer, u8, ^Packet) -> i32 --- - peer_receive :: proc(^Peer, ^u8) -> ^Packet --- - peer_ping :: proc(^Peer) --- - peer_ping_interval :: proc(^Peer, u32) --- - peer_timeout :: proc(^Peer, u32, u32, u32) --- - peer_reset :: proc(^Peer) --- - peer_disconnect :: proc(^Peer, u32) --- - peer_disconnect_now :: proc(^Peer, u32) --- - peer_disconnect_later :: proc(^Peer, u32) --- - peer_throttle_configure :: proc(^Peer, u32, u32, u32) --- + peer_send :: proc(peer: ^Peer, channelID: u8, packet: ^Packet) -> i32 --- + peer_receive :: proc(peer: ^Peer, channelID: ^u8) -> ^Packet --- + peer_ping :: proc(peer: ^Peer) --- + peer_ping_interval :: proc(peer: ^Peer, pingInterval: u32) --- + peer_timeout :: proc(peer: ^Peer, timoutLimit: u32, timeoutMinimum: u32, timeoutMaximum: u32) --- + peer_reset :: proc(peer: ^Peer) --- + peer_disconnect :: proc(peer: ^Peer, data: u32) --- + peer_disconnect_now :: proc(peer: ^Peer, data: u32) --- + peer_disconnect_later :: proc(peer: ^Peer, data: u32) --- + peer_throttle_configure :: proc(peer: ^Peer, interval: u32, acceleration: u32, deceleration: u32) --- range_coder_create :: proc() -> rawptr --- - range_coder_destroy :: proc(rawptr) --- - range_coder_compress :: proc(rawptr, [^]Buffer, uint, uint, [^]u8, uint) -> uint --- - range_coder_decompress :: proc(rawptr, [^]u8, uint, [^]u8, uint) -> uint --- + range_coder_destroy :: proc(ctx: rawptr) --- + range_coder_compress :: proc(ctx: rawptr, inBuffers: [^]Buffer, inBufferCount: uint, inLimit: uint, outData: [^]u8, outLimit: uint) -> uint --- + range_coder_decompress :: proc(ctx: rawptr, inData: [^]u8, inLimit: uint, outData: [^]u8, outLimit: uint) -> uint --- } \ No newline at end of file diff --git a/vendor/raylib/LICENSE b/vendor/raylib/LICENSE index d7d866436..91da62ed9 100644 --- a/vendor/raylib/LICENSE +++ b/vendor/raylib/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) +Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) This software is provided "as-is", without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. diff --git a/vendor/raylib/README.md b/vendor/raylib/README.md index cbfdf22f4..11b4bda48 100644 --- a/vendor/raylib/README.md +++ b/vendor/raylib/README.md @@ -12,15 +12,17 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
-[![GitHub contributors](https://img.shields.io/github/contributors/raysan5/raylib)](https://github.com/raysan5/raylib/graphs/contributors) -[![GitHub All Releases](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) -[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.0.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) +[![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) +[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) +[![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) -[![Chat on Discord](https://img.shields.io/discord/426912293134270465.svg?logo=discord)](https://discord.gg/raylib) -[![GitHub stars](https://img.shields.io/github/stars/raysan5/raylib?style=social)](https://github.com/raysan5/raylib/stargazers) -[![Twitter Follow](https://img.shields.io/twitter/follow/raysan5?style=social)](https://twitter.com/raysan5) -[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?style=social)](https://www.reddit.com/r/raylib/) +[![Discord Members](https://img.shields.io/discord/426912293134270465.svg?label=Discord&logo=discord)](https://discord.gg/raylib) +[![Subreddit Subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?label=reddit%20r%2Fraylib&logo=reddit)](https://www.reddit.com/r/raylib/) +[![Youtube Subscribers](https://img.shields.io/youtube/channel/subscribers/UC8WIBkhYb5sBNqXO1mZ7WSQ?style=flat&label=Youtube&logo=youtube)](https://www.youtube.com/c/raylib) +[![Twitch Status](https://img.shields.io/twitch/status/raysan5?style=flat&label=Twitch&logo=twitch)](https://www.twitch.tv/raysan5) [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) @@ -36,10 +38,10 @@ features -------- - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** - - Written in plain C code (C99) in PascalCase/camelCase notation + - Written in plain C code (C99) using PascalCase/camelCase notation - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - - Multiple **Fonts** formats supported (TTF, XNA fonts, AngelCode fonts) + - Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** @@ -49,7 +51,7 @@ features - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)! - - Bindings to [+50 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! + - Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! - **Free and open source**. basic example @@ -61,25 +63,57 @@ package example import rl "vendor:raylib" main :: proc() { - rl.InitWindow(800, 450, "raylib [core] example - basic window") + rl.InitWindow(800, 450, "raylib [core] example - basic window") - for !rl.WindowShouldClose() { - rl.BeginDrawing() - rl.ClearBackground(rl.RAYWHITE) - rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY) - rl.EndDrawing() - } + for !rl.WindowShouldClose() { + rl.BeginDrawing() + rl.ClearBackground(rl.RAYWHITE) + rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY) + rl.EndDrawing() + } - rl.CloseWindow() + rl.CloseWindow() } ``` + +build and installation +---------------------- + +raylib binary releases for Windows, Linux, macOS, Android and HTML5 are available at the [Github Releases page](https://github.com/raysan5/raylib/releases). + +raylib is also available via multiple [package managers](https://github.com/raysan5/raylib/issues/613) on multiple OS distributions. + +#### Installing and building raylib on multiple platforms + +[raylib Wiki](https://github.com/raysan5/raylib/wiki#development-platforms) contains detailed instructions on building and usage on multiple platforms. + + - [Working on Windows](https://github.com/raysan5/raylib/wiki/Working-on-Windows) + - [Working on macOS](https://github.com/raysan5/raylib/wiki/Working-on-macOS) + - [Working on GNU Linux](https://github.com/raysan5/raylib/wiki/Working-on-GNU-Linux) + - [Working on Chrome OS](https://github.com/raysan5/raylib/wiki/Working-on-Chrome-OS) + - [Working on FreeBSD](https://github.com/raysan5/raylib/wiki/Working-on-FreeBSD) + - [Working on Raspberry Pi](https://github.com/raysan5/raylib/wiki/Working-on-Raspberry-Pi) + - [Working for Android](https://github.com/raysan5/raylib/wiki/Working-for-Android) + - [Working for Web (HTML5)](https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5)) + - [Working anywhere with CMake](https://github.com/raysan5/raylib/wiki/Working-with-CMake) + +*Note that the Wiki is open for edit, if you find some issues while building raylib for your target platform, feel free to edit the Wiki or open an issue related to it.* + +#### Setup raylib with multiple IDEs + +raylib has been developed on Windows platform using [Notepad++](https://notepad-plus-plus.org/) and [MinGW GCC](https://www.mingw-w64.org/) compiler but it can be used with other IDEs on multiple platforms. + +[Projects directory](https://github.com/raysan5/raylib/tree/master/projects) contains several ready-to-use **project templates** to build raylib and code examples with multiple IDEs. + +*Note that there are lots of IDEs supported, some of the provided templates could require some review, so please, if you find some issue with a template or you think they could be improved, feel free to send a PR or open a related issue.* + learning and docs ------------------ -raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library and a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. +raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. -Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here the more relevant links: +Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here are the relevant links: - [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html) - [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture) @@ -106,4 +140,4 @@ license raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details. -raylib uses internally some libraries for window/graphics/inputs management and also to support different fileformats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details. \ No newline at end of file +raylib uses internally some libraries for window/graphics/inputs management and also to support different file formats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details. diff --git a/vendor/raylib/linux/libraylib.a b/vendor/raylib/linux/libraylib.a index 1c97be57b..7de8b292c 100644 Binary files a/vendor/raylib/linux/libraylib.a and b/vendor/raylib/linux/libraylib.a differ diff --git a/vendor/raylib/linux/libraylib.so b/vendor/raylib/linux/libraylib.so index 134f573c8..80d5c6ae7 100644 Binary files a/vendor/raylib/linux/libraylib.so and b/vendor/raylib/linux/libraylib.so differ diff --git a/vendor/raylib/linux/libraylib.so.4.0.0 b/vendor/raylib/linux/libraylib.so.4.0.0 deleted file mode 100644 index 134f573c8..000000000 Binary files a/vendor/raylib/linux/libraylib.so.4.0.0 and /dev/null differ diff --git a/vendor/raylib/linux/libraylib.so.4.5.0 b/vendor/raylib/linux/libraylib.so.4.5.0 new file mode 100644 index 000000000..80d5c6ae7 Binary files /dev/null and b/vendor/raylib/linux/libraylib.so.4.5.0 differ diff --git a/vendor/raylib/linux/libraylib.so.400 b/vendor/raylib/linux/libraylib.so.400 deleted file mode 100644 index 134f573c8..000000000 Binary files a/vendor/raylib/linux/libraylib.so.400 and /dev/null differ diff --git a/vendor/raylib/linux/libraylib.so.450 b/vendor/raylib/linux/libraylib.so.450 new file mode 100644 index 000000000..80d5c6ae7 Binary files /dev/null and b/vendor/raylib/linux/libraylib.so.450 differ diff --git a/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib b/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib deleted file mode 100644 index a40219baa..000000000 Binary files a/vendor/raylib/macos-arm64/libraylib.4.0.0.dylib and /dev/null differ diff --git a/vendor/raylib/macos-arm64/libraylib.4.5.0.dylib b/vendor/raylib/macos-arm64/libraylib.4.5.0.dylib new file mode 100644 index 000000000..3a3c76982 Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.4.5.0.dylib differ diff --git a/vendor/raylib/macos-arm64/libraylib.400.dylib b/vendor/raylib/macos-arm64/libraylib.400.dylib deleted file mode 100644 index a40219baa..000000000 Binary files a/vendor/raylib/macos-arm64/libraylib.400.dylib and /dev/null differ diff --git a/vendor/raylib/macos-arm64/libraylib.450.dylib b/vendor/raylib/macos-arm64/libraylib.450.dylib new file mode 100644 index 000000000..3a3c76982 Binary files /dev/null and b/vendor/raylib/macos-arm64/libraylib.450.dylib differ diff --git a/vendor/raylib/macos-arm64/libraylib.a b/vendor/raylib/macos-arm64/libraylib.a index 5eddcb8fa..4814819b9 100644 Binary files a/vendor/raylib/macos-arm64/libraylib.a and b/vendor/raylib/macos-arm64/libraylib.a differ diff --git a/vendor/raylib/macos-arm64/libraylib.dylib b/vendor/raylib/macos-arm64/libraylib.dylib index a40219baa..3a3c76982 100644 Binary files a/vendor/raylib/macos-arm64/libraylib.dylib and b/vendor/raylib/macos-arm64/libraylib.dylib differ diff --git a/vendor/raylib/macos/libraylib.4.0.0.dylib b/vendor/raylib/macos/libraylib.4.0.0.dylib deleted file mode 100644 index 42da76800..000000000 Binary files a/vendor/raylib/macos/libraylib.4.0.0.dylib and /dev/null differ diff --git a/vendor/raylib/macos/libraylib.4.5.0.dylib b/vendor/raylib/macos/libraylib.4.5.0.dylib new file mode 100644 index 000000000..3a3c76982 Binary files /dev/null and b/vendor/raylib/macos/libraylib.4.5.0.dylib differ diff --git a/vendor/raylib/macos/libraylib.400.dylib b/vendor/raylib/macos/libraylib.400.dylib deleted file mode 100644 index 42da76800..000000000 Binary files a/vendor/raylib/macos/libraylib.400.dylib and /dev/null differ diff --git a/vendor/raylib/macos/libraylib.450.dylib b/vendor/raylib/macos/libraylib.450.dylib new file mode 100644 index 000000000..3a3c76982 Binary files /dev/null and b/vendor/raylib/macos/libraylib.450.dylib differ diff --git a/vendor/raylib/macos/libraylib.a b/vendor/raylib/macos/libraylib.a index 2ef3e490f..4814819b9 100644 Binary files a/vendor/raylib/macos/libraylib.a and b/vendor/raylib/macos/libraylib.a differ diff --git a/vendor/raylib/macos/libraylib.dylib b/vendor/raylib/macos/libraylib.dylib index 42da76800..3a3c76982 100644 Binary files a/vendor/raylib/macos/libraylib.dylib and b/vendor/raylib/macos/libraylib.dylib differ diff --git a/vendor/raylib/raylib.lib b/vendor/raylib/raylib.lib deleted file mode 100644 index a3c99a25e..000000000 Binary files a/vendor/raylib/raylib.lib and /dev/null differ diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 97e800392..2bedf77c4 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -1,79 +1,84 @@ -/********************************************************************************************** -* -* raylib v4.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) -* -* FEATURES: -* - NO external dependencies, all required libraries included with raylib -* - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, -* MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. -* - Written in plain C code (C99) in PascalCase/camelCase notation -* - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) -* - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] -* - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) -* - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) -* - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! -* - Flexible Materials system, supporting classic maps and PBR maps -* - Animated 3D models supported (skeletal bones animation) (IQM) -* - Shaders support, including Model shaders and Postprocessing shaders -* - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] -* - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) -* - VR stereo rendering with configurable HMD device parameters -* - Bindings to multiple programming languages available! -* -* NOTES: -* - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] -* - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) -* - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) -* - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) -* -* DEPENDENCIES (included): -* [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) -* [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) -* [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management -* -* OPTIONAL DEPENDENCIES (included): -* [rcore] msf_gif (Miles Fogle) for GIF recording -* [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorythm -* [rcore] sdefl (Micha Mettke) for DEFLATE compression algorythm -* [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) -* [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) -* [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms -* [rtext] stb_truetype (Sean Barret) for ttf fonts loading -* [rtext] stb_rect_pack (Sean Barret) for rectangles packing -* [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation -* [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) -* [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) -* [raudio] dr_wav (David Reid) for WAV audio file loading -* [raudio] dr_flac (David Reid) for FLAC audio file loading -* [raudio] dr_mp3 (David Reid) for MP3 audio file loading -* [raudio] stb_vorbis (Sean Barret) for OGG audio loading -* [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading -* [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading -* -* -* LICENSE: zlib/libpng -* -* raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, -* BSD-like license that allows static linking with closed source software: -* -* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5) -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ +/* +Package vendor:raylib implements bindings for version 4.5 of the raylib library (https://www.raylib.com/) + + ********************************************************************************************* + * + * raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) + * + * FEATURES: + * - NO external dependencies, all required libraries included with raylib + * - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly, + * MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5. + * - Written in plain C code (C99) in PascalCase/camelCase notation + * - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile) + * - Unique OpenGL abstraction layer (usable as standalone module): [rlgl] + * - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts) + * - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC) + * - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more! + * - Flexible Materials system, supporting classic maps and PBR maps + * - Animated 3D models supported (skeletal bones animation) (IQM) + * - Shaders support, including Model shaders and Postprocessing shaders + * - Powerful math module for Vector, Matrix and Quaternion operations: [raymath] + * - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) + * - VR stereo rendering with configurable HMD device parameters + * - Bindings to multiple programming languages available! + * + * NOTES: + * - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text] + * - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2) + * - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2) + * - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2) + * + * DEPENDENCIES (included): + * [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP) + * [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP) + * [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management + * + * OPTIONAL DEPENDENCIES (included): + * [rcore] msf_gif (Miles Fogle) for GIF recording + * [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorithm + * [rcore] sdefl (Micha Mettke) for DEFLATE compression algorithm + * [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...) + * [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG) + * [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms + * [rtext] stb_truetype (Sean Barret) for ttf fonts loading + * [rtext] stb_rect_pack (Sean Barret) for rectangles packing + * [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation + * [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL) + * [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF) + * [rmodels] Model3D (bzt) for models loading (M3D, https://bztsrc.gitlab.io/model3d) + * [raudio] dr_wav (David Reid) for WAV audio file loading + * [raudio] dr_flac (David Reid) for FLAC audio file loading + * [raudio] dr_mp3 (David Reid) for MP3 audio file loading + * [raudio] stb_vorbis (Sean Barret) for OGG audio loading + * [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading + * [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading + * + * + * LICENSE: zlib/libpng + * + * raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, + * BSD-like license that allows static linking with closed source software: + * + * Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) + * + * This software is provided "as-is", without any express or implied warranty. In no event + * will the authors be held liable for any damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, including commercial + * applications, and to alter it and redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not claim that you + * wrote the original software. If you use this software in a product, an acknowledgment + * in the product documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be misrepresented + * as being the original software. + * + * 3. This notice may not be removed or altered from any source distribution. + * + ********************************************************************************************* +*/ package raylib import c "core:c/libc" @@ -92,8 +97,9 @@ MAX_TEXT_BUFFER_LENGTH :: #config(RAYLIB_MAX_TEXT_BUFFER_LENGTH, 1024) #assert(size_of(rune) == size_of(c.int)) when ODIN_OS == .Windows { + @(extra_linker_flags="/NODEFAULTLIB:libcmt") foreign import lib { - "raylib.lib", + "windows/raylib.lib", "system:Winmm.lib", "system:Gdi32.lib", "system:User32.lib", @@ -125,7 +131,7 @@ when ODIN_OS == .Windows { foreign import lib "system:raylib" } -VERSION :: "4.0" +VERSION :: "4.5" PI :: 3.14159265358979323846 DEG2RAD :: PI/180.0 @@ -277,8 +283,6 @@ Font :: struct { chars: [^]GlyphInfo, // Characters info data } -SpriteFont :: Font // SpriteFont type fallback, defaults to Font - // Camera type, defines a camera position/orientation in 3d space Camera3D :: struct { position: Vector3, // Camera position @@ -316,7 +320,7 @@ Mesh :: struct { // Animation vertex data animVertices: [^]f32, // Animated vertex positions (after bones transformations) animNormals: [^]f32, // Animated normals (after bones transformations) - boneIds: [^]c.int, // Vertex bone ids, up to 4 bones influence by vertex (skinning) + boneIds: [^]u8, // Vertex bone ids, up to 4 bones influence by vertex (skinning) boneWeights: [^]f32, // Vertex bone weight, up to 4 bones influence by vertex (skinning) // OpenGL identifiers @@ -327,7 +331,7 @@ Mesh :: struct { // Shader type (generic) Shader :: struct { id: c.uint, // Shader program id - locs: [^]i32, // Shader locations array (MAX_SHADER_LOCATIONS) + locs: [^]c.int, // Shader locations array (MAX_SHADER_LOCATIONS) } // Material texture map @@ -353,7 +357,7 @@ Transform :: struct { // Bone information BoneInfo :: struct { - name: [32]byte, // Bone name + name: [32]byte `fmt:"s,0"`, // Bone name parent: c.int, // Bone parent } @@ -378,7 +382,7 @@ ModelAnimation :: struct { boneCount: c.int, // Number of bones frameCount: c.int, // Number of animation frames bones: [^]BoneInfo, // Bones information (skeleton) - framePoses: [^]^Transform, // Poses array by frame + framePoses: [^][^]Transform, // Poses array by frame } // Ray type (useful for raycast) @@ -403,7 +407,7 @@ BoundingBox :: struct { // Wave type, defines audio wave data Wave :: struct { - sampleCount: c.uint, // Total number of samples + frameCount: c.uint, // Total number of frames (considering channels) sampleRate: c.uint, // Frequency (samples per second) sampleSize: c.uint, // Bit depth (bits per sample): 8, 16, 32 (24 not supported) channels: c.uint, // Number of channels (1-mono, 2-stereo) @@ -411,9 +415,10 @@ Wave :: struct { } // Audio stream type -// NOTE: Useful to create custom audio streams not bound to a specific file +// NOTE: Actual structs are defined internally in raudio module AudioStream :: struct { buffer: rawptr, // Pointer to internal data used by the audio system + processor: rawptr, // Pointer to internal data processor, useful for audio effects sampleRate: c.uint, // Frequency (samples per second) sampleSize: c.uint, // Bit depth (bits per sample): 8, 16, 32 (24 not supported) @@ -423,14 +428,14 @@ AudioStream :: struct { // Sound source type Sound :: struct { using stream: AudioStream, // Audio stream - sampleCount: c.uint, // Total number of samples + frameCount: c.uint, // Total number of frames (considering channels) } // Music stream type (audio file streaming from memory) // NOTE: Anything longer than ~10 seconds should be streamed Music :: struct { using stream: AudioStream, // Audio stream - sampleCount: c.uint, // Total number of samples + frameCount: c.uint, // Total number of frames (considering channels) looping: bool, // Music looping enable ctxType: c.int, // Type of music context (audio filetype) @@ -463,6 +468,13 @@ VrStereoConfig :: struct { scaleIn: [2]f32, // VR distortion scale in } +// File path list +FilePathList :: struct { + capacity: c.uint, // Filepaths max entries + count: c.uint, // Filepaths entries count + paths: [^]cstring, // Filepaths entries +} + //---------------------------------------------------------------------------------- // Enumerators Definition @@ -471,403 +483,380 @@ VrStereoConfig :: struct { // NOTE: Every bit registers one state (use it with bit masks) // By default all flags are set to 0 ConfigFlag :: enum c.int { - VSYNC_HINT = 6, // Set to try enabling V-Sync on GPU - FULLSCREEN_MODE = 1, // Set to run program in fullscreen - WINDOW_RESIZABLE = 2, // Set to allow resizable window - WINDOW_UNDECORATED = 3, // Set to disable window decoration (frame and buttons) - WINDOW_HIDDEN = 7, // Set to hide window - WINDOW_MINIMIZED = 9, // Set to minimize window (iconify) - WINDOW_MAXIMIZED = 10, // Set to maximize window (expanded to monitor) - WINDOW_UNFOCUSED = 11, // Set to window non focused - WINDOW_TOPMOST = 12, // Set to window always on top - WINDOW_ALWAYS_RUN = 8, // Set to allow windows running while minimized - WINDOW_TRANSPARENT = 4, // Set to allow transparent framebuffer - WINDOW_HIGHDPI = 13, // Set to support HighDPI - MSAA_4X_HINT = 5, // Set to try enabling MSAA 4X - INTERLACED_HINT = 16, // Set to try enabling interlaced video format (for V3D) + VSYNC_HINT = 6, // Set to try enabling V-Sync on GPU + FULLSCREEN_MODE = 1, // Set to run program in fullscreen + WINDOW_RESIZABLE = 2, // Set to allow resizable window + WINDOW_UNDECORATED = 3, // Set to disable window decoration (frame and buttons) + WINDOW_HIDDEN = 7, // Set to hide window + WINDOW_MINIMIZED = 9, // Set to minimize window (iconify) + WINDOW_MAXIMIZED = 10, // Set to maximize window (expanded to monitor) + WINDOW_UNFOCUSED = 11, // Set to window non focused + WINDOW_TOPMOST = 12, // Set to window always on top + WINDOW_ALWAYS_RUN = 8, // Set to allow windows running while minimized + WINDOW_TRANSPARENT = 4, // Set to allow transparent framebuffer + WINDOW_HIGHDPI = 13, // Set to support HighDPI + WINDOW_MOUSE_PASSTHROUGH = 14, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED + MSAA_4X_HINT = 5, // Set to try enabling MSAA 4X + INTERLACED_HINT = 16, // Set to try enabling interlaced video format (for V3D) } ConfigFlags :: distinct bit_set[ConfigFlag; c.int] // Trace log level TraceLogLevel :: enum c.int { - ALL = 0, // Display all logs - TRACE, - DEBUG, - INFO, - WARNING, - ERROR, - FATAL, - NONE, // Disable logging + ALL = 0, // Display all logs + TRACE, // Trace logging, intended for internal use only + DEBUG, // Debug logging, used for internal debugging, it should be disabled on release builds + INFO, // Info logging, used for program execution info + WARNING, // Warning logging, used on recoverable failures + ERROR, // Error logging, used on unrecoverable failures + FATAL, // Fatal logging, used to abort program: exit(EXIT_FAILURE) + NONE, // Disable logging } // Keyboard keys (US keyboard layout) // NOTE: Use GetKeyPressed() to allow redefining // required keys for alternative layouts KeyboardKey :: enum c.int { - NULL = 0, + KEY_NULL = 0, // Key: NULL, used for no key pressed // Alphanumeric keys - APOSTROPHE = 39, - COMMA = 44, - MINUS = 45, - PERIOD = 46, - SLASH = 47, - ZERO = 48, - ONE = 49, - TWO = 50, - THREE = 51, - FOUR = 52, - FIVE = 53, - SIX = 54, - SEVEN = 55, - EIGHT = 56, - NINE = 57, - SEMICOLON = 59, - EQUAL = 61, - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - + APOSTROPHE = 39, // Key: ' + COMMA = 44, // Key: , + MINUS = 45, // Key: - + PERIOD = 46, // Key: . + SLASH = 47, // Key: / + ZERO = 48, // Key: 0 + ONE = 49, // Key: 1 + TWO = 50, // Key: 2 + THREE = 51, // Key: 3 + FOUR = 52, // Key: 4 + FIVE = 53, // Key: 5 + SIX = 54, // Key: 6 + SEVEN = 55, // Key: 7 + EIGHT = 56, // Key: 8 + NINE = 57, // Key: 9 + SEMICOLON = 59, // Key: ; + EQUAL = 61, // Key: = + A = 65, // Key: A | a + B = 66, // Key: B | b + C = 67, // Key: C | c + D = 68, // Key: D | d + E = 69, // Key: E | e + F = 70, // Key: F | f + G = 71, // Key: G | g + H = 72, // Key: H | h + I = 73, // Key: I | i + J = 74, // Key: J | j + K = 75, // Key: K | k + L = 76, // Key: L | l + M = 77, // Key: M | m + N = 78, // Key: N | n + O = 79, // Key: O | o + P = 80, // Key: P | p + Q = 81, // Key: Q | q + R = 82, // Key: R | r + S = 83, // Key: S | s + T = 84, // Key: T | t + U = 85, // Key: U | u + V = 86, // Key: V | v + W = 87, // Key: W | w + X = 88, // Key: X | x + Y = 89, // Key: Y | y + Z = 90, // Key: Z | z + LEFT_BRACKET = 91, // Key: [ + BACKSLASH = 92, // Key: '\' + RIGHT_BRACKET = 93, // Key: ] + GRAVE = 96, // Key: ` // Function keys - SPACE = 32, - ESCAPE = 256, - ENTER = 257, - TAB = 258, - BACKSPACE = 259, - INSERT = 260, - DELETE = 261, - RIGHT = 262, - LEFT = 263, - DOWN = 264, - UP = 265, - PAGE_UP = 266, - PAGE_DOWN = 267, - HOME = 268, - END = 269, - CAPS_LOCK = 280, - SCROLL_LOCK = 281, - NUM_LOCK = 282, - PRINT_SCREEN = 283, - PAUSE = 284, - F1 = 290, - F2 = 291, - F3 = 292, - F4 = 293, - F5 = 294, - F6 = 295, - F7 = 296, - F8 = 297, - F9 = 298, - F10 = 299, - F11 = 300, - F12 = 301, - LEFT_SHIFT = 340, - LEFT_CONTROL = 341, - LEFT_ALT = 342, - LEFT_SUPER = 343, - RIGHT_SHIFT = 344, - RIGHT_CONTROL = 345, - RIGHT_ALT = 346, - RIGHT_SUPER = 347, - KB_MENU = 348, - LEFT_BRACKET = 91, - BACKSLASH = 92, - RIGHT_BRACKET = 93, - GRAVE = 96, - + SPACE = 32, // Key: Space + ESCAPE = 256, // Key: Esc + ENTER = 257, // Key: Enter + TAB = 258, // Key: Tab + BACKSPACE = 259, // Key: Backspace + INSERT = 260, // Key: Ins + DELETE = 261, // Key: Del + RIGHT = 262, // Key: Cursor right + LEFT = 263, // Key: Cursor left + DOWN = 264, // Key: Cursor down + UP = 265, // Key: Cursor up + PAGE_UP = 266, // Key: Page up + PAGE_DOWN = 267, // Key: Page down + HOME = 268, // Key: Home + END = 269, // Key: End + CAPS_LOCK = 280, // Key: Caps lock + SCROLL_LOCK = 281, // Key: Scroll down + NUM_LOCK = 282, // Key: Num lock + PRINT_SCREEN = 283, // Key: Print screen + PAUSE = 284, // Key: Pause + F1 = 290, // Key: F1 + F2 = 291, // Key: F2 + F3 = 292, // Key: F3 + F4 = 293, // Key: F4 + F5 = 294, // Key: F5 + F6 = 295, // Key: F6 + F7 = 296, // Key: F7 + F8 = 297, // Key: F8 + F9 = 298, // Key: F9 + F10 = 299, // Key: F10 + F11 = 300, // Key: F11 + F12 = 301, // Key: F12 + LEFT_SHIFT = 340, // Key: Shift left + LEFT_CONTROL = 341, // Key: Control left + LEFT_ALT = 342, // Key: Alt left + LEFT_SUPER = 343, // Key: Super left + RIGHT_SHIFT = 344, // Key: Shift right + RIGHT_CONTROL = 345, // Key: Control right + RIGHT_ALT = 346, // Key: Alt right + RIGHT_SUPER = 347, // Key: Super right + KB_MENU = 348, // Key: KB menu // Keypad keys - KP_0 = 320, - KP_1 = 321, - KP_2 = 322, - KP_3 = 323, - KP_4 = 324, - KP_5 = 325, - KP_6 = 326, - KP_7 = 327, - KP_8 = 328, - KP_9 = 329, - KP_DECIMAL = 330, - KP_DIVIDE = 331, - KP_MULTIPLY = 332, - KP_SUBTRACT = 333, - KP_ADD = 334, - KP_ENTER = 335, - KP_EQUAL = 336, + KP_0 = 320, // Key: Keypad 0 + KP_1 = 321, // Key: Keypad 1 + KP_2 = 322, // Key: Keypad 2 + KP_3 = 323, // Key: Keypad 3 + KP_4 = 324, // Key: Keypad 4 + KP_5 = 325, // Key: Keypad 5 + KP_6 = 326, // Key: Keypad 6 + KP_7 = 327, // Key: Keypad 7 + KP_8 = 328, // Key: Keypad 8 + KP_9 = 329, // Key: Keypad 9 + KP_DECIMAL = 330, // Key: Keypad . + KP_DIVIDE = 331, // Key: Keypad / + KP_MULTIPLY = 332, // Key: Keypad * + KP_SUBTRACT = 333, // Key: Keypad - + KP_ADD = 334, // Key: Keypad + + KP_ENTER = 335, // Key: Keypad Enter + KP_EQUAL = 336, // Key: Keypad = // Android key buttons - BACK = 4, - MENU = 82, - VOLUME_UP = 24, - VOLUME_DOWN = 25, + BACK = 4, // Key: Android back button + MENU = 82, // Key: Android menu button + VOLUME_UP = 24, // Key: Android volume up button + VOLUME_DOWN = 25, // Key: Android volume down button } // Mouse buttons MouseButton :: enum c.int { - LEFT = 0, // Mouse button left - RIGHT = 1, // Mouse button right - MIDDLE = 2, // Mouse button middle (pressed wheel) - SIDE = 3, // Mouse button side (advanced mouse device) - EXTRA = 4, // Mouse button extra (advanced mouse device) - FORWARD = 5, // Mouse button fordward (advanced mouse device) - BACK = 6, // Mouse button back (advanced mouse device) + LEFT = 0, // Mouse button left + RIGHT = 1, // Mouse button right + MIDDLE = 2, // Mouse button middle (pressed wheel) + SIDE = 3, // Mouse button side (advanced mouse device) + EXTRA = 4, // Mouse button extra (advanced mouse device) + FORWARD = 5, // Mouse button fordward (advanced mouse device) + BACK = 6, // Mouse button back (advanced mouse device) } // Mouse cursor MouseCursor :: enum c.int { - DEFAULT = 0, - ARROW = 1, - IBEAM = 2, - CROSSHAIR = 3, - POINTING_HAND = 4, - RESIZE_EW = 5, // The horizontal resize/move arrow shape - RESIZE_NS = 6, // The vertical resize/move arrow shape - RESIZE_NWSE = 7, // The top-left to bottom-right diagonal resize/move arrow shape - RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape - RESIZE_ALL = 9, // The omni-directional resize/move cursor shape - NOT_ALLOWED = 10, // The operation-not-allowed shape + DEFAULT = 0, // Default pointer shape + ARROW = 1, // Arrow shape + IBEAM = 2, // Text writing cursor shape + CROSSHAIR = 3, // Cross shape + POINTING_HAND = 4, // Pointing hand cursor + RESIZE_EW = 5, // Horizontal resize/move arrow shape + RESIZE_NS = 6, // Vertical resize/move arrow shape + RESIZE_NWSE = 7, // Top-left to bottom-right diagonal resize/move arrow shape + RESIZE_NESW = 8, // The top-right to bottom-left diagonal resize/move arrow shape + RESIZE_ALL = 9, // The omnidirectional resize/move cursor shape + NOT_ALLOWED = 10, // The operation-not-allowed shape } // Gamepad buttons GamepadButton :: enum c.int { - // This is here just for error checking - UNKNOWN = 0, - - // This is normally a DPAD - LEFT_FACE_UP, - LEFT_FACE_RIGHT, - LEFT_FACE_DOWN, - LEFT_FACE_LEFT, - - // This normally corresponds with PlayStation and Xbox controllers - // XBOX: [Y,X,A,B] - // PS3: [Triangle,Square,Cross,Circle] - // No support for 6 button controllers though.. - RIGHT_FACE_UP, - RIGHT_FACE_RIGHT, - RIGHT_FACE_DOWN, - RIGHT_FACE_LEFT, - - // Triggers - LEFT_TRIGGER_1, - LEFT_TRIGGER_2, - RIGHT_TRIGGER_1, - RIGHT_TRIGGER_2, - - // These are buttons in the center of the gamepad - MIDDLE_LEFT, // PS3 Select - MIDDLE, // PS Button/XBOX Button - MIDDLE_RIGHT, // PS3 Start - - // These are the joystick press in buttons - LEFT_THUMB, - RIGHT_THUMB, + UNKNOWN = 0, // Unknown button, just for error checking + LEFT_FACE_UP, // Gamepad left DPAD up button + LEFT_FACE_RIGHT, // Gamepad left DPAD right button + LEFT_FACE_DOWN, // Gamepad left DPAD down button + LEFT_FACE_LEFT, // Gamepad left DPAD left button + RIGHT_FACE_UP, // Gamepad right button up (i.e. PS3: Triangle, Xbox: Y) + RIGHT_FACE_RIGHT, // Gamepad right button right (i.e. PS3: Square, Xbox: X) + RIGHT_FACE_DOWN, // Gamepad right button down (i.e. PS3: Cross, Xbox: A) + RIGHT_FACE_LEFT, // Gamepad right button left (i.e. PS3: Circle, Xbox: B) + LEFT_TRIGGER_1, // Gamepad top/back trigger left (first), it could be a trailing button + LEFT_TRIGGER_2, // Gamepad top/back trigger left (second), it could be a trailing button + RIGHT_TRIGGER_1, // Gamepad top/back trigger right (one), it could be a trailing button + RIGHT_TRIGGER_2, // Gamepad top/back trigger right (second), it could be a trailing button + MIDDLE_LEFT, // Gamepad center buttons, left one (i.e. PS3: Select) + MIDDLE, // Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX) + MIDDLE_RIGHT, // Gamepad center buttons, right one (i.e. PS3: Start) + LEFT_THUMB, // Gamepad joystick pressed button left + RIGHT_THUMB, // Gamepad joystick pressed button right } // Gamepad axis GamepadAxis :: enum c.int { - // Left stick - LEFT_X = 0, - LEFT_Y = 1, - - // Right stick - RIGHT_X = 2, - RIGHT_Y = 3, - - // Pressure levels for the back triggers - LEFT_TRIGGER = 4, // [1..-1] (pressure-level) - RIGHT_TRIGGER = 5, // [1..-1] (pressure-level) + LEFT_X = 0, // Gamepad left stick X axis + LEFT_Y = 1, // Gamepad left stick Y axis + RIGHT_X = 2, // Gamepad right stick X axis + RIGHT_Y = 3, // Gamepad right stick Y axis + LEFT_TRIGGER = 4, // Gamepad back trigger left, pressure level: [1..-1] + RIGHT_TRIGGER = 5, // Gamepad back trigger right, pressure level: [1..-1] } // Material map index MaterialMapIndex :: enum c.int { - ALBEDO = 0, // MATERIAL_MAP_DIFFUSE - METALNESS = 1, // MATERIAL_MAP_SPECULAR - NORMAL = 2, - ROUGHNESS = 3, - OCCLUSION, - EMISSION, - HEIGHT, - BRDG, - CUBEMAP, // NOTE: Uses GL_TEXTURE_CUBE_MAP - IRRADIANCE, // NOTE: Uses GL_TEXTURE_CUBE_MAP - PREFILTER, // NOTE: Uses GL_TEXTURE_CUBE_MAP - - DIFFUSE = ALBEDO, - SPECULAR = METALNESS, + ALBEDO = 0, // Albedo material (same as: MATERIAL_MAP_DIFFUSE) + METALNESS, // Metalness material (same as: MATERIAL_MAP_SPECULAR) + NORMAL, // Normal material + ROUGHNESS, // Roughness material + OCCLUSION, // Ambient occlusion material + EMISSION, // Emission material + HEIGHT, // Heightmap material + CUBEMAP, // Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + IRRADIANCE, // Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + PREFILTER, // Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP) + BRDF, // Brdf material } // Shader location index ShaderLocationIndex :: enum c.int { - VERTEX_POSITION = 0, - VERTEX_TEXCOORD01, - VERTEX_TEXCOORD02, - VERTEX_NORMAL, - VERTEX_TANGENT, - VERTEX_COLOR, - MATRIX_MVP, - MATRIX_VIEW, - MATRIX_PROJECTION, - MATRIX_MODEL, - MATRIX_NORMAL, - VECTOR_VIEW, - COLOR_DIFFUSE, - COLOR_SPECULAR, - COLOR_AMBIENT, - MAP_ALBEDO, - MAP_METALNESS, - MAP_NORMAL, - MAP_ROUGHNESS, - MAP_OCCLUSION, - MAP_EMISSION, - MAP_HEIGHT, - MAP_CUBEMAP, - MAP_IRRADIANCE, - MAP_PREFILTER, - MAP_BRDF, - - MAP_DIFFUSE = MAP_ALBEDO, - MAP_SPECULAR = MAP_METALNESS, + VERTEX_POSITION = 0, // Shader location: vertex attribute: position + VERTEX_TEXCOORD01, // Shader location: vertex attribute: texcoord01 + VERTEX_TEXCOORD02, // Shader location: vertex attribute: texcoord02 + VERTEX_NORMAL, // Shader location: vertex attribute: normal + VERTEX_TANGENT, // Shader location: vertex attribute: tangent + VERTEX_COLOR, // Shader location: vertex attribute: color + MATRIX_MVP, // Shader location: matrix uniform: model-view-projection + MATRIX_VIEW, // Shader location: matrix uniform: view (camera transform) + MATRIX_PROJECTION, // Shader location: matrix uniform: projection + MATRIX_MODEL, // Shader location: matrix uniform: model (transform) + MATRIX_NORMAL, // Shader location: matrix uniform: normal + VECTOR_VIEW, // Shader location: vector uniform: view + COLOR_DIFFUSE, // Shader location: vector uniform: diffuse color + COLOR_SPECULAR, // Shader location: vector uniform: specular color + COLOR_AMBIENT, // Shader location: vector uniform: ambient color + MAP_ALBEDO, // Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE) + MAP_METALNESS, // Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR) + MAP_NORMAL, // Shader location: sampler2d texture: normal + MAP_ROUGHNESS, // Shader location: sampler2d texture: roughness + MAP_OCCLUSION, // Shader location: sampler2d texture: occlusion + MAP_EMISSION, // Shader location: sampler2d texture: emission + MAP_HEIGHT, // Shader location: sampler2d texture: height + MAP_CUBEMAP, // Shader location: samplerCube texture: cubemap + MAP_IRRADIANCE, // Shader location: samplerCube texture: irradiance + MAP_PREFILTER, // Shader location: samplerCube texture: prefilter + MAP_BRDF, // Shader location: sampler2d texture: brdf } // Shader uniform data type ShaderUniformDataType :: enum c.int { - FLOAT = 0, - VEC2, - VEC3, - VEC4, - INT, - IVEC2, - IVEC3, - IVEC4, - SAMPLER2D, + FLOAT = 0, // Shader uniform type: float + VEC2, // Shader uniform type: vec2 (2 float) + VEC3, // Shader uniform type: vec3 (3 float) + VEC4, // Shader uniform type: vec4 (4 float) + INT, // Shader uniform type: int + IVEC2, // Shader uniform type: ivec2 (2 int) + IVEC3, // Shader uniform type: ivec3 (3 int) + IVEC4, // Shader uniform type: ivec4 (4 int) + SAMPLER2D, // Shader uniform type: sampler2d } // Pixel formats // NOTE: Support depends on OpenGL version and platform PixelFormat :: enum c.int { - UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) - UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) - UNCOMPRESSED_R5G6B5, // 16 bpp - UNCOMPRESSED_R8G8B8, // 24 bpp - UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) - UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) - UNCOMPRESSED_R8G8B8A8, // 32 bpp - UNCOMPRESSED_R32, // 32 bpp (1 channel - float) - UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) - UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) - COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) - COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) - COMPRESSED_DXT3_RGBA, // 8 bpp - COMPRESSED_DXT5_RGBA, // 8 bpp - COMPRESSED_ETC1_RGB, // 4 bpp - COMPRESSED_ETC2_RGB, // 4 bpp - COMPRESSED_ETC2_EAC_RGBA, // 8 bpp - COMPRESSED_PVRT_RGB, // 4 bpp - COMPRESSED_PVRT_RGBA, // 4 bpp - COMPRESSED_ASTC_4x4_RGBA, // 8 bpp - COMPRESSED_ASTC_8x8_RGBA, // 2 bpp + UNKNOWN = 0, + UNCOMPRESSED_GRAYSCALE = 1, // 8 bit per pixel (no alpha) + UNCOMPRESSED_GRAY_ALPHA, // 8*2 bpp (2 channels) + UNCOMPRESSED_R5G6B5, // 16 bpp + UNCOMPRESSED_R8G8B8, // 24 bpp + UNCOMPRESSED_R5G5B5A1, // 16 bpp (1 bit alpha) + UNCOMPRESSED_R4G4B4A4, // 16 bpp (4 bit alpha) + UNCOMPRESSED_R8G8B8A8, // 32 bpp + UNCOMPRESSED_R32, // 32 bpp (1 channel - float) + UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) + UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) + COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) + COMPRESSED_DXT3_RGBA, // 8 bpp + COMPRESSED_DXT5_RGBA, // 8 bpp + COMPRESSED_ETC1_RGB, // 4 bpp + COMPRESSED_ETC2_RGB, // 4 bpp + COMPRESSED_ETC2_EAC_RGBA, // 8 bpp + COMPRESSED_PVRT_RGB, // 4 bpp + COMPRESSED_PVRT_RGBA, // 4 bpp + COMPRESSED_ASTC_4x4_RGBA, // 8 bpp + COMPRESSED_ASTC_8x8_RGBA, // 2 bpp } // Texture parameters: filter mode // NOTE 1: Filtering considers mipmaps if available in the texture // NOTE 2: Filter is accordingly set for minification and magnification TextureFilter :: enum c.int { - POINT = 0, // No filter, just pixel aproximation - BILINEAR, // Linear filtering - TRILINEAR, // Trilinear filtering (linear with mipmaps) - ANISOTROPIC_4X, // Anisotropic filtering 4x - ANISOTROPIC_8X, // Anisotropic filtering 8x - ANISOTROPIC_16X, // Anisotropic filtering 16x + POINT = 0, // No filter, just pixel approximation + BILINEAR, // Linear filtering + TRILINEAR, // Trilinear filtering (linear with mipmaps) + ANISOTROPIC_4X, // Anisotropic filtering 4x + ANISOTROPIC_8X, // Anisotropic filtering 8x + ANISOTROPIC_16X, // Anisotropic filtering 16x } // Texture parameters: wrap mode TextureWrap :: enum c.int { - REPEAT = 0, // Repeats texture in tiled mode - CLAMP, // Clamps texture to edge pixel in tiled mode - MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode - MIRROR_CLAMP, // Mirrors and clamps to border the texture in tiled mode + REPEAT = 0, // Repeats texture in tiled mode + CLAMP, // Clamps texture to edge pixel in tiled mode + MIRROR_REPEAT, // Mirrors and repeats the texture in tiled mode + MIRROR_CLAMP, // Mirrors and clamps to border the texture in tiled mode } // Cubemap layouts CubemapLayout :: enum c.int { - AUTO_DETECT = 0, // Automatically detect layout type - LINE_VERTICAL, // Layout is defined by a vertical line with faces - LINE_HORIZONTAL, // Layout is defined by an horizontal line with faces - CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces - CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces - PANORAMA, // Layout is defined by a panorama image (equirectangular map) + AUTO_DETECT = 0, // Automatically detect layout type + LINE_VERTICAL, // Layout is defined by a vertical line with faces + LINE_HORIZONTAL, // Layout is defined by an horizontal line with faces + CROSS_THREE_BY_FOUR, // Layout is defined by a 3x4 cross with cubemap faces + CROSS_FOUR_BY_THREE, // Layout is defined by a 4x3 cross with cubemap faces + PANORAMA, // Layout is defined by a panorama image (equirectangular map) } // Font type, defines generation method FontType :: enum c.int { - DEFAULT = 0, // Default font generation, anti-aliased - BITMAP, // Bitmap font generation, no anti-aliasing - SDF, // SDF font generation, requires external shader + DEFAULT = 0, // Default font generation, anti-aliased + BITMAP, // Bitmap font generation, no anti-aliasing + SDF, // SDF font generation, requires external shader } // Color blending modes (pre-defined) BlendMode :: enum c.int { - ALPHA = 0, // Blend textures considering alpha (default) - ADDITIVE, // Blend textures adding colors - MULTIPLIED, // Blend textures multiplying colors - ADD_COLORS, // Blend textures adding colors (alternative) - SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) - CUSTOM, // Belnd textures using custom src/dst factors (use rlSetBlendMode()) + ALPHA = 0, // Blend textures considering alpha (default) + ADDITIVE, // Blend textures adding colors + MULTIPLIED, // Blend textures multiplying colors + ADD_COLORS, // Blend textures adding colors (alternative) + SUBTRACT_COLORS, // Blend textures subtracting colors (alternative) + ALPHA_PREMULTIPLY, // Blend premultiplied textures considering alpha + CUSTOM, // Blend textures using custom src/dst factors (use rlSetBlendFactors()) + CUSTOM_SEPARATE, // Blend textures using custom rgb/alpha separate src/dst factors (use rlSetBlendFactorsSeparate()) } // Gestures // NOTE: It could be used as flags to enable only some gestures Gesture :: enum c.int { - TAP = 0, - DOUBLETAP = 1, - HOLD = 2, - DRAG = 3, - SWIPE_RIGHT = 4, - SWIPE_LEFT = 5, - SWIPE_UP = 6, - SWIPE_DOWN = 7, - PINCH_IN = 8, - PINCH_OUT = 9, + TAP = 0, // Tap gesture + DOUBLETAP = 1, // Double tap gesture + HOLD = 2, // Hold gesture + DRAG = 3, // Drag gesture + SWIPE_RIGHT = 4, // Swipe right gesture + SWIPE_LEFT = 5, // Swipe left gesture + SWIPE_UP = 6, // Swipe up gesture + SWIPE_DOWN = 7, // Swipe down gesture + PINCH_IN = 8, // Pinch in gesture + PINCH_OUT = 9, // Pinch out gesture } Gestures :: distinct bit_set[Gesture; c.int] // Camera system modes CameraMode :: enum c.int { - CUSTOM = 0, - FREE, - ORBITAL, - FIRST_PERSON, - THIRD_PERSON, + CUSTOM = 0, // Custom camera + FREE, // Free camera + ORBITAL, // Orbital camera + FIRST_PERSON, // First person camera + THIRD_PERSON, // Third person camera } // Camera projection CameraProjection :: enum c.int { - PERSPECTIVE = 0, - ORTHOGRAPHIC, + PERSPECTIVE = 0, // Perspective projection + ORTHOGRAPHIC, // Orthographic projection } // N-patch layout @@ -881,12 +870,14 @@ NPatchLayout :: enum c.int { // Callbacks to hook some internal functions // WARNING: This callbacks are intended for advance users -TraceLogCallback :: #type proc "c" (logLevel: TraceLogLevel, text: cstring, args: c.va_list) // Logging: Redirect trace log messages +TraceLogCallback :: #type proc "c" (logLevel: TraceLogLevel, text: cstring, args: c.va_list) // Logging: Redirect trace log messages LoadFileDataCallback :: #type proc "c"(fileName: cstring, bytesRead: ^c.uint) -> [^]u8 // FileIO: Load binary data SaveFileDataCallback :: #type proc "c" (fileName: cstring, data: rawptr, bytesToWrite: c.uint) -> bool // FileIO: Save binary data LoadFileTextCallback :: #type proc "c" (fileName: cstring) -> [^]u8 // FileIO: Load text data SaveFileTextCallback :: #type proc "c" (fileName: cstring, text: cstring) -> bool // FileIO: Save text data +AudioCallback :: #type proc "c" (bufferData: rawptr, frames: c.uint) + @(default_calling_convention="c") foreign lib { @@ -900,37 +891,42 @@ foreign lib { //------------------------------------------------------------------------------------ // Window-related functions + InitWindow :: proc(width, height: c.int, title: cstring) --- // Initialize window and OpenGL context - WindowShouldClose :: proc() -> bool --- // Check if KEY_ESCAPE pressed or Close icon pressed + WindowShouldClose :: proc() -> bool --- // Check if KEY_ESCAPE pressed or Close icon pressed CloseWindow :: proc() --- // Close window and unload OpenGL context - IsWindowReady :: proc() -> bool --- // Check if window has been initialized successfully - IsWindowFullscreen :: proc() -> bool --- // Check if window is currently fullscreen - IsWindowHidden :: proc() -> bool --- // Check if window is currently hidden (only PLATFORM_DESKTOP) - IsWindowMinimized :: proc() -> bool --- // Check if window is currently minimized (only PLATFORM_DESKTOP) - IsWindowMaximized :: proc() -> bool --- // Check if window is currently maximized (only PLATFORM_DESKTOP) - IsWindowFocused :: proc() -> bool --- // Check if window is currently focused (only PLATFORM_DESKTOP) - IsWindowResized :: proc() -> bool --- // Check if window has been resized last frame - IsWindowState :: proc(flag: ConfigFlags) -> bool --- // Check if one specific window flag is enabled - SetWindowState :: proc(flags: ConfigFlags) --- // Set window configuration state using flags + IsWindowReady :: proc() -> bool --- // Check if window has been initialized successfully + IsWindowFullscreen :: proc() -> bool --- // Check if window is currently fullscreen + IsWindowHidden :: proc() -> bool --- // Check if window is currently hidden (only PLATFORM_DESKTOP) + IsWindowMinimized :: proc() -> bool --- // Check if window is currently minimized (only PLATFORM_DESKTOP) + IsWindowMaximized :: proc() -> bool --- // Check if window is currently maximized (only PLATFORM_DESKTOP) + IsWindowFocused :: proc() -> bool --- // Check if window is currently focused (only PLATFORM_DESKTOP) + IsWindowResized :: proc() -> bool --- // Check if window has been resized last frame + IsWindowState :: proc(flag: ConfigFlag) -> bool --- // Check if one specific window flag is enabled + SetWindowState :: proc(flags: ConfigFlags) --- // Set window configuration state using flags (only PLATFORM_DESKTOP) ClearWindowState :: proc(flags: ConfigFlags) --- // Clear window configuration state flags ToggleFullscreen :: proc() --- // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) MaximizeWindow :: proc() --- // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) MinimizeWindow :: proc() --- // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) RestoreWindow :: proc() --- // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) - SetWindowIcon :: proc(image: Image) --- // Set icon for window (only PLATFORM_DESKTOP) + SetWindowIcon :: proc(image: Image) --- // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) + SetWindowIcons :: proc(images: [^]Image, count: c.int) --- // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) SetWindowTitle :: proc(title: cstring) --- // Set title for window (only PLATFORM_DESKTOP) SetWindowPosition :: proc(x, y: c.int) --- // Set window position on screen (only PLATFORM_DESKTOP) SetWindowMonitor :: proc(monitor: c.int) --- // Set monitor for the current window (fullscreen mode) SetWindowMinSize :: proc(width, height: c.int) --- // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) SetWindowSize :: proc(width, height: c.int) --- // Set window dimensions + SetWindowOpacity :: proc(opacity: f32) --- // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) GetWindowHandle :: proc() -> rawptr --- // Get native window handle GetScreenWidth :: proc() -> c.int --- // Get current screen width GetScreenHeight :: proc() -> c.int --- // Get current screen height + GetRenderWidth :: proc() -> c.int --- // Get current render width (it considers HiDPI) + GetRenderHeight :: proc() -> c.int --- // Get current render height (it considers HiDPI) GetMonitorCount :: proc() -> c.int --- // Get number of connected monitors GetCurrentMonitor :: proc() -> c.int --- // Get current connected monitor GetMonitorPosition :: proc(monitor: c.int) -> Vector2 --- // Get specified monitor position - GetMonitorWidth :: proc(monitor: c.int) -> c.int --- // Get specified monitor width (max available by monitor) - GetMonitorHeight :: proc(monitor: c.int) -> c.int --- // Get specified monitor height (max available by monitor) + GetMonitorWidth :: proc(monitor: c.int) -> c.int --- // Get specified monitor width (current video mode used by monitor) + GetMonitorHeight :: proc(monitor: c.int) -> c.int --- // Get specified monitor height (current video mode used by monitor) GetMonitorPhysicalWidth :: proc(monitor: c.int) -> c.int --- // Get specified monitor physical width in millimetres GetMonitorPhysicalHeight :: proc(monitor: c.int) -> c.int --- // Get specified monitor physical height in millimetres GetMonitorRefreshRate :: proc(monitor: c.int) -> c.int --- // Get specified monitor refresh rate @@ -939,26 +935,31 @@ foreign lib { GetMonitorName :: proc(monitor: c.int) -> cstring --- // Get the human-readable, UTF-8 encoded name of the primary monitor SetClipboardText :: proc(text: cstring) --- // Set clipboard text content GetClipboardText :: proc() -> cstring --- // Get clipboard text content + EnableEventWaiting :: proc() --- // Enable waiting for events on EndDrawing(), no automatic event polling + DisableEventWaiting :: proc() --- // Disable waiting for events on EndDrawing(), automatic events polling + - // Custom frame control functions // NOTE: Those functions are intended for advance users that want full control over the frame processing // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents() // To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL - SwapScreenBuffer :: proc() --- // Swap back buffer with front buffer (screen drawing) - PollInputEvents :: proc() --- // Register all input events - WaitTime :: proc(ms: f32) --- // Wait for some milliseconds (halt program execution) + + SwapScreenBuffer :: proc() --- // Swap back buffer with front buffer (screen drawing) + PollInputEvents :: proc() --- // Register all input events + WaitTime :: proc(seconds: f64) --- // Wait for some time (halt program execution) // Cursor-related functions - ShowCursor :: proc() --- // Shows cursor - HideCursor :: proc() --- // Hides cursor - IsCursorHidden :: proc() -> bool --- // Check if cursor is not visible - EnableCursor :: proc() --- // Enables cursor (unlock cursor) - DisableCursor :: proc() --- // Disables cursor (lock cursor) - IsCursorOnScreen :: proc() -> bool --- // Check if cursor is on the current screen. + + ShowCursor :: proc() --- // Shows cursor + HideCursor :: proc() --- // Hides cursor + IsCursorHidden :: proc() -> bool --- // Check if cursor is not visible + EnableCursor :: proc() --- // Enables cursor (unlock cursor) + DisableCursor :: proc() --- // Disables cursor (lock cursor) + IsCursorOnScreen :: proc() -> bool --- // Check if cursor is on the current screen. // Drawing-related functions + ClearBackground :: proc(color: Color) --- // Set background color (framebuffer clear color) BeginDrawing :: proc() --- // Setup canvas (framebuffer) to start drawing EndDrawing :: proc() --- // End canvas drawing and swap buffers (double buffering) @@ -978,13 +979,16 @@ foreign lib { EndVrStereoMode :: proc() --- // End stereo rendering (requires VR simulator) // VR stereo config functions for VR simulator + LoadVrStereoConfig :: proc(device: VrDeviceInfo) -> VrStereoConfig --- // Load VR stereo config for VR simulator device parameters UnloadVrStereoConfig :: proc(config: VrStereoConfig) --- // Unload VR stereo config // Shader management functions // NOTE: Shader functionality is not available on OpenGL 1.1 + LoadShader :: proc(vsFileName, fsFileName: cstring) -> Shader --- // Load shader from files and bind default locations LoadShaderFromMemory :: proc(vsCode, fsCode: cstring) -> Shader --- // Load shader from code strings and bind default locations + IsShaderReady :: proc(shader: Shader) -> bool --- // Check if a shader is ready GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> c.int --- // Get shader uniform location GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> c.int --- // Get shader attribute location SetShaderValue :: proc(shader: Shader, locIndex: ShaderLocationIndex, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value @@ -994,21 +998,24 @@ foreign lib { UnloadShader :: proc(shader: Shader) --- // Unload shader from GPU memory (VRAM) // Screen-space-related functions - GetMouseRay :: proc(mousePosition: Vector2, camera: Camera) -> Ray --- // Returns a ray trace from mouse position - GetCameraMatrix :: proc(camera: Camera) -> Matrix --- // Returns camera transform matrix (view matrix) - GetCameraMatrix2D :: proc(camera: Camera2D) -> Matrix --- // Returns camera 2d transform matrix - GetWorldToScreen :: proc(position: Vector3, camera: Camera) -> Vector2 --- // Returns the screen space position for a 3d world space position - GetWorldToScreenEx :: proc(position: Vector3, camera: Camera, width, height: c.int) -> Vector2 --- // Returns size position for a 3d world space position - GetWorldToScreen2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Returns the screen space position for a 2d camera world space position - GetScreenToWorld2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Returns the world space position for a 2d camera screen space position + + GetMouseRay :: proc(mousePosition: Vector2, camera: Camera) -> Ray --- // Get a ray trace from mouse position + GetCameraMatrix :: proc(camera: Camera) -> Matrix --- // Get camera transform matrix (view matrix) + GetCameraMatrix2D :: proc(camera: Camera2D) -> Matrix --- // Get camera 2d transform matrix + GetWorldToScreen :: proc(position: Vector3, camera: Camera) -> Vector2 --- // Get the screen space position for a 3d world space position + GetScreenToWorld2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Get the world space position for a 2d camera screen space position + GetWorldToScreenEx :: proc(position: Vector3, camera: Camera, width, height: c.int) -> Vector2 --- // Get size position for a 3d world space position + GetWorldToScreen2D :: proc(position: Vector2, camera: Camera2D) -> Vector2 --- // Get the screen space position for a 2d camera world space position // Timing-related functions + SetTargetFPS :: proc(fps: c.int) --- // Set target FPS (maximum) GetFPS :: proc() -> c.int --- // Returns current FPS GetFrameTime :: proc() -> f32 --- // Returns time in seconds for last frame drawn (delta time) GetTime :: proc() -> f64 --- // Returns elapsed time in seconds since InitWindow() // Misc. functions + GetRandomValue :: proc(min, max: c.int) -> c.int --- // Returns a random value between min and max (both included) SetRandomSeed :: proc(seed: c.uint) --- // Set the seed for the random number generator TakeScreenshot :: proc(fileName: cstring) --- // Takes a screenshot of current screen (filename extension defines format) @@ -1016,12 +1023,15 @@ foreign lib { TraceLog :: proc(logLevel: TraceLogLevel, text: cstring, #c_vararg args: ..any) --- // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR) SetTraceLogLevel :: proc(logLevel: TraceLogLevel) --- // Set the current threshold (minimum) log level - MemAlloc :: proc(size: c.int) -> rawptr --- // Internal memory allocator - MemRealloc :: proc(ptr: rawptr, size: c.int) -> rawptr --- // Internal memory reallocator - MemFree :: proc(ptr: rawptr) --- // Internal memory free + MemAlloc :: proc(size: c.uint) -> rawptr --- // Internal memory allocator + MemRealloc :: proc(ptr: rawptr, size: c.uint) -> rawptr --- // Internal memory reallocator + MemFree :: proc(ptr: rawptr) --- // Internal memory free + + OpenURL :: proc(url: cstring) --- // Open URL with default system browser (if available) // Set custom callbacks // WARNING: Callbacks setup is intended for advance users + SetTraceLogCallback :: proc(callback: TraceLogCallback) --- // Set custom trace log SetLoadFileDataCallback :: proc(callback: LoadFileDataCallback) --- // Set custom file binary data loader SetSaveFileDataCallback :: proc(callback: SaveFileDataCallback) --- // Set custom file binary data saver @@ -1029,45 +1039,48 @@ foreign lib { SetSaveFileTextCallback :: proc(callback: SaveFileTextCallback) --- // Set custom file text data saver // Files management functions - LoadFileData :: proc(fileName: cstring, bytesRead: ^c.uint) -> [^]u8 --- // Load file data as byte array (read) - UnloadFileData :: proc(data: [^]u8) --- // Unload file data allocated by LoadFileData() - SaveFileData :: proc(fileName: cstring, data: rawptr, bytesToWrite: c.uint) -> bool --- // Save data to file from byte array (write), returns true on success - LoadFileText :: proc(fileName: cstring) -> [^]u8 --- // Load text data from file (read), returns a '\0' terminated string - UnloadFileText :: proc(text: cstring) --- // Unload file text data allocated by LoadFileText() - SaveFileText :: proc(fileName: cstring, text: cstring) -> bool --- // Save text data to file (write), string must be '\0' terminated, returns true on success - FileExists :: proc(fileName: cstring) -> bool --- // Check if file exists - DirectoryExists :: proc(dirPath: cstring) -> bool --- // Check if a directory path exists - IsFileExtension :: proc(fileName: cstring, ext: cstring) -> bool --- // Check file extension (including point: .png, .wav) - GetFileExtension :: proc(fileName: cstring) -> cstring --- // Get pointer to extension for a filename string (includes dot: ".png") - GetFileName :: proc(filePath: cstring) -> cstring --- // Get pointer to filename for a path string - GetFileNameWithoutExt :: proc(filePath: cstring) -> cstring --- // Get filename string without extension (uses static string) - GetDirectoryPath :: proc(filePath: cstring) -> cstring --- // Get full path for a given fileName with path (uses static string) - GetPrevDirectoryPath :: proc(dirPath: cstring) -> cstring --- // Get previous directory path for a given path (uses static string) - GetWorkingDirectory :: proc() -> cstring --- // Get current working directory (uses static string) - GetDirectoryFiles :: proc(dirPath: cstring, count: ^c.int) -> [^]cstring --- // Get filenames in a directory path (memory should be freed) - ClearDirectoryFiles :: proc() --- // Clear directory files paths buffers (free memory) - ChangeDirectory :: proc(dir: cstring) -> bool --- // Change working directory, return true on success - IsFileDropped :: proc() -> bool --- // Check if a file has been dropped into window - GetDroppedFiles :: proc(count: ^c.int) -> [^]cstring --- // Get dropped files names (memory should be freed) - ClearDroppedFiles :: proc() --- // Clear dropped files paths buffer (free memory) - GetFileModTime :: proc(fileName: cstring) -> c.long --- // Get file modification time (last write time) - CompressData :: proc(data: [^]u8, dataLength: c.int, compDataLength: ^c.int) -> [^]u8 --- // Compress data (DEFLATE algorithm) - DecompressData :: proc(compData: [^]u8, compDataLength: c.int, dataLength: ^c.int) -> [^]u8 --- // Decompress data (DEFLATE algorithm) - EncodeDataBase64 :: proc(data: [^]u8, dataLength: c.int, outputLength: ^c.int) -> [^]u8 --- // Encode data to Base64 string - DecodeDataBase64 :: proc(data: [^]u8, outputLength: ^c.int) -> [^]u8 --- // Decode Base64 string data + LoadFileData :: proc(fileName: cstring, bytesRead: ^c.uint) -> [^]byte --- // Load file data as byte array (read) + UnloadFileData :: proc(data: [^]byte) --- // Unload file data allocated by LoadFileData() + SaveFileData :: proc(fileName: cstring, data: rawptr, bytesToWrite: c.uint) -> bool --- // Save data to file from byte array (write), returns true on success + ExportDataAsCode :: proc(data: rawptr, size: c.uint, fileName: cstring) -> bool --- // Export data to code (.h), returns true on success + LoadFileText :: proc(fileName: cstring) -> [^]byte --- // Load text data from file (read), returns a '\0' terminated string + UnloadFileText :: proc(text: [^]byte) --- // Unload file text data allocated by LoadFileText() + SaveFileText :: proc(fileName: cstring, text: [^]byte) -> bool --- // Save text data to file (write), string must be '\0' terminated, returns true on success + FileExists :: proc(fileName: cstring) -> bool --- // Check if file exists + DirectoryExists :: proc(dirPath: cstring) -> bool --- // Check if a directory path exists + IsFileExtension :: proc(fileName, ext: cstring) -> bool --- // Check file extension (including point: .png, .wav) + GetFileLength :: proc(fileName: cstring) -> c.int --- // Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) + GetFileExtension :: proc(fileName: cstring) -> cstring --- // Get pointer to extension for a filename string (includes dot: '.png') + GetFileName :: proc(filePath: cstring) -> cstring --- // Get pointer to filename for a path string + GetFileNameWithoutExt :: proc(filePath: cstring) -> cstring --- // Get filename string without extension (uses static string) + GetDirectoryPath :: proc(filePath: cstring) -> cstring --- // Get full path for a given fileName with path (uses static string) + GetPrevDirectoryPath :: proc(dirPath: cstring) -> cstring --- // Get previous directory path for a given path (uses static string) + GetWorkingDirectory :: proc() -> cstring --- // Get current working directory (uses static string) + GetApplicationDirectory :: proc() -> cstring --- // Get the directory if the running application (uses static string) + ChangeDirectory :: proc(dir: cstring) -> bool --- // Change working directory, return true on success + IsPathFile :: proc(path: cstring) -> bool --- // Check if a given path is a file or a directory + LoadDirectoryFiles :: proc(dirPath: cstring) -> FilePathList --- // Load directory filepaths + LoadDirectoryFilesEx :: proc(basePath: cstring, filter: cstring, scanSubdirs: bool) -> FilePathList --- // Load directory filepaths with extension filtering and recursive directory scan + UnloadDirectoryFiles :: proc(files: FilePathList) --- // Unload filepaths + IsFileDropped :: proc() -> bool --- // Check if a file has been dropped into window + LoadDroppedFiles :: proc() -> FilePathList --- // Load dropped filepaths + UnloadDroppedFiles :: proc(files: FilePathList) --- // Unload dropped filepaths + GetFileModTime :: proc(fileName: cstring) -> c.long --- // Get file modification time (last write time) - // Persistent storage management - SaveStorageValue :: proc(position: c.uint, value: c.int) -> bool --- // Save integer value to storage file (to defined position), returns true on success - LoadStorageValue :: proc(position: c.uint) -> c.int --- // Load integer value from storage file (from defined position) + // Compression/Encoding functionality - OpenURL :: proc(url: cstring) --- // Open URL with default system browser (if available) + CompressData :: proc(data: rawptr, dataSize: c.int, compDataSize: ^c.int) -> [^]byte --- // Compress data (DEFLATE algorithm), memory must be MemFree() + DecompressData :: proc(compData: rawptr, compDataSize: c.int, dataSize: ^c.int) -> [^]byte --- // Decompress data (DEFLATE algorithm), memory must be MemFree() + EncodeDataBase64 :: proc(data: rawptr, dataSize: c.int, outputSize: ^c.int) -> [^]byte --- // Encode data to Base64 string, memory must be MemFree() + DecodeDataBase64 :: proc(data: rawptr, outputSize: ^c.int) -> [^]byte --- // Decode Base64 string data, memory must be MemFree() //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ // Input-related functions: keyboard + IsKeyPressed :: proc(key: KeyboardKey) -> bool --- // Detect if a key has been pressed once IsKeyDown :: proc(key: KeyboardKey) -> bool --- // Detect if a key is being pressed IsKeyReleased :: proc(key: KeyboardKey) -> bool --- // Detect if a key has been released once @@ -1077,19 +1090,20 @@ foreign lib { GetCharPressed :: proc() -> rune --- // Get char pressed (unicode), call it multiple times for chars queued // Input-related functions: gamepads - IsGamepadAvailable :: proc(gamepad: c.int) -> bool --- // Detect if a gamepad is available - IsGamepadName :: proc(gamepad: c.int, name: cstring) -> bool --- // Check gamepad name (if available) - GetGamepadName :: proc(gamepad: c.int) -> cstring --- // Return gamepad internal name id - IsGamepadButtonPressed :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Detect if a gamepad button has been pressed once - IsGamepadButtonDown :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Detect if a gamepad button is being pressed - IsGamepadButtonReleased :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Detect if a gamepad button has been released once - IsGamepadButtonUp :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Detect if a gamepad button is NOT being pressed - GetGamepadButtonPressed :: proc() -> c.int --- // Get the last gamepad button pressed - GetGamepadAxisCount :: proc(gamepad: c.int) -> c.int --- // Return gamepad axis count for a gamepad - GetGamepadAxisMovement :: proc(gamepad: c.int, axis: GamepadAxis) -> f32 --- // Return axis movement value for a gamepad axis - SetGamepadMappings :: proc(mappings: cstring) -> c.int --- // Set internal gamepad mappings (SDL_GameControllerDB) + + IsGamepadAvailable :: proc(gamepad: c.int) -> bool --- // Check if a gamepad is available + GetGamepadName :: proc(gamepad: c.int) -> cstring --- // Get gamepad internal name id + IsGamepadButtonPressed :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Check if a gamepad button has been pressed once + IsGamepadButtonDown :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Check if a gamepad button is being pressed + IsGamepadButtonReleased :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Check if a gamepad button has been released once + IsGamepadButtonUp :: proc(gamepad: c.int, button: GamepadButton) -> bool --- // Check if a gamepad button is NOT being pressed + GetGamepadButtonPressed :: proc() -> GamepadButton --- // Get the last gamepad button pressed + GetGamepadAxisCount :: proc(gamepad: c.int) -> c.int --- // Get gamepad axis count for a gamepad + GetGamepadAxisMovement :: proc(gamepad: c.int, axis: GamepadAxis) -> f32 --- // Get axis movement value for a gamepad axis + SetGamepadMappings :: proc(mappings: cstring) -> c.int --- // Set internal gamepad mappings (SDL_GameControllerDB) // Input-related functions: mouse + IsMouseButtonPressed :: proc(button: MouseButton) -> bool --- // Detect if a mouse button has been pressed once IsMouseButtonDown :: proc(button: MouseButton) -> bool --- // Detect if a mouse button is being pressed IsMouseButtonReleased :: proc(button: MouseButton) -> bool --- // Detect if a mouse button has been released once @@ -1102,9 +1116,11 @@ foreign lib { SetMouseOffset :: proc(offsetX, offsetY: c.int) --- // Set mouse offset SetMouseScale :: proc(scaleX, scaleY: f32) --- // Set mouse scaling GetMouseWheelMove :: proc() -> f32 --- // Returns mouse wheel movement Y + GetMouseWheelMoveV :: proc() -> Vector2 --- // Get mouse wheel movement for both X and Y SetMouseCursor :: proc(cursor: MouseCursor) --- // Set mouse cursor // Input-related functions: touch + GetTouchX :: proc() -> c.int --- // Returns touch position X for touch point 0 (relative to screen size) GetTouchY :: proc() -> c.int --- // Returns touch position Y for touch point 0 (relative to screen size) GetTouchPosition :: proc(index: c.int) -> Vector2 --- // Returns touch position XY for a touch point index (relative to screen size) @@ -1112,8 +1128,9 @@ foreign lib { GetTouchPointCount :: proc() -> c.int --- // Get number of touch points //------------------------------------------------------------------------------------ - // Gestures and Touch Handling Functions (Module: gestures) + // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ + SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected GetGestureDetected :: proc() -> Gesture --- // Get latest detected gesture @@ -1126,13 +1143,9 @@ foreign lib { //------------------------------------------------------------------------------------ // Camera System Functions (Module: camera) //------------------------------------------------------------------------------------ - SetCameraMode :: proc(camera: Camera, mode: CameraMode) --- // Set camera mode (multiple camera modes available) - UpdateCamera :: proc(camera: ^Camera) --- // Update camera position for selected mode - SetCameraPanControl :: proc(keyPan: KeyboardKey) --- // Set camera pan key to combine with mouse movement (free camera) - SetCameraAltControl :: proc(keyAlt: KeyboardKey) --- // Set camera alt key to combine with mouse movement (free camera) - SetCameraSmoothZoomControl :: proc(keySmoothZoom: KeyboardKey) --- // Set camera smooth zoom key to combine with mouse (free camera) - SetCameraMoveControls :: proc(keyFront, keyBack, keyRight, keyLeft, keyUp, keyDown: KeyboardKey) --- // Set camera move controls (1st person and 3rd person cameras) + UpdateCamera :: proc(camera: ^Camera, mode: CameraMode) --- // Set camera mode (multiple camera modes available) + UpdateCameraPro :: proc(camera: ^Camera, movement: Vector3, rotation: Vector3, zoom: f32) --- // Update camera movement/rotation //------------------------------------------------------------------------------------ // Basic Shapes Drawing Functions (Module: shapes) @@ -1140,369 +1153,413 @@ foreign lib { // Set texture and rectangle to be used on shapes drawing // NOTE: It can be useful when using basic shapes and one single font, // defining a font char white rectangle would allow drawing everything in a single draw call + SetShapesTexture :: proc(texture: Texture2D, source: Rectangle) --- // Basic shapes drawing functions - DrawPixel :: proc(posX, posY: c.int, color: Color) --- // Draw a pixel - DrawPixelV :: proc(position: Vector2 , color: Color) --- // Draw a pixel (Vector version) - DrawLine :: proc(startPosX, startPosY, endPosX, endPosY: c.int, color: Color) --- // Draw a line - DrawLineV :: proc(startPos, endPos: Vector2, color: Color) --- // Draw a line (Vector version) - DrawLineEx :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line defining thickness - DrawLineBezier :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line using cubic-bezier curves in-out - DrawLineBezierQuad :: proc(startPos, endPos: Vector2, controlPos: Vector2, thick: f32, color: Color) --- // Draw line using quadratic bezier curves with a control point - DrawLineBezierCubic :: proc(startPos, endPos: Vector2, startControlPos, endControlPos: Vector2, thick: f32, color: Color) --- // Draw line using cubic bezier curves with 2 control points - DrawLineStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw lines sequence - DrawCircle :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw a color-filled circle - DrawCircleSector :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw a piece of a circle - DrawCircleSectorLines :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw circle sector outline - DrawCircleGradient :: proc(centerX, centerY: c.int, radius: f32, color1: Color, color2: Color) --- // Draw a gradient-filled circle - DrawCircleV :: proc(center: Vector2, radius: f32, color: Color) --- // Draw a color-filled circle (Vector version) - DrawCircleLines :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw circle outline - DrawEllipse :: proc(centerX, centerY: c.int, radiusH: f32, radiusV: f32, color: Color) --- // Draw ellipse - DrawEllipseLines :: proc(centerX, centerY: c.int, radiusH: f32, radiusV: f32, color: Color) --- // Draw ellipse outline - DrawRing :: proc(center: Vector2, innerRadius, outerRadius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw ring - DrawRingLines :: proc(center: Vector2, innerRadius, outerRadius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw ring outline - DrawRectangle :: proc(posX, posY, width, height: c.int, color: Color) --- // Draw a color-filled rectangle - DrawRectangleV :: proc(position, size: Vector2, color: Color) --- // Draw a color-filled rectangle (Vector version) - DrawRectangleRec :: proc(rec: Rectangle, color: Color) --- // Draw a color-filled rectangle - DrawRectanglePro :: proc(rec: Rectangle, origin: Vector2, rotation: f32, color: Color) --- // Draw a color-filled rectangle with pro parameters - DrawRectangleGradientV :: proc(posX, posY, width, height: c.int, color1: Color, color2: Color) --- // Draw a vertical-gradient-filled rectangle - DrawRectangleGradientH :: proc(posX, posY, width, height: c.int, color1: Color, color2: Color) --- // Draw a horizontal-gradient-filled rectangle - DrawRectangleGradientEx :: proc(rec: Rectangle, col1, col2, col3, col4: Color) --- // Draw a gradient-filled rectangle with custom vertex colors - DrawRectangleLines :: proc(posX, posY, width, height: c.int, color: Color) --- // Draw rectangle outline - DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: f32, color: Color) --- // Draw rectangle outline with extended parameters - DrawRectangleRounded :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle with rounded edges - DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle with rounded edges outline - DrawTriangle :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!) - DrawTriangleLines :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw triangle outline (vertex in counter-clockwise order!) - DrawTriangleFan :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw a triangle fan defined by points (first vertex is the center) - DrawTriangleStrip :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) --- // Draw a triangle strip defined by points - DrawPoly :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a regular polygon (Vector version) - DrawPolyLines :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a polygon outline of n sides - DrawPolyLinesEx :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, lineThick: f32, color: Color) --- // Draw a polygon outline of n sides with extended parameters - // Basic shapes collision detection functions - CheckCollisionRecs :: proc(rec1, rec2: Rectangle) -> bool --- // Check collision between two rectangles - CheckCollisionCircles :: proc(center1: Vector2, radius1: f32, center2: Vector2, radius2: f32) -> bool --- // Check collision between two circles - CheckCollisionCircleRec :: proc(center: Vector2, radius: f32, rec: Rectangle) -> bool --- // Check collision between circle and rectangle - CheckCollisionPointRec :: proc(point: Vector2, rec: Rectangle) -> bool --- // Check if point is inside rectangle - CheckCollisionPointCircle :: proc(point: Vector2, center: Vector2, radius: f32) -> bool --- // Check if point is inside circle - CheckCollisionPointTriangle :: proc(point, p1, p2, p3: Vector2) -> bool --- // Check if point is inside a triangle - CheckCollisionLines :: proc(startPos1, endPos1, startPos2, endPos2: Vector2, collisionPoint: ^Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference - CheckCollisionPointLine :: proc(point, p1, p2: Vector2, threshold: c.int) -> bool --- // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] - GetCollisionRec :: proc(rec1, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision + DrawPixel :: proc(posX, posY: c.int, color: Color) --- // Draw a pixel + DrawPixelV :: proc(position: Vector2, color: Color) --- // Draw a pixel (Vector version) + DrawLine :: proc(startPosX, startPosY, endPosX, endPosY: c.int, color: Color) --- // Draw a line + DrawLineV :: proc(startPos, endPos: Vector2, color: Color) --- // Draw a line (Vector version) + DrawLineEx :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line defining thickness + DrawLineBezier :: proc(startPos, endPos: Vector2, thick: f32, color: Color) --- // Draw a line using cubic-bezier curves in-out + DrawLineBezierQuad :: proc(startPos, endPos: Vector2, controlPos: Vector2, thick: f32, color: Color) --- // Draw line using quadratic bezier curves with a control point + DrawLineBezierCubic :: proc(startPos, endPos: Vector2, startControlPos, endControlPos: Vector2, thick: f32, color: Color) --- // Draw line using cubic bezier curves with 2 control points + DrawLineStrip :: proc(points: [^]Vector2, pointCount: c.int, color: Color) --- // Draw lines sequence + DrawCircle :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw a color-filled circle + DrawCircleSector :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw a piece of a circle + DrawCircleSectorLines :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw circle sector outline + DrawCircleGradient :: proc(centerX, centerY: c.int, radius: f32, color1, color2: Color) --- // Draw a gradient-filled circle + DrawCircleV :: proc(center: Vector2, radius: f32, color: Color) --- // Draw a color-filled circle (Vector version) + DrawCircleLines :: proc(centerX, centerY: c.int, radius: f32, color: Color) --- // Draw circle outline + DrawEllipse :: proc(centerX, centerY: c.int, radiusH, radiusV: f32, color: Color) --- // Draw ellipse + DrawEllipseLines :: proc(centerX, centerY: c.int, radiusH, radiusV: f32, color: Color) --- // Draw ellipse outline + DrawRing :: proc(center: Vector2, innerRadius, outerRadius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw ring + DrawRingLines :: proc(center: Vector2, innerRadius, outerRadius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) --- // Draw ring outline + DrawRectangle :: proc(posX, posY: c.int, width, height: c.int, color: Color) --- // Draw a color-filled rectangle + DrawRectangleV :: proc(position: Vector2, size: Vector2, color: Color) --- // Draw a color-filled rectangle (Vector version) + DrawRectangleRec :: proc(rec: Rectangle, color: Color) --- // Draw a color-filled rectangle + DrawRectanglePro :: proc(rec: Rectangle, origin: Vector2, rotation: f32, color: Color) --- // Draw a color-filled rectangle with pro parameters + DrawRectangleGradientV :: proc(posX, posY: c.int, width, height: c.int, color1, color2: Color) --- // Draw a vertical-gradient-filled rectangle + DrawRectangleGradientH :: proc(posX, posY: c.int, width, height: c.int, color1, color2: Color) --- // Draw a horizontal-gradient-filled rectangle + DrawRectangleGradientEx :: proc(rec: Rectangle, col1, col2, col3, col4: Color) --- // Draw a gradient-filled rectangle with custom vertex colors + DrawRectangleLines :: proc(posX, posY: c.int, width, height: c.int, color: Color) --- // Draw rectangle outline + DrawRectangleLinesEx :: proc(rec: Rectangle, lineThick: f32, color: Color) --- // Draw rectangle outline with extended parameters + DrawRectangleRounded :: proc(rec: Rectangle, roundness: f32, segments: c.int, color: Color) --- // Draw rectangle with rounded edges + DrawRectangleRoundedLines :: proc(rec: Rectangle, roundness: f32, segments: c.int, lineThick: f32, color: Color) --- // Draw rectangle with rounded edges outline + DrawTriangle :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!) + DrawTriangleLines :: proc(v1, v2, v3: Vector2, color: Color) --- // Draw triangle outline (vertex in counter-clockwise order!) + DrawTriangleFan :: proc(points: [^]Vector2, pointCount: c.int, color: Color) --- // Draw a triangle fan defined by points (first vertex is the center) + DrawTriangleStrip :: proc(points: [^]Vector2, pointCount: c.int, color: Color) --- // Draw a triangle strip defined by points + DrawPoly :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a regular polygon (Vector version) + DrawPolyLines :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) --- // Draw a polygon outline of n sides + DrawPolyLinesEx :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, lineThick: f32, color: Color) --- // Draw a polygon outline of n sides with extended parameters + + // Basic shapes collision detection functions + CheckCollisionRecs :: proc(rec1, rec2: Rectangle) -> bool --- // Check collision between two rectangles + CheckCollisionCircles :: proc(center1: Vector2, radius1: f32, center2: Vector2, radius2: f32) -> bool --- // Check collision between two circles + CheckCollisionCircleRec :: proc(center: Vector2, radius: f32, rec: Rectangle) -> bool --- // Check collision between circle and rectangle + CheckCollisionPointRec :: proc(point: Vector2, rec: Rectangle) -> bool --- // Check if point is inside rectangle + CheckCollisionPointCircle :: proc(point, center: Vector2, radius: f32) -> bool --- // Check if point is inside circle + CheckCollisionPointTriangle :: proc(point: Vector2, p1, p2, p3: Vector2) -> bool --- // Check if point is inside a triangle + CheckCollisionPointPoly :: proc(point: Vector2, points: [^]Vector2, pointCount: c.int) -> bool --- // Check if point is within a polygon described by array of vertices + CheckCollisionLines :: proc(startPos1, endPos1, startPos2, endPos2: Vector2, collisionPoint: [^]Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference + CheckCollisionPointLine :: proc(point: Vector2, p1, p2: Vector2, threshold: c.int) -> bool --- // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] + GetCollisionRec :: proc(rec1, rec2: Rectangle) -> Rectangle --- // Get collision rectangle for two rectangles collision + - //------------------------------------------------------------------------------------ - // Texture Loading and Drawing Functions (Module: textures) - //------------------------------------------------------------------------------------ // Image loading functions - // NOTE: This functions do not require GPU access + // NOTE: These functions do not require GPU access + LoadImage :: proc(fileName: cstring) -> Image --- // Load image from file into CPU memory (RAM) LoadImageRaw :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data - LoadImageAnim :: proc(fileName: cstring, frames: ^c.int) -> Image --- // Load image sequence from file (frames appended to image.data) - LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. ".png" + LoadImageAnim :: proc(fileName: cstring, frames: [^]c.int) -> Image --- // Load image sequence from file (frames appended to image.data) + LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image --- // Load image from memory buffer, fileType refers to extension: i.e. '.png' LoadImageFromTexture :: proc(texture: Texture2D) -> Image --- // Load image from GPU texture data + LoadImageFromScreen :: proc() -> Image --- // Load image from screen buffer and (screenshot) + IsImageReady :: proc(image: Image) -> bool --- // Check if an image is ready UnloadImage :: proc(image: Image) --- // Unload image from CPU memory (RAM) ExportImage :: proc(image: Image, fileName: cstring) -> bool --- // Export image data to file, returns true on success ExportImageAsCode :: proc(image: Image, fileName: cstring) -> bool --- // Export image as code file defining an array of bytes, returns true on success // Image generation functions - GenImageColor :: proc(width, height: c.int, color: Color) -> Image --- // Generate image: plain color - GenImageGradientV :: proc(width, height: c.int, top, bottom: Color) -> Image --- // Generate image: vertical gradient - GenImageGradientH :: proc(width, height: c.int, left, right: Color) -> Image --- // Generate image: horizontal gradient - GenImageGradientRadial :: proc(width, height: c.int, density: f32, inner, outer: Color) -> Image --- // Generate image: radial gradient - GenImageChecked :: proc(width, height: c.int, checksX, checksY: c.int, col1, col2: Color) -> Image --- // Generate image: checked - GenImageWhiteNoise :: proc(width, height: c.int, factor: f32) -> Image --- // Generate image: white noise - GenImageCellular :: proc(width, height: c.int, tileSize: c.int) -> Image --- // Generate image: cellular algorithm. Bigger tileSize means bigger cells + + GenImageColor :: proc(width, height: c.int, color: Color) -> Image --- // Generate image: plain color + GenImageGradientV :: proc(width, height: c.int, top, bottom: Color) -> Image --- // Generate image: vertical gradient + GenImageGradientH :: proc(width, height: c.int, left, right: Color) -> Image --- // Generate image: horizontal gradient + GenImageGradientRadial :: proc(width, height: c.int, density: f32, inner, outer: Color) -> Image --- // Generate image: radial gradient + GenImageChecked :: proc(width, height: c.int, checksX, checksY: c.int, col1, col2: Color) -> Image --- // Generate image: checked + GenImageWhiteNoise :: proc(width, height: c.int, factor: f32) -> Image --- // Generate image: white noise + GenImagePerlinNoise :: proc(width, height: c.int, offsetX, offsetY: c.int, scale: f32) -> Image --- // Generate image: perlin noise + GenImageCellular :: proc(width, height: c.int, tileSize: c.int) -> Image --- // Generate image: cellular algorithm, bigger tileSize means bigger cells + GenImageText :: proc(width, height: c.int, text: cstring) -> Image --- // Generate image: grayscale image from text data // Image manipulation functions - ImageCopy :: proc(image: Image) -> Image --- // Create an image duplicate (useful for transformations) - ImageFromImage :: proc(image: Image, rec: Rectangle) -> Image --- // Create an image from another image piece - ImageText :: proc(text: cstring, fontSize: c.int, color: Color) -> Image --- // Create an image from text (default font) - ImageTextEx :: proc(font: Font, text: cstring, fontSize, spacing: f32, tint: Color) -> Image --- // Create an image from text (custom sprite font) - ImageFormat :: proc(image: ^Image, newFormat: PixelFormat) --- // Convert image data to desired format - ImageToPOT :: proc(image: ^Image, fill: Color) --- // Convert image to POT (power-of-two) - ImageCrop :: proc(image: ^Image, crop: Rectangle) --- // Crop an image to a defined rectangle - ImageAlphaCrop :: proc(image: ^Image, threshold: f32) --- // Crop image depending on alpha value - ImageAlphaClear :: proc(image: ^Image, color: Color, threshold: f32) --- // Clear alpha channel to desired color - ImageAlphaMask :: proc(image: ^Image, alphaMask: Image) --- // Apply alpha mask to image - ImageAlphaPremultiply :: proc(image: ^Image) --- // Premultiply alpha channel - ImageResize :: proc(image: ^Image, newWidth, newHeight: c.int) --- // Resize image (Bicubic scaling algorithm) - ImageResizeNN :: proc(image: ^Image, newWidth, newHeight: c.int) --- // Resize image (Nearest-Neighbor scaling algorithm) - ImageResizeCanvas :: proc(image: ^Image, newWidth, newHeight: c.int, offsetX, offsetY: c.int, fill: Color) --- // Resize canvas and fill with color - ImageMipmaps :: proc(image: ^Image) --- // Generate all mipmap levels for a provided image - ImageDither :: proc(image: ^Image, rBpp, gBpp, bBpp, aBpp: c.int) --- // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) - ImageFlipVertical :: proc(image: ^Image) --- // Flip image vertically - ImageFlipHorizontal :: proc(image: ^Image) --- // Flip image horizontally - ImageRotateCW :: proc(image: ^Image) --- // Rotate image clockwise 90deg - ImageRotateCCW :: proc(image: ^Image) --- // Rotate image counter-clockwise 90deg - ImageColorTint :: proc(image: ^Image, color: Color) --- // Modify image color: tint - ImageColorInvert :: proc(image: ^Image) --- // Modify image color: invert - ImageColorGrayscale :: proc(image: ^Image) --- // Modify image color: grayscale - ImageColorContrast :: proc(image: ^Image, contrast: f32) --- // Modify image color: contrast (-100 to 100) - ImageColorBrightness :: proc(image: ^Image, brightness: c.int) --- // Modify image color: brightness (-255 to 255) - ImageColorReplace :: proc(image: ^Image, color, replace: Color) --- // Modify image color: replace color - LoadImageColors :: proc(image: Image) -> [^]Color --- // Load color data from image as a Color array (RGBA - 32bit) - LoadImagePalette :: proc(image: Image, maxPaletteSize: c.int, colorsCount: [^]c.int) -> [^]Color --- // Load colors palette from image as a Color array (RGBA - 32bit) - UnloadImageColors :: proc(colors: [^]Color) --- // Unload color data loaded with LoadImageColors() - UnloadImagePalette :: proc(colors: [^]Color) --- // Unload colors palette loaded with LoadImagePalette() - GetImageAlphaBorder :: proc(image: Image, threshold: f32) -> Rectangle --- // Get image alpha border rectangle - GetImageColor :: proc(image: Image, x, y: c.int) -> Color --- // Get image pixel color at (x, y) position + + ImageCopy :: proc(image: Image) -> Image --- // Create an image duplicate (useful for transformations) + ImageFromImage :: proc(image: Image, rec: Rectangle) -> Image --- // Create an image from another image piece + ImageText :: proc(text: cstring, fontSize: c.int, color: Color) -> Image --- // Create an image from text (default font) + ImageTextEx :: proc(font: Font, text: cstring, fontSize: f32, spacing: f32, tint: Color) -> Image --- // Create an image from text (custom sprite font) + ImageFormat :: proc(image: ^Image, newFormat: PixelFormat) --- // Convert image data to desired format + ImageToPOT :: proc(image: ^Image, fill: Color) --- // Convert image to POT (power-of-two) + ImageCrop :: proc(image: ^Image, crop: Rectangle) --- // Crop an image to a defined rectangle + ImageAlphaCrop :: proc(image: ^Image, threshold: f32) --- // Crop image depending on alpha value + ImageAlphaClear :: proc(image: ^Image, color: Color, threshold: f32) --- // Clear alpha channel to desired color + ImageAlphaMask :: proc(image: ^Image, alphaMask: Image) --- // Apply alpha mask to image + ImageAlphaPremultiply :: proc(image: ^Image) --- // Premultiply alpha channel + ImageBlurGaussian :: proc(image: ^Image, blurSize: c.int) --- // Apply Gaussian blur using a box blur approximation + ImageResize :: proc(image: ^Image, newWidth, newHeight: c.int) --- // Resize image (Bicubic scaling algorithm) + ImageResizeNN :: proc(image: ^Image, newWidth, newHeight: c.int) --- // Resize image (Nearest-Neighbor scaling algorithm) + ImageResizeCanvas :: proc(image: ^Image, newWidth, newHeight: c.int, offsetX, offsetY: c.int, fill: Color) --- // Resize canvas and fill with color + ImageMipmaps :: proc(image: ^Image) --- // Compute all mipmap levels for a provided image + ImageDither :: proc(image: ^Image, rBpp, gBpp, bBpp, aBpp: c.int) --- // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) + ImageFlipVertical :: proc(image: ^Image) --- // Flip image vertically + ImageFlipHorizontal :: proc(image: ^Image) --- // Flip image horizontally + ImageRotateCW :: proc(image: ^Image) --- // Rotate image clockwise 90deg + ImageRotateCCW :: proc(image: ^Image) --- // Rotate image counter-clockwise 90deg + ImageColorTint :: proc(image: ^Image, color: Color) --- // Modify image color: tint + ImageColorInvert :: proc(image: ^Image) --- // Modify image color: invert + ImageColorGrayscale :: proc(image: ^Image) --- // Modify image color: grayscale + ImageColorContrast :: proc(image: ^Image, contrast: f32) --- // Modify image color: contrast (-100 to 100) + ImageColorBrightness :: proc(image: ^Image, brightness: c.int) --- // Modify image color: brightness (-255 to 255) + ImageColorReplace :: proc(image: ^Image, color, replace: Color) --- // Modify image color: replace color + LoadImageColors :: proc(image: Image) -> [^]Color --- // Load color data from image as a Color array (RGBA - 32bit) + LoadImagePalette :: proc(image: Image, maxPaletteSize: c.int, colorCount: ^c.int) -> [^]Color --- // Load colors palette from image as a Color array (RGBA - 32bit) + UnloadImageColors :: proc(colors: [^]Color) --- // Unload color data loaded with LoadImageColors() + UnloadImagePalette :: proc(colors: [^]Color) --- // Unload colors palette loaded with LoadImagePalette() + GetImageAlphaBorder :: proc(image: Image, threshold: f32) -> Rectangle --- // Get image alpha border rectangle + GetImageColor :: proc(image: Image, x, y: c.int) -> Color --- // Get image pixel color at (x, y) position // Image drawing functions // NOTE: Image software-rendering functions (CPU) - ImageClearBackground :: proc(dst: ^Image, color: Color) --- // Clear image background with given color - ImageDrawPixel :: proc(dst: ^Image, posX, posY: c.int, color: Color) --- // Draw pixel within an image - ImageDrawPixelV :: proc(dst: ^Image, position: Vector2, color: Color) --- // Draw pixel within an image (Vector version) - ImageDrawLine :: proc(dst: ^Image, startPosX, startPosY, endPosX, endPosY: c.int, color: Color) --- // Draw line within an image - ImageDrawLineV :: proc(dst: ^Image, start, end: Vector2, color: Color) --- // Draw line within an image (Vector version) - ImageDrawCircle :: proc(dst: ^Image, centerX, centerY, radius: c.int, color: Color) --- // Draw circle within an image - ImageDrawCircleV :: proc(dst: ^Image, center: Vector2, radius: c.int, color: Color) --- // Draw circle within an image (Vector version) - ImageDrawRectangle :: proc(dst: ^Image, posX, posY, width, height: c.int, color: Color) --- // Draw rectangle within an image - ImageDrawRectangleV :: proc(dst: ^Image, position, size: Vector2, color: Color) --- // Draw rectangle within an image (Vector version) - ImageDrawRectangleRec :: proc(dst: ^Image, rec: Rectangle, color: Color) --- // Draw rectangle within an image - ImageDrawRectangleLines :: proc(dst: ^Image, rec: Rectangle, thick: c.int, color: Color) --- // Draw rectangle lines within an image - ImageDraw :: proc(dst: ^Image, src: Image, srcRec, dstRec: Rectangle, tint: Color) --- // Draw a source image within a destination image (tint applied to source) - ImageDrawText :: proc(dst: ^Image, text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) within an image (destination) - ImageDrawTextEx :: proc(dst: ^Image, font: Font, text: cstring, position: Vector2, fontSize, spacing: f32, tint: Color) --- // Draw text (custom sprite font) within an image (destination) + + ImageClearBackground :: proc(dst: ^Image, color: Color) --- // Clear image background with given color + ImageDrawPixel :: proc(dst: ^Image, posX, posY: c.int, color: Color) --- // Draw pixel within an image + ImageDrawPixelV :: proc(dst: ^Image, position: Vector2, color: Color) --- // Draw pixel within an image (Vector version) + ImageDrawLine :: proc(dst: ^Image, startPosX, startPosY, endPosX, endPosY: c.int, color: Color) --- // Draw line within an image + ImageDrawLineV :: proc(dst: ^Image, start, end: Vector2, color: Color) --- // Draw line within an image (Vector version) + ImageDrawCircle :: proc(dst: ^Image, centerX, centerY: c.int, radius: c.int, color: Color) --- // Draw a filled circle within an image + ImageDrawCircleV :: proc(dst: ^Image, center: Vector2, radius: c.int, color: Color) --- // Draw a filled circle within an image (Vector version) + ImageDrawCircleLines :: proc(dst: ^Image, centerX, centerY: c.int, radius: c.int, color: Color) --- // Draw circle outline within an image + ImageDrawCircleLinesV :: proc(dst: ^Image, center: Vector2, radius: c.int, color: Color) --- // Draw circle outline within an image (Vector version) + ImageDrawRectangle :: proc(dst: ^Image, posX, posY: c.int, width, height: c.int, color: Color) --- // Draw rectangle within an image + ImageDrawRectangleV :: proc(dst: ^Image, position, size: Vector2, color: Color) --- // Draw rectangle within an image (Vector version) + ImageDrawRectangleRec :: proc(dst: ^Image, rec: Rectangle, color: Color) --- // Draw rectangle within an image + ImageDrawRectangleLines :: proc(dst: ^Image, rec: Rectangle, thick: c.int, color: Color) --- // Draw rectangle lines within an image + ImageDraw :: proc(dst: ^Image, src: Image, srcRec, dstRec: Rectangle, tint: Color) --- // Draw a source image within a destination image (tint applied to source) + ImageDrawText :: proc(dst: ^Image, text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) within an image (destination) + ImageDrawTextEx :: proc(dst: ^Image, font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text (custom sprite font) within an image (destination) // Texture loading functions // NOTE: These functions require GPU access + LoadTexture :: proc(fileName: cstring) -> Texture2D --- // Load texture from file into GPU memory (VRAM) LoadTextureFromImage :: proc(image: Image) -> Texture2D --- // Load texture from image data LoadTextureCubemap :: proc(image: Image, layout: CubemapLayout) -> TextureCubemap --- // Load cubemap from image, multiple image cubemap layouts supported LoadRenderTexture :: proc(width, height: c.int) -> RenderTexture2D --- // Load texture for rendering (framebuffer) + IsTextureReady :: proc(texture: Texture2D) -> bool --- // Check if a texture is ready UnloadTexture :: proc(texture: Texture2D) --- // Unload texture from GPU memory (VRAM) + IsRenderTextureReady :: proc(target: RenderTexture2D) -> bool --- // Check if a render texture is ready UnloadRenderTexture :: proc(target: RenderTexture2D) --- // Unload render texture from GPU memory (VRAM) UpdateTexture :: proc(texture: Texture2D, pixels: rawptr) --- // Update GPU texture with new data UpdateTextureRec :: proc(texture: Texture2D, rec: Rectangle, pixels: rawptr) --- // Update GPU texture rectangle with new data // Texture configuration functions - GenTextureMipmaps :: proc(texture: ^Texture2D) --- // Generate GPU mipmaps for a texture - SetTextureFilter :: proc(texture: Texture2D, filter: TextureFilter) --- // Set texture scaling filter mode - SetTextureWrap :: proc(texture: Texture2D, wrap: TextureWrap) --- // Set texture wrapping mode - // Texture drawing functions - DrawTexture :: proc(texture: Texture2D, posX, posY: c.int, tint: Color) --- // Draw a Texture2D - DrawTextureV :: proc(texture: Texture2D, position: Vector2, tint: Color) --- // Draw a Texture2D with position defined as Vector2 - DrawTextureEx :: proc(texture: Texture2D, position: Vector2, rotation, scale: f32, tint: Color) --- // Draw a Texture2D with extended parameters - DrawTextureRec :: proc(texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) --- // Draw a part of a texture defined by a rectangle - DrawTextureQuad :: proc(texture: Texture2D, tiling: Vector2, offset: Vector2, quad: Rectangle, tint: Color) --- // Draw texture quad with tiling and offset parameters - DrawTextureTiled :: proc(texture: Texture2D, source, dest: Rectangle, origin: Vector2, rotation, scale: f32, tint: Color) --- // Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest. - DrawTexturePro :: proc(texture: Texture2D, source, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draw a part of a texture defined by a rectangle with 'pro' parameters - DrawTextureNPatch :: proc(texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draws a texture (or part of it) that stretches or shrinks nicely - DrawTexturePoly :: proc(texture: Texture2D, center: Vector2, points: [^]Vector2, texcoords: [^]Vector2, pointsCount: c.int, tint: Color) --- // Draw a textured polygon + GenTextureMipmaps :: proc(texture: ^Texture2D) --- // Generate GPU mipmaps for a texture + SetTextureFilter :: proc(texture: Texture2D, filter: TextureFilter) --- // Set texture scaling filter mode + SetTextureWrap :: proc(texture: Texture2D, wrap: TextureWrap) --- // Set texture wrapping mode + + // Texture drawing functions + DrawTexture :: proc(texture: Texture2D, posX, posY: c.int, tint: Color) --- // Draw a Texture2D + DrawTextureV :: proc(texture: Texture2D, position: Vector2, tint: Color) --- // Draw a Texture2D with position defined as Vector2 + DrawTextureEx :: proc(texture: Texture2D, position: Vector2, rotation: f32, scale: f32, tint: Color) --- // Draw a Texture2D with extended parameters + DrawTextureRec :: proc(texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) --- // Draw a part of a texture defined by a rectangle + DrawTexturePro :: proc(texture: Texture2D, source, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draw a part of a texture defined by a rectangle with 'pro' parameters + DrawTextureNPatch :: proc(texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: f32, tint: Color) --- // Draws a texture (or part of it) that stretches or shrinks nicely // Color/pixel related functions - Fade :: proc(color: Color, alpha: f32) -> Color --- // Returns color with alpha applied, alpha goes from 0.0f to 1.0f - ColorToInt :: proc(color: Color) -> c.int --- // Returns hexadecimal value for a Color - ColorNormalize :: proc(color: Color) -> Vector4 --- // Returns Color normalized as float [0..1] - ColorFromNormalized :: proc(normalized: Vector4) -> Color --- // Returns Color from normalized values [0..1] - ColorToHSV :: proc(color: Color) -> Vector3 --- // Returns HSV values for a Color, hue [0..360], saturation/value [0..1] - ColorFromHSV :: proc(hue, saturation, value: f32) -> Color --- // Returns a Color from HSV values, hue [0..360], saturation/value [0..1] - ColorAlpha :: proc(color: Color, alpha: f32) -> Color --- // Returns color with alpha applied, alpha goes from 0.0f to 1.0f - ColorAlphaBlend :: proc(dst, src, tint: Color) -> Color --- // Returns src alpha-blended into dst color with tint - GetColor :: proc(hexValue: c.int) -> Color --- // Get Color structure from hexadecimal value + + Fade :: proc(color: Color, alpha: f32) -> Color --- // Get color with alpha applied, alpha goes from 0.0f to 1.0f + ColorToInt :: proc(color: Color) -> c.uint --- // Get hexadecimal value for a Color + ColorNormalize :: proc(color: Color) -> Vector4 --- // Get Color normalized as float [0..1] + ColorFromNormalized :: proc(normalized: Vector4) -> Color --- // Get Color from normalized values [0..1] + ColorToHSV :: proc(color: Color) -> Vector3 --- // Get HSV values for a Color, hue [0..360], saturation/value [0..1] + ColorFromHSV :: proc(hue, saturation, value: f32) -> Color --- // Get a Color from HSV values, hue [0..360], saturation/value [0..1] + ColorTint :: proc(color, tint: Color) -> Color --- // Get color multiplied with another color + ColorBrightness :: proc(color: Color, factor: f32) -> Color --- // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f + ColorContrast :: proc(color: Color, contrast: f32) -> Color --- // Get color with contrast correction, contrast values between -1.0f and 1.0f + ColorAlpha :: proc(color: Color, alpha: f32) -> Color --- // Get color with alpha applied, alpha goes from 0.0f to 1.0f + ColorAlphaBlend :: proc(dst, src, tint: Color) -> Color --- // Get src alpha-blended into dst color with tint + GetColor :: proc(hexValue: c.uint) -> Color --- // Get Color structure from hexadecimal value GetPixelColor :: proc(srcPtr: rawptr, format: PixelFormat) -> Color --- // Get Color from a source pixel pointer of certain format SetPixelColor :: proc(dstPtr: rawptr, color: Color, format: PixelFormat) --- // Set color formatted into destination pixel pointer GetPixelDataSize :: proc(width, height: c.int, format: PixelFormat) -> c.int --- // Get pixel data size in bytes for certain format + + + //------------------------------------------------------------------------------------ // Font Loading and Text Drawing Functions (Module: text) //------------------------------------------------------------------------------------ // Font loading/unloading functions - GetFontDefault :: proc() -> Font --- // Get the default Font - LoadFont :: proc(fileName: cstring) -> Font --- // Load font from file into GPU memory (VRAM) - LoadFontEx :: proc(fileName: cstring, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from file with extended parameters - LoadFontFromImage :: proc(image: Image, key: Color, firstChar: rune) -> Font --- // Load font from Image (XNA style) - LoadFontFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font --- // Load font from memory buffer, fileType refers to extension: i.e. ".ttf" - LoadFontData :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int, type: FontType) -> [^]GlyphInfo --- // Load font data for further use - GenImageFontAtlas :: proc(chars: [^]GlyphInfo, recs: ^[^]Rectangle, charsCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image --- // Generate image font atlas using chars info - UnloadFontData :: proc(chars: [^]GlyphInfo, charsCount: c.int) --- // Unload font chars info data (RAM) - UnloadFont :: proc(font: Font) --- // Unload Font from GPU memory (VRAM) + + GetFontDefault :: proc() -> Font --- // Get the default Font + LoadFont :: proc(fileName: cstring) -> Font --- // Load font from file into GPU memory (VRAM) + LoadFontEx :: proc(fileName: cstring, fontSize: c.int, fontChars: [^]rune, glyphCount: c.int) -> Font --- // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set + LoadFontFromImage :: proc(image: Image, key: Color, firstChar: rune) -> Font --- // Load font from Image (XNA style) + LoadFontFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, glyphCount: c.int) -> Font --- // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' + IsFontReady :: proc(font: Font) -> bool --- // Check if a font is ready + LoadFontData :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, glyphCount: c.int, type: FontType) -> [^]GlyphInfo --- // Load font data for further use + GenImageFontAtlas :: proc(chars: [^]GlyphInfo, recs: ^[^]Rectangle, glyphCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image --- // Generate image font atlas using chars info + UnloadFontData :: proc(chars: [^]GlyphInfo, glyphCount: c.int) --- // Unload font chars info data (RAM) + UnloadFont :: proc(font: Font) --- // Unload font from GPU memory (VRAM) + ExportFontAsCode :: proc(font: Font, fileName: cstring) -> bool --- // Export font as code file, returns true on success // Text drawing functions - DrawFPS :: proc(posX, posY: c.int) --- // Draw current FPS - DrawText :: proc(text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) - DrawTextEx :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using font and additional parameters - DrawTextPro :: proc(font: Font, text: cstring, position, origin: Vector2, - rotation: f32, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using Font and pro parameters (rotation) - DrawTextCodepoint :: proc(font: Font, codepoint: rune, position: Vector2, fontSize: f32, tint: Color) --- // Draw one character (codepoint) - // Text misc. functions - MeasureText :: proc(text: cstring, fontSize: c.int) -> c.int --- // Measure string width for default font - MeasureTextEx :: proc(font: Font, text: cstring, fontSize, spacing: f32) -> Vector2 --- // Measure string size for Font - GetGlyphIndex :: proc(font: Font, codepoint: rune) -> c.int --- // Get index position for a unicode character on font - GetGlyphInfo :: proc(font: Font, codepoint: rune) -> GlyphInfo --- // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found - GetGlyphAtlasRec :: proc(font: Font, codepoint: rune) -> Rectangle --- // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found + DrawFPS :: proc(posX, posY: c.int) --- // Draw current FPS + DrawText :: proc(text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) --- // Draw text (using default font) + DrawTextEx :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using font and additional parameters + DrawTextPro :: proc(font: Font, text: cstring, position, origin: Vector2, rotation: f32, fontSize: f32, spacing: f32, tint: Color) --- // Draw text using Font and pro parameters (rotation) + DrawTextCodepoint :: proc(font: Font, codepoint: rune, position: Vector2, fontSize: f32, tint: Color) --- // Draw one character (codepoint) + DrawTextCodepoints :: proc(font: Font, codepoints: [^]rune, count: c.int, position: Vector2, fontSize: f32, spacing: f32, tint: Color) --- // Draw multiple character (codepoint) + + // Text font info functions + + MeasureText :: proc(text: cstring, fontSize: c.int) -> c.int --- // Measure string width for default font + MeasureTextEx :: proc(font: Font, text: cstring, fontSize: f32, spacing: f32) -> Vector2 --- // Measure string size for Font + GetGlyphIndex :: proc(font: Font, codepoint: rune) -> c.int --- // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found + GetGlyphInfo :: proc(font: Font, codepoint: rune) -> GlyphInfo --- // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found + GetGlyphAtlasRec :: proc(font: Font, codepoint: rune) -> Rectangle --- // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found // Text codepoints management functions (unicode characters) - LoadCodepoints :: proc(text: cstring, count: ^c.int) -> [^]rune --- // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter - UnloadCodepoints :: proc(codepoints: [^]rune) --- // Unload codepoints data from memory - GetCodepointCount :: proc(text: cstring) -> c.int --- // Get total number of codepoints in a UTF-8 encoded string - GetCodepoint :: proc(text: cstring, bytesProcessed: ^c.int) -> c.int --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure - CodepointToUTF8 :: proc(codepoint: rune, byteSize: ^c.int) -> cstring --- // Encode one codepoint into UTF-8 byte array (array length returned as parameter) - TextCodepointsToUTF8 :: proc(codepoints: [^]rune, length: c.int) -> cstring --- // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!) + LoadUTF8 :: proc(codepoints: [^]rune, length: c.int) -> [^]byte --- // Load UTF-8 text encoded from codepoints array + UnloadUTF8 :: proc(text: [^]byte) --- // Unload UTF-8 text encoded from codepoints array + LoadCodepoints :: proc(text: rawptr, count: ^c.int) -> [^]rune --- // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter + UnloadCodepoints :: proc(codepoints: [^]rune) --- // Unload codepoints data from memory + GetCodepointCount :: proc(text : cstring) -> c.int --- // Get total number of codepoints in a UTF-8 encoded string + GetCodepoint :: proc(text: cstring, codepointSize: ^c.int) -> rune --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure + GetCodepointNext :: proc(text: cstring, codepointSize: ^c.int) -> rune --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure + GetCodepointPrevious :: proc(text: cstring, codepointSize: ^c.int) -> rune --- // Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure + CodepointToUTF8 :: proc(codepoint: rune, utf8Size: ^c.int) -> cstring --- // Encode one codepoint into UTF-8 byte array (array length returned as parameter) - // Text strings management functions (no utf8 strings, only byte chars) + // Text strings management functions (no UTF-8 strings, only byte chars) // NOTE: Some strings allocate memory internally for returned strings, just be careful! - TextCopy :: proc(dst: [^]u8, src: cstring) -> c.int --- // Copy one string to another, returns bytes copied - TextIsEqual :: proc(text1, text2: cstring) -> bool --- // Check if two text string are equal - TextLength :: proc(text: cstring) -> c.uint --- // Get text length, checks for '\0' ending - TextSubtext :: proc(text: cstring, position: c.int, length: c.int) -> cstring --- // Get a piece of a text string - TextReplace :: proc(text: [^]byte, replace, by: cstring) -> cstring --- // Replace text string (memory must be freed!) - TextInsert :: proc(text, insert: cstring, position: c.int) -> cstring --- // Insert text in a position (memory must be freed!) - TextJoin :: proc(textList: [^]cstring, count: c.int, delimiter: cstring) -> cstring --- // Join text strings with delimiter - TextSplit :: proc(text: cstring, delimiter: byte, count: ^c.int) -> [^]cstring --- // Split text into multiple strings - TextAppend :: proc(text: [^]byte, append: cstring, position: ^c.int) --- // Append text at specific position and move cursor! - TextFindIndex :: proc(text: cstring, find: cstring) -> c.int --- // Find first text occurrence within a string - TextToUpper :: proc(text: cstring) -> cstring --- // Get upper case version of provided string - TextToLower :: proc(text: cstring) -> cstring --- // Get lower case version of provided string - TextToPascal :: proc(text: cstring) -> cstring --- // Get Pascal case notation version of provided string - TextToInteger :: proc(text: cstring) -> c.int --- // Get integer value from text (negative values not supported) + + TextCopy :: proc(dst: [^]byte, src: cstring) -> c.int --- // Copy one string to another, returns bytes copied + TextIsEqual :: proc(text1, text2: cstring) -> bool --- // Check if two text string are equal + TextLength :: proc(text: cstring) -> c.uint --- // Get text length, checks for '\0' ending + + // TextFormat is defined at the bottom of this file + + TextSubtext :: proc(text: cstring, position: c.int, length: c.int) -> cstring --- // Get a piece of a text string + TextReplace :: proc(text: [^]byte, replace, by: cstring) -> [^]byte --- // Replace text string (WARNING: memory must be freed!) + TextInsert :: proc(text, insert: cstring, position: c.int) -> [^]byte --- // Insert text in a position (WARNING: memory must be freed!) + TextJoin :: proc(textList: [^]cstring, count: c.int, delimiter: cstring) -> cstring --- // Join text strings with delimiter + TextSplit :: proc(text: cstring, delimiter: byte, count: ^c.int) -> [^]cstring --- // Split text into multiple strings + TextAppend :: proc(text: [^]byte, append: cstring, position: ^c.int) --- // Append text at specific position and move cursor! + TextFindIndex :: proc(text, find: cstring) -> c.int --- // Find first text occurrence within a string + TextToUpper :: proc(text: cstring) -> cstring --- // Get upper case version of provided string + TextToLower :: proc(text: cstring) -> cstring --- // Get lower case version of provided string + TextToPascal :: proc(text: cstring) -> cstring --- // Get Pascal case notation version of provided string + TextToInteger :: proc(text: cstring) -> c.int --- // Get integer value from text (negative values not supported) + //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) //------------------------------------------------------------------------------------ // Basic geometric 3D shapes drawing functions - DrawLine3D :: proc(startPos, endPos: Vector3, color: Color) --- // Draw a line in 3D world space - DrawPoint3D :: proc(position: Vector3, color: Color) --- // Draw a point in 3D space, actually a small line - DrawCircle3D :: proc(center: Vector3, radius: f32, rotationAxis: Vector3, rotationAngle: f32, color: Color) --- // Draw a circle in 3D world space - DrawTriangle3D :: proc(v1, v2, v3: Vector3, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!) - DrawTriangleStrip3D :: proc(points: [^]Vector3, pointsCount: c.int, color: Color) --- // Draw a triangle strip defined by points - DrawCube :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube - DrawCubeV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube (Vector version) - DrawCubeWires :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube wires - DrawCubeWiresV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube wires (Vector version) - DrawCubeTexture :: proc(texture: Texture2D, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube textured - DrawCubeTextureRec :: proc(texture: Texture2D, source: Rectangle, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube with a region of a texture - DrawSphere :: proc(centerPos: Vector3, radius: f32, color: Color) --- // Draw sphere - DrawSphereEx :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere with extended parameters - DrawSphereWires :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere wires - DrawCylinder :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone - DrawCylinderEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder with base at startPos and top at endPos - DrawCylinderWires :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires - DrawCylinderWiresEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder wires with base at startPos and top at endPos - DrawPlane :: proc(centerPos: Vector3, size: Vector2, color: Color) --- // Draw a plane XZ - DrawRay :: proc(ray: Ray, color: Color) --- // Draw a ray line - DrawGrid :: proc(slices: c.int, spacing: f32) --- // Draw a grid (centered at (0, 0, 0)) + + DrawLine3D :: proc(startPos, endPos: Vector3, color: Color) --- // Draw a line in 3D world space + DrawPoint3D :: proc(position: Vector3, color: Color) --- // Draw a point in 3D space, actually a small line + DrawCircle3D :: proc(center: Vector3, radius: f32, rotationAxis: Vector3, rotationAngle: f32, color: Color) --- // Draw a circle in 3D world space + DrawTriangle3D :: proc(v1, v2, v3: Vector3, color: Color) --- // Draw a color-filled triangle (vertex in counter-clockwise order!) + DrawTriangleStrip3D :: proc(points: [^]Vector3, pointCount: c.int, color: Color) --- // Draw a triangle strip defined by points + DrawCube :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube + DrawCubeV :: proc(position: Vector3, size: Vector3, color: Color) --- // Draw cube (Vector version) + DrawCubeWires :: proc(position: Vector3, width, height, length: f32, color: Color) --- // Draw cube wires + DrawCubeWiresV :: proc(position, size: Vector3, color: Color) --- // Draw cube wires (Vector version) + DrawSphere :: proc(centerPos: Vector3, radius: f32, color: Color) --- // Draw sphere + DrawSphereEx :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere with extended parameters + DrawSphereWires :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) --- // Draw sphere wires + DrawCylinder :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone + DrawCylinderEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder with base at startPos and top at endPos + DrawCylinderWires :: proc(position: Vector3, radiusTop, radiusBottom, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires + DrawCylinderWiresEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) --- // Draw a cylinder wires with base at startPos and top at endPos + DrawCapsule :: proc(startPos, endPos: Vector3, radius: f32, slices, rings: c.int, color: Color) --- // Draw a capsule with the center of its sphere caps at startPos and endPos + DrawCapsuleWires :: proc(startPos, endPos: Vector3, radius: f32, slices, rings: c.int, color: Color) --- // Draw capsule wireframe with the center of its sphere caps at startPos and endPos + DrawPlane :: proc(centerPos: Vector3, size: Vector2, color: Color) --- // Draw a plane XZ + DrawRay :: proc(ray: Ray, color: Color) --- // Draw a ray line + DrawGrid :: proc(slices: c.int, spacing: f32) --- // Draw a grid (centered at (0, 0, 0)) //------------------------------------------------------------------------------------ // Model 3d Loading and Drawing Functions (Module: models) //------------------------------------------------------------------------------------ - // Model loading/unloading functions - LoadModel :: proc(fileName: cstring) -> Model --- // Load model from files (meshes and materials) - LoadModelFromMesh :: proc(mesh: Mesh) -> Model --- // Load model from generated mesh (default material) - UnloadModel :: proc(model: Model) --- // Unload model (including meshes) from memory (RAM and/or VRAM) - UnloadModelKeepMeshes :: proc(model: Model) --- // Unload model (but not meshes) from memory (RAM and/or VRAM) - GetModelBoundingBox :: proc(model: Model) -> BoundingBox --- // Compute model bounding box limits (considers all meshes) + // Model management functions + + LoadModel :: proc(fileName: cstring) -> Model --- // Load model from files (meshes and materials) + LoadModelFromMesh :: proc(mesh: Mesh) -> Model --- // Load model from generated mesh (default material) + IsModelReady :: proc(model: Model) -> bool --- // Check if a model is ready + UnloadModel :: proc(model: Model) --- // Unload model (including meshes) from memory (RAM and/or VRAM) + GetModelBoundingBox :: proc(model: Model) -> BoundingBox --- // Compute model bounding box limits (considers all meshes) // Model drawing functions - DrawModel :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model (with texture if set) - DrawModelEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters - DrawModelWires :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model wires (with texture if set) - DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters - DrawBoundingBox :: proc(box: BoundingBox, color: Color) --- // Draw bounding box (wires) - DrawBillboard :: proc(camera: Camera, texture: Texture2D, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture - DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, center: Vector3, size: f32, tint: Color) --- // Draw a billboard texture defined by source - DrawBillboardPro :: proc(camera: Camera, texture: Texture2D, source: Rectangle, - position, up: Vector3, size, origin: Vector2, rotation: f32, tint: Color) --- // Draw a billboard texture defined by source and rotation + + DrawModel :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model (with texture if set) + DrawModelEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters + DrawModelWires :: proc(model: Model, position: Vector3, scale: f32, tint: Color) --- // Draw a model wires (with texture if set) + DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters + DrawBoundingBox :: proc(box: BoundingBox, color: Color) --- // Draw bounding box (wires) + DrawBillboard :: proc(camera: Camera, texture: Texture2D, position: Vector3, size: f32, tint: Color) --- // Draw a billboard texture + DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, size: Vector2, tint: Color) --- // Draw a billboard texture defined by source + DrawBillboardPro :: proc(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: f32, tint: Color) --- // Draw a billboard texture defined by source and rotation // Mesh management functions - UploadMesh :: proc(mesh: ^Mesh, is_dynamic: bool) --- // Upload mesh vertex data in GPU and provide VAO/VBO ids - UpdateMeshBuffer :: proc(mesh: Mesh, index: c.int, data: rawptr, dataSize: c.int, offset: c.int) --- // Update mesh vertex data in GPU for a specific buffer index - UnloadMesh :: proc(mesh: Mesh) --- // Unload mesh data from CPU and GPU - DrawMesh :: proc(mesh: Mesh, material: Material, transform: Matrix) --- // Draw a 3d mesh with material and transform - DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms - ExportMesh :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh data to file, returns true on success - GetMeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox --- // Compute mesh bounding box limits - GenMeshTangents :: proc(mesh: ^Mesh) --- // Compute mesh tangents - GenMeshBinormals :: proc(mesh: ^Mesh) --- - + + UploadMesh :: proc(mesh: ^Mesh, is_dynamic: bool) --- // Upload mesh vertex data in GPU and provide VAO/VBO ids + UpdateMeshBuffer :: proc(mesh: Mesh, index: c.int, data: rawptr, dataSize: c.int, offset: c.int) --- // Update mesh vertex data in GPU for a specific buffer index + UnloadMesh :: proc(mesh: Mesh) --- // Unload mesh data from CPU and GPU + DrawMesh :: proc(mesh: Mesh, material: Material, transform: Matrix) --- // Draw a 3d mesh with material and transform + DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms + ExportMesh :: proc(mesh: Mesh, fileName: cstring) -> bool --- // Export mesh data to file, returns true on success + GetMeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox --- // Compute mesh bounding box limits + GenMeshTangents :: proc(mesh: ^Mesh) --- // Compute mesh tangents + // Mesh generation functions - GenMeshPoly :: proc(sides: c.int, radius: f32) -> Mesh --- // Generate polygonal mesh - GenMeshPlane :: proc(width, length: f32, resX, resZ: c.int) -> Mesh --- // Generate plane mesh (with subdivisions) - GenMeshCube :: proc(width, height, length: f32) -> Mesh --- // Generate cuboid mesh - GenMeshSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate sphere mesh (standard sphere) - GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate half-sphere mesh (no bottom cap) - GenMeshCylinder :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cylinder mesh - GenMeshCone :: proc(radius: f32, height: f32, slices: c.int) -> Mesh --- // Generate cone/pyramid mesh - GenMeshTorus :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh - GenMeshKnot :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh - GenMeshHeightmap :: proc(heightmap: Image, size: Vector3) -> Mesh --- // Generate heightmap mesh from image data - GenMeshCubicmap :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh --- // Generate cubes-based map mesh from image data + + GenMeshPoly :: proc(sides: c.int, radius: f32) -> Mesh --- // Generate polygonal mesh + GenMeshPlane :: proc(width, lengthL: f32, resX, resZ: c.int) -> Mesh --- // Generate plane mesh (with subdivisions) + GenMeshCube :: proc(width, height, length: f32) -> Mesh --- // Generate cuboid mesh + GenMeshSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate sphere mesh (standard sphere) + GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh --- // Generate half-sphere mesh (no bottom cap) + GenMeshCylinder :: proc(radius, height: f32, slices: c.int) -> Mesh --- // Generate cylinder mesh + GenMeshCone :: proc(radius, height: f32, slices: c.int) -> Mesh --- // Generate cone/pyramid mesh + GenMeshTorus :: proc(radius, size: f32, radSeg, sides: c.int) -> Mesh --- // Generate torus mesh + GenMeshKnot :: proc(radius, size: f32, radSeg, sides: c.int) -> Mesh --- // Generate trefoil knot mesh + GenMeshHeightmap :: proc(heightmap: Image, size: Vector3) -> Mesh --- // Generate heightmap mesh from image data + GenMeshCubicmap :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh --- // Generate cubes-based map mesh from image data // Material loading/unloading functions - LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> [^]Material --- // Load materials from model file - LoadMaterialDefault :: proc() -> Material --- // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) - UnloadMaterial :: proc(material: Material) --- // Unload material from GPU memory (VRAM) - SetMaterialTexture :: proc(material: ^Material, mapType: MaterialMapIndex, texture: Texture2D) --- // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) - SetModelMeshMaterial :: proc(model: ^Model, meshId: c.int, materialId: c.int) --- // Set material for a mesh + + LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> [^]Material --- // Load materials from model file + LoadMaterialDefault :: proc() -> Material --- // Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) + IsMaterialReady :: proc(material: Material) -> bool --- // Check if a material is ready + UnloadMaterial :: proc(material: Material) --- // Unload material from GPU memory (VRAM) + SetMaterialTexture :: proc(material: ^Material, mapType: MaterialMapIndex, texture: Texture2D) --- // Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) + SetModelMeshMaterial :: proc(model: ^Model, meshId: c.int, materialId: c.int) --- // Set material for a mesh // Model animations loading/unloading functions - LoadModelAnimations :: proc(fileName: cstring, animsCount: ^c.int) -> [^]ModelAnimation --- // Load model animations from file - UpdateModelAnimation :: proc(model: Model, anim: ModelAnimation, frame: c.int) --- // Update model animation pose - UnloadModelAnimation :: proc(anim: ModelAnimation) --- // Unload animation data - UnloadModelAnimations :: proc(animations: [^]ModelAnimation, count: c.uint) --- // Unload animation array data - IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool --- // Check model animation skeleton match - + + LoadModelAnimations :: proc(fileName: cstring, animCount: ^c.uint) -> [^]ModelAnimation --- // Load model animations from file + UpdateModelAnimation :: proc(model: Model, anim: ModelAnimation, frame: c.int) --- // Update model animation pose + UnloadModelAnimation :: proc(anim: ModelAnimation) --- // Unload animation data + UnloadModelAnimations :: proc(animations: [^]ModelAnimation, count: c.uint) --- // Unload animation array data + IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool --- // Check model animation skeleton match + // Collision detection functions + CheckCollisionSpheres :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool --- // Check collision between two spheres CheckCollisionBoxes :: proc(box1, box2: BoundingBox) -> bool --- // Check collision between two bounding boxes CheckCollisionBoxSphere :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool --- // Check collision between box and sphere - GetRayCollisionSphere :: proc(ray: Ray, center: Vector3, radius: f32) -> RayCollision --- // Get collision info between ray and sphere - GetRayCollisionBox :: proc(ray: Ray, box: BoundingBox) -> RayCollision --- // Get collision info between ray and box - GetRayCollisionModel :: proc(ray: Ray, model: Model) -> RayCollision --- // Get collision info between ray and model - GetRayCollisionMesh :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayCollision --- // Get collision info between ray and mesh - GetRayCollisionTriangle :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayCollision --- // Get collision info between ray and triangle - GetRayCollisionQuad :: proc(ray: Ray, p1, p2, p3, p4: Vector3) -> RayCollision --- // Get collision info between ray and quad + GetRayCollisionSphere :: proc(ray: Ray, center: Vector3, radius: f32) -> RayCollision --- // Get collision info between ray and sphere + GetRayCollisionBox :: proc(ray: Ray, box: BoundingBox) -> RayCollision --- // Get collision info between ray and box + GetRayCollisionMesh :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayCollision --- // Get collision info between ray and mesh + GetRayCollisionTriangle :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayCollision --- // Get collision info between ray and triangle + GetRayCollisionQuad :: proc(ray: Ray, p1, p2, p3, p4: Vector3) -> RayCollision --- // Get collision info between ray and quad //------------------------------------------------------------------------------------ // Audio Loading and Playing Functions (Module: audio) //------------------------------------------------------------------------------------ // Audio device management functions - InitAudioDevice :: proc() --- // Initialize audio device and context - CloseAudioDevice :: proc() --- // Close the audio device and context - IsAudioDeviceReady :: proc() -> bool --- // Check if audio device has been initialized successfully - SetMasterVolume :: proc(volume: f32) --- // Set master volume (listener) + + InitAudioDevice :: proc() --- // Initialize audio device and context + CloseAudioDevice :: proc() --- // Close the audio device and context + IsAudioDeviceReady :: proc() -> bool --- // Check if audio device has been initialized successfully + SetMasterVolume :: proc(volume: f32) --- // Set master volume (listener) // Wave/Sound loading/unloading functions - LoadWave :: proc(fileName: cstring) -> Wave --- // Load wave data from file - LoadWaveFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Wave --- // Load wave from memory buffer, fileType refers to extension: i.e. ".wav" - LoadSound :: proc(fileName: cstring) -> Sound --- // Load sound from file - LoadSoundFromWave :: proc(wave: Wave) -> Sound --- // Load sound from wave data - UpdateSound :: proc(sound: Sound, data: rawptr, samplesCount: c.int) --- // Update sound buffer with new data - UnloadWave :: proc(wave: Wave) --- // Unload wave data - UnloadSound :: proc(sound: Sound) --- // Unload sound - ExportWave :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave data to file, returns true on success - ExportWaveAsCode :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave sample data to code (.h), returns true on success + + LoadWave :: proc(fileName: cstring) -> Wave --- // Load wave data from file + LoadWaveFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Wave --- // Load wave from memory buffer, fileType refers to extension: i.e. '.wav' + IsWaveReady :: proc(wave: Wave) -> bool --- // Checks if wave data is ready + LoadSound :: proc(fileName: cstring) -> Sound --- // Load sound from file + LoadSoundFromWave :: proc(wave: Wave) -> Sound --- // Load sound from wave data + IsSoundReady :: proc(sound: Sound) -> bool --- // Checks if a sound is ready + UpdateSound :: proc(sound: Sound, data: rawptr, sampleCount: c.int) --- // Update sound buffer with new data + UnloadWave :: proc(wave: Wave) --- // Unload wave data + UnloadSound :: proc(sound: Sound) --- // Unload sound + ExportWave :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave data to file, returns true on success + ExportWaveAsCode :: proc(wave: Wave, fileName: cstring) -> bool --- // Export wave sample data to code (.h), returns true on success // Wave/Sound management functions + PlaySound :: proc(sound: Sound) --- // Play a sound StopSound :: proc(sound: Sound) --- // Stop playing a sound PauseSound :: proc(sound: Sound) --- // Pause a sound ResumeSound :: proc(sound: Sound) --- // Resume a paused sound - PlaySoundMulti :: proc(sound: Sound) --- // Play a sound (using multichannel buffer pool) - StopSoundMulti :: proc() --- // Stop any sound playing (using multichannel buffer pool) - GetSoundsPlaying :: proc() -> c.int --- // Get number of sounds playing in the multichannel IsSoundPlaying :: proc(sound: Sound) -> bool --- // Check if a sound is currently playing SetSoundVolume :: proc(sound: Sound, volume: f32) --- // Set volume for a sound (1.0 is max level) SetSoundPitch :: proc(sound: Sound, pitch: f32) --- // Set pitch for a sound (1.0 is base level) - WaveFormat :: proc(wave: ^Wave, sampleRate, sampleSize: c.int, channels: c.int) --- // Convert wave data to desired format + SetSoundPan :: proc(sound: Sound, pan: f32) --- // Set pan for a sound (0.5 is center) WaveCopy :: proc(wave: Wave) -> Wave --- // Copy a wave to a new wave WaveCrop :: proc(wave: ^Wave, initSample, finalSample: c.int) --- // Crop a wave to defined samples range - LoadWaveSamples :: proc(wave: Wave) -> [^]f32 --- // Load samples data from wave as a floats array + WaveFormat :: proc(wave: ^Wave, sampleRate, sampleSize: c.int, channels: c.int) --- // Convert wave data to desired format + LoadWaveSamples :: proc(wave: Wave) -> [^]f32 --- // Load samples data from wave as a 32bit float data array UnloadWaveSamples :: proc(samples: [^]f32) --- // Unload samples data loaded with LoadWaveSamples() // Music management functions + LoadMusicStream :: proc(fileName: cstring) -> Music --- // Load music stream from file LoadMusicStreamFromMemory :: proc(fileType: cstring, data: rawptr, dataSize: c.int) -> Music --- // Load music stream from data + IsMusicReady :: proc(music: Music) -> bool --- // Checks if a music stream is ready UnloadMusicStream :: proc(music: Music) --- // Unload music stream PlayMusicStream :: proc(music: Music) --- // Start music playing IsMusicStreamPlaying :: proc(music: Music) -> bool --- // Check if music is playing @@ -1513,22 +1570,33 @@ foreign lib { SeekMusicStream :: proc(music: Music, position: f32) --- // Seek music to a position (in seconds) SetMusicVolume :: proc(music: Music, volume: f32) --- // Set volume for music (1.0 is max level) SetMusicPitch :: proc(music: Music, pitch: f32) --- // Set pitch for a music (1.0 is base level) + SetMusicPan :: proc(music: Music, pan: f32) --- // Set pan for a music (0.5 is center) GetMusicTimeLength :: proc(music: Music) -> f32 --- // Get music time length (in seconds) GetMusicTimePlayed :: proc(music: Music) -> f32 --- // Get current music time played (in seconds) // AudioStream management functions - LoadAudioStream :: proc(sampleRate, sampleSize, channels: c.uint) -> AudioStream --- // Load audio stream (to stream raw audio pcm data) - UnloadAudioStream :: proc(stream: AudioStream) --- // Unload audio stream and free memory - UpdateAudioStream :: proc(stream: AudioStream, data: rawptr, frameCount: c.int) --- // Update audio stream buffers with data - IsAudioStreamProcessed :: proc(stream: AudioStream) -> bool --- // Check if any audio stream buffers requires refill - PlayAudioStream :: proc(stream: AudioStream) --- // Play audio stream - PauseAudioStream :: proc(stream: AudioStream) --- // Pause audio stream - ResumeAudioStream :: proc(stream: AudioStream) --- // Resume audio stream - IsAudioStreamPlaying :: proc(stream: AudioStream) -> bool --- // Check if audio stream is playing - StopAudioStream :: proc(stream: AudioStream) --- // Stop audio stream - SetAudioStreamVolume :: proc(stream: AudioStream, volume: f32) --- // Set volume for audio stream (1.0 is max level) - SetAudioStreamPitch :: proc(stream: AudioStream, pitch: f32) --- // Set pitch for audio stream (1.0 is base level) - SetAudioStreamBufferSizeDefault :: proc(size: c.int) --- // Default size for new audio streams + + LoadAudioStream :: proc(sampleRate, sampleSize: c.uint, channels: c.uint) -> AudioStream --- // Load audio stream (to stream raw audio pcm data) + IsAudioStreamReady :: proc(stream: AudioStream) -> bool --- // Checks if an audio stream is ready + UnloadAudioStream :: proc(stream: AudioStream) --- // Unload audio stream and free memory + UpdateAudioStream :: proc(stream: AudioStream, data: rawptr, frameCount: c.int) --- // Update audio stream buffers with data + IsAudioStreamProcessed :: proc(stream: AudioStream) -> bool --- // Check if any audio stream buffers requires refill + PlayAudioStream :: proc(stream: AudioStream) --- // Play audio stream + PauseAudioStream :: proc(stream: AudioStream) --- // Pause audio stream + ResumeAudioStream :: proc(stream: AudioStream) --- // Resume audio stream + IsAudioStreamPlaying :: proc(stream: AudioStream) -> bool --- // Check if audio stream is playing + StopAudioStream :: proc(stream: AudioStream) --- // Stop audio stream + SetAudioStreamVolume :: proc(stream: AudioStream, volume: f32) --- // Set volume for audio stream (1.0 is max level) + SetAudioStreamPitch :: proc(stream: AudioStream, pitch: f32) --- // Set pitch for audio stream (1.0 is base level) + SetAudioStreamPan :: proc(stream: AudioStream, pan: f32) --- // Set pan for audio stream (0.5 is centered) + SetAudioStreamBufferSizeDefault :: proc(size: c.int) --- // Default size for new audio streams + SetAudioStreamCallback :: proc(stream: AudioStream, callback: AudioCallback) --- // Audio thread callback to request new data + + AttachAudioStreamProcessor :: proc(stream: AudioStream, processor: AudioCallback) --- // Attach audio stream processor to stream + DetachAudioStreamProcessor :: proc(stream: AudioStream, processor: AudioCallback) --- // Detach audio stream processor from stream + + AttachAudioMixedProcessor :: proc(processor: AudioCallback) --- // Attach audio stream processor to the entire audio pipeline + DetachAudioMixedProcessor :: proc(processor: AudioCallback) --- // Detach audio stream processor from the entire audio pipeline } @@ -1565,7 +1633,7 @@ MemAllocatorProc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, old_memory: rawptr, old_size: int, location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) { switch mode { case .Alloc, .Alloc_Non_Zeroed: - ptr := MemAlloc(c.int(size)) + ptr := MemAlloc(c.uint(size)) if ptr == nil { err = .Out_Of_Memory return @@ -1577,7 +1645,7 @@ MemAllocatorProc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, return nil, nil case .Resize: - ptr := MemRealloc(old_memory, c.int(size)) + ptr := MemRealloc(old_memory, c.uint(size)) if ptr == nil { err = .Out_Of_Memory return diff --git a/vendor/raylib/rlgl.odin b/vendor/raylib/rlgl.odin index 7e7f2feea..5ef340e3f 100644 --- a/vendor/raylib/rlgl.odin +++ b/vendor/raylib/rlgl.odin @@ -1,10 +1,116 @@ +/********************************************************************************************** +* +* rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API +* +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* initialized on rlglInit() to accumulate vertex data. +* +* When an internal state change is required all the stored vertex data is renderer in batch, +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. +* +* Some additional resources are also loaded for convenience, here the complete list: +* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data +* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 +* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) +* +* Internal buffer (and additional resources) must be manually unloaded calling rlglClose(). +* +* +* CONFIGURATION: +* +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it +* +* #define RLGL_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version +* +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init +* +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) +* +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): +* +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits +* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* +* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance +* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* +* When loading a shader, the following vertex attribute and uniform +* location names are tried to be set automatically: +* +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +* +* DEPENDENCIES: +* +* - OpenGL libraries (depending on platform and OpenGL version selected) +* - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + + package raylib import "core:c" when ODIN_OS == .Windows { foreign import lib { - "raylib.lib", + "windows/raylib.lib", "system:Winmm.lib", "system:Gdi32.lib", "system:User32.lib", @@ -13,37 +119,51 @@ when ODIN_OS == .Windows { } else when ODIN_OS == .Linux { foreign import lib "linux/libraylib.a" } else when ODIN_OS == .Darwin { - foreign import lib "macos/libraylib.a" + when ODIN_ARCH == .arm64 { + foreign import lib { + "macos-arm64/libraylib.a", + "system:Cocoa.framework", + "system:OpenGL.framework", + "system:IOKit.framework", + } + } else { + foreign import lib { + "macos/libraylib.a", + "system:Cocoa.framework", + "system:OpenGL.framework", + "system:IOKit.framework", + } + } } else { foreign import lib "system:raylib" } -GRAPHICS_API_OPENGL_11 :: false -GRAPHICS_API_OPENGL_21 :: true -GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21 -GRAPHICS_API_OPENGL_ES2 :: false -GRAPHICS_API_OPENGL_43 :: false +RL_GRAPHICS_API_OPENGL_11 :: false +RL_GRAPHICS_API_OPENGL_21 :: true +RL_GRAPHICS_API_OPENGL_33 :: RL_GRAPHICS_API_OPENGL_21 // default currently +RL_GRAPHICS_API_OPENGL_ES2 :: false +RL_GRAPHICS_API_OPENGL_43 :: false -when !GRAPHICS_API_OPENGL_ES2 { +when !RL_GRAPHICS_API_OPENGL_ES2 { // This is the maximum amount of elements (quads) per batch // NOTE: Be careful with text, every letter maps to a quad - DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192 + RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 8192 } else { // We reduce memory sizes for embedded systems (RPI and HTML5) // NOTE: On HTML5 (emscripten) this is allocated on heap, // by default it's only 16MB!...just take care... - DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048 + RL_DEFAULT_BATCH_BUFFER_ELEMENTS :: 2048 } -DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering) -DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture) -MAX_BATCH_ACTIVE_TEXTURES :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture()) +RL_DEFAULT_BATCH_BUFFERS :: 1 // Default number of batch buffers (multi-buffering) +RL_DEFAULT_BATCH_DRAWCALLS :: 256 // Default number of batch draw calls (by state changes: mode, texture) +RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS :: 4 // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture()) // Internal Matrix stack -MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack +RL_MAX_MATRIX_STACK_SIZE :: 32 // Maximum size of Matrix stack // Shader limits -MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported +RL_MAX_SHADER_LOCATIONS :: 32 // Maximum number of shader locations supported // Projection matrix culling RL_CULL_DISTANCE_NEAR :: 0.01 // Default near cull distance @@ -98,87 +218,129 @@ RL_FRAGMENT_SHADER :: 0x8B30 // GL_FRAGMENT_SHADER RL_VERTEX_SHADER :: 0x8B31 // GL_VERTEX_SHADER RL_COMPUTE_SHADER :: 0x91B9 // GL_COMPUTE_SHADER +// GL blending factors +RL_ZERO :: 0 // GL_ZERO +RL_ONE :: 1 // GL_ONE +RL_SRC_COLOR :: 0x0300 // GL_SRC_COLOR +RL_ONE_MINUS_SRC_COLOR :: 0x0301 // GL_ONE_MINUS_SRC_COLOR +RL_SRC_ALPHA :: 0x0302 // GL_SRC_ALPHA +RL_ONE_MINUS_SRC_ALPHA :: 0x0303 // GL_ONE_MINUS_SRC_ALPHA +RL_DST_ALPHA :: 0x0304 // GL_DST_ALPHA +RL_ONE_MINUS_DST_ALPHA :: 0x0305 // GL_ONE_MINUS_DST_ALPHA +RL_DST_COLOR :: 0x0306 // GL_DST_COLOR +RL_ONE_MINUS_DST_COLOR :: 0x0307 // GL_ONE_MINUS_DST_COLOR +RL_SRC_ALPHA_SATURATE :: 0x0308 // GL_SRC_ALPHA_SATURATE +RL_CONSTANT_COLOR :: 0x8001 // GL_CONSTANT_COLOR +RL_ONE_MINUS_CONSTANT_COLOR :: 0x8002 // GL_ONE_MINUS_CONSTANT_COLOR +RL_CONSTANT_ALPHA :: 0x8003 // GL_CONSTANT_ALPHA +RL_ONE_MINUS_CONSTANT_ALPHA :: 0x8004 // GL_ONE_MINUS_CONSTANT_ALPHA + +// GL blending functions/equations +RL_FUNC_ADD :: 0x8006 // GL_FUNC_ADD +RL_MIN :: 0x8007 // GL_MIN +RL_MAX :: 0x8008 // GL_MAX +RL_FUNC_SUBTRACT :: 0x800A // GL_FUNC_SUBTRACT +RL_FUNC_REVERSE_SUBTRACT :: 0x800B // GL_FUNC_REVERSE_SUBTRACT +RL_BLEND_EQUATION :: 0x8009 // GL_BLEND_EQUATION +RL_BLEND_EQUATION_RGB :: 0x8009 // GL_BLEND_EQUATION_RGB // (Same as BLEND_EQUATION) +RL_BLEND_EQUATION_ALPHA :: 0x883D // GL_BLEND_EQUATION_ALPHA +RL_BLEND_DST_RGB :: 0x80C8 // GL_BLEND_DST_RGB +RL_BLEND_SRC_RGB :: 0x80C9 // GL_BLEND_SRC_RGB +RL_BLEND_DST_ALPHA :: 0x80CA // GL_BLEND_DST_ALPHA +RL_BLEND_SRC_ALPHA :: 0x80CB // GL_BLEND_SRC_ALPHA +RL_BLEND_COLOR :: 0x8005 // GL_BLEND_COLOR + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -GlVersion :: enum c.int { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } -FramebufferAttachType :: enum c. int { - COLOR_CHANNEL0 = 0, - COLOR_CHANNEL1, - COLOR_CHANNEL2, - COLOR_CHANNEL3, - COLOR_CHANNEL4, - COLOR_CHANNEL5, - COLOR_CHANNEL6, - COLOR_CHANNEL7, - DEPTH = 100, - STENCIL = 200, -} -FramebufferAttachTextureType :: enum c.int { - CUBEMAP_POSITIVE_X = 0, - CUBEMAP_NEGATIVE_X, - CUBEMAP_POSITIVE_Y, - CUBEMAP_NEGATIVE_Y, - CUBEMAP_POSITIVE_Z, - CUBEMAP_NEGATIVE_Z, - TEXTURE2D = 100, - RENDERBUFFER = 200, -} - -VertexBufferIndexType :: c.ushort when GRAPHICS_API_OPENGL_ES2 else c.uint +VertexBufferIndexType :: c.ushort when RL_GRAPHICS_API_OPENGL_ES2 else c.uint // Dynamic vertex buffers (position + texcoords + colors + indices arrays) VertexBuffer :: struct { - elementsCount: c.int, // Number of elements in the buffer (QUADS) + elementCount: c.int, // Number of elements in the buffer (QUADS) - vCounter: c.int, // Vertex position counter to process (and draw) from full buffer - tcCounter: c.int, // Vertex texcoord counter to process (and draw) from full buffer - cCounter: c.int, // Vertex color counter to process (and draw) from full buffer - - vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) - texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) - colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) - indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad) - vaoId: u32, // OpenGL Vertex Array Object id - vboId: [4]u32, // OpenGL Vertex Buffer Objects id (4 types of vertex data) -} + vertices: [^]f32, // Vertex position (XYZ - 3 components per vertex) (shader-location = 0) + texcoords: [^]f32, // Vertex texture coordinates (UV - 2 components per vertex) (shader-location = 1) + colors: [^]u8, // Vertex colors (RGBA - 4 components per vertex) (shader-location = 3) + indices: [^]VertexBufferIndexType, // Vertex indices (in case vertex data comes indexed) (6 indices per quad) + vaoId: c.uint, // OpenGL Vertex Array Object id + vboId: [4]c.uint, // OpenGL Vertex Buffer Objects id (4 types of vertex data) +} // Draw call type // NOTE: Only texture changes register a new draw, other state-change-related elements are not // used at this moment (vaoId, shaderId, matrices), raylib just forces a batch draw call if any // of those state-change happens (this is done in core module) DrawCall :: struct { - mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS - vertexCount: c.int, // Number of vertex of the draw - vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES) - //vaoId: u32, // Vertex array id to be used on the draw -> Using RLGL.currentBatch->vertexBuffer.vaoId - //shaderId: u32, // Shader id to be used on the draw -> Using RLGL.currentShader.id - textureId: u32, // Texture id to be used on the draw -> Use to create new draw call if changes - - //projection: Matrix, // Projection matrix for this draw -> Using RLGL.projection by default - //modelview: Matrix, // Modelview matrix for this draw -> Using RLGL.modelview by default -} + mode: c.int, // Drawing mode: LINES, TRIANGLES, QUADS + vertexCount: c.int, // Number of vertex of the draw + vertexAlignment: c.int, // Number of vertex required for index alignment (LINES, TRIANGLES) + textureId: c.uint, // Texture id to be used on the draw -> Use to create new draw call if changes +} // RenderBatch type RenderBatch :: struct { - buffersCount: c.int, // Number of vertex buffers (multi-buffering support) + bufferCount: c.int, // Number of vertex buffers (multi-buffering support) currentBuffer: c.int, // Current buffer tracking in case of multi-buffering vertexBuffer: [^]VertexBuffer, // Dynamic buffer(s) for vertex data - draws: [^]DrawCall, // Draw calls array, depends on textureId - drawsCounter: c.int, // Draw calls counter - currentDepth: f32, // Current depth value for next draw + draws: [^]DrawCall, // Draw calls array, depends on textureId + drawCounter: c.int, // Draw calls counter + currentDepth: f32, // Current depth value for next draw } + +// OpenGL version +GlVersion :: enum c.int { + OPENGL_11 = 1, // OpenGL 1.1 + OPENGL_21, // OpenGL 2.1 (GLSL 120) + OPENGL_33, // OpenGL 3.3 (GLSL 330) + OPENGL_43, // OpenGL 4.3 (using GLSL 330) + OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) +} + + // Shader attribute data types ShaderAttributeDataType :: enum c.int { - FLOAT = 0, - VEC2, - VEC3, - VEC4, + FLOAT = 0, // Shader attribute type: float + VEC2, // Shader attribute type: vec2 (2 float) + VEC3, // Shader attribute type: vec3 (3 float) + VEC4, // Shader attribute type: vec4 (4 float) +} + +// Framebuffer attachment type +// NOTE: By default up to 8 color channels defined, but it can be more +FramebufferAttachType :: enum c.int { + COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + COLOR_CHANNEL1, // Framebuffer attachment type: color 1 + COLOR_CHANNEL2, // Framebuffer attachment type: color 2 + COLOR_CHANNEL3, // Framebuffer attachment type: color 3 + COLOR_CHANNEL4, // Framebuffer attachment type: color 4 + COLOR_CHANNEL5, // Framebuffer attachment type: color 5 + COLOR_CHANNEL6, // Framebuffer attachment type: color 6 + COLOR_CHANNEL7, // Framebuffer attachment type: color 7 + DEPTH = 100, // Framebuffer attachment type: depth + STENCIL = 200, // Framebuffer attachment type: stencil +} + +// Framebuffer texture attachment type +FramebufferAttachTextureType :: enum c.int { + CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side + CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side + CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side + CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side + CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side + TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer +} + +CullMode :: enum c.int { + FRONT = 0, + BACK, } @(default_calling_convention="c") @@ -219,38 +381,38 @@ foreign lib { //------------------------------------------------------------------------------------ // Vertex buffers state - rlEnableVertexArray :: proc(vaoId: u32) -> bool --- // Enable vertex array (VAO, if supported) - rlDisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported) - rlEnableVertexBuffer :: proc(id: u32) --- // Enable vertex buffer (VBO) - rlDisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO) - rlEnableVertexBufferElement :: proc(id: u32) --- // Enable vertex buffer element (VBO element) - rlDisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element) - rlEnableVertexAttribute :: proc(index: u32) --- // Enable vertex attribute index - rlDisableVertexAttribute :: proc(index: u32) --- // Disable vertex attribute index - when GRAPHICS_API_OPENGL_11 { + rlEnableVertexArray :: proc(vaoId: c.uint) -> bool --- // Enable vertex array (VAO, if supported) + rlDisableVertexArray :: proc() --- // Disable vertex array (VAO, if supported) + rlEnableVertexBuffer :: proc(id: c.uint) --- // Enable vertex buffer (VBO) + rlDisableVertexBuffer :: proc() --- // Disable vertex buffer (VBO) + rlEnableVertexBufferElement :: proc(id: c.uint) --- // Enable vertex buffer element (VBO element) + rlDisableVertexBufferElement :: proc() --- // Disable vertex buffer element (VBO element) + rlEnableVertexAttribute :: proc(index: c.uint) --- // Enable vertex attribute index + rlDisableVertexAttribute :: proc(index: c.uint) --- // Disable vertex attribute index + when RL_GRAPHICS_API_OPENGL_11 { rlEnableStatePointer :: proc(vertexAttribType: c.int, buffer: rawptr) --- rlDisableStatePointer :: proc(vertexAttribType: c.int) --- } // Textures state - rlActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot - rlEnableTexture :: proc(id: u32) --- // Enable texture - rlDisableTexture :: proc() --- // Disable texture - rlEnableTextureCubemap :: proc(id: u32) --- // Enable texture cubemap - rlDisableTextureCubemap :: proc() --- // Disable texture cubemap - rlTextureParameters :: proc(id: u32, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) + rlActiveTextureSlot :: proc(slot: c.int) --- // Select and active a texture slot + rlEnableTexture :: proc(id: c.uint) --- // Enable texture + rlDisableTexture :: proc() --- // Disable texture + rlEnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap + rlDisableTextureCubemap :: proc() --- // Disable texture cubemap + rlTextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) + rlCubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) // Shader state - rlEnableShader :: proc(id: u32) --- // Enable shader program + rlEnableShader :: proc(id: c.uint) --- // Enable shader program rlDisableShader :: proc() --- // Disable shader program // Framebuffer state - rlEnableFramebuffer :: proc(id: u32) --- // Enable render texture (fbo) + rlEnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) rlDisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer rlActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers // General render state - rlEnableColorBlend :: proc() --- // Enable color blending rlDisableColorBlend :: proc() --- // Disable color blending rlEnableDepthTest :: proc() --- // Enable depth test rlDisableDepthTest :: proc() --- // Disable depth test @@ -258,6 +420,7 @@ foreign lib { rlDisableDepthMask :: proc() --- // Disable depth write rlEnableBackfaceCulling :: proc() --- // Enable backface culling rlDisableBackfaceCulling :: proc() --- // Disable backface culling + rlSetCullFace :: proc(mode: CullMode) --- // Set face culling mode rlEnableScissorTest :: proc() --- // Enable scissor test rlDisableScissorTest :: proc() --- // Disable scissor test rlScissor :: proc(x, y, width, height: c.int) --- // Scissor test @@ -271,102 +434,107 @@ foreign lib { rlDisableStereoRender :: proc() --- // Disable stereo rendering rlIsStereoRenderEnabled :: proc() -> bool --- // Check if stereo render is enabled - rlClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color - rlClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth) - rlCheckErrors :: proc() --- // Check and log OpenGL error codes - rlSetBlendMode :: proc(mode: c.int) --- // Set blending mode - rlSetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors) + + rlClearColor :: proc(r, g, b, a: u8) --- // Clear color buffer with color + rlClearScreenBuffers :: proc() --- // Clear used screen buffers (color and depth) + rlCheckErrors :: proc() --- // Check and log OpenGL error codes + rlSetBlendMode :: proc(mode: c.int) --- // Set blending mode + rlSetBlendFactors :: proc(glSrcFactor, glDstFactor, glEquation: c.int) --- // Set blending mode factor and equation (using OpenGL factors) + rlSetBlendFactorsSeparate :: proc(glSrcRGB, glDstRGB, glSrcAlpha, glDstAlpha, glEqRGB, glEqAlpha: c.int) --- // Set blending mode factors and equations separately (using OpenGL factors) //------------------------------------------------------------------------------------ // Functions Declaration - rlgl functionality //------------------------------------------------------------------------------------ // rlgl initialization functions - rlglInit :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states) - rlglClose :: proc() --- // De-inititialize rlgl (buffers, shaders, textures) - rlLoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function pointer required) - rlGetVersion :: proc() -> GlVersion --- // Returns current OpenGL version - rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width - rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height + rlglInit :: proc(width, height: c.int) --- // Initialize rlgl (buffers, shaders, textures, states) + rlglClose :: proc() --- // De-initialize rlgl (buffers, shaders, textures) + rlLoadExtensions :: proc(loader: rawptr) --- // Load OpenGL extensions (loader function required) + rlGetVersion :: proc() -> GlVersion --- // Get current OpenGL version + rlSetFramebufferWidth :: proc(width: c.int) --- // Set current framebuffer width + rlGetFramebufferWidth :: proc() -> c.int --- // Get default framebuffer width + rlSetFramebufferHeight :: proc(height: c.int) --- // Set current framebuffer height + rlGetFramebufferHeight :: proc() -> c.int --- // Get default framebuffer height - rlGetTextureIdDefault :: proc() -> u32 --- // Get default texture id - rlGetShaderIdDefault :: proc() -> u32 --- // Get default shader id - rlGetShaderLocsDefault :: proc() -> [^]i32 --- // Get default shader locations + + rlGetTextureIdDefault :: proc() -> c.uint --- // Get default texture id + rlGetShaderIdDefault :: proc() -> c.uint --- // Get default shader id + rlGetShaderLocsDefault :: proc() -> [^]c.int --- // Get default shader locations // Render batch management // NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode // but this render batch API is exposed in case of custom batches are required - rlLoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system - rlUnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system - rlDrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) - rlSetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) - rlDrawRenderBatchActive :: proc() --- // Update and draw internal render batch - rlCheckRenderBatchLimit :: proc(vCount: c.int) -> bool --- // Check internal buffer overflow for a given number of vertex - rlSetTexture :: proc(id: u32) --- // Set current texture for render batch and check buffers limits + rlLoadRenderBatch :: proc(numBuffers, bufferElements: c.int) -> RenderBatch --- // Load a render batch system + rlUnloadRenderBatch :: proc(batch: RenderBatch) --- // Unload render batch system + rlDrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) + rlSetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) + rlDrawRenderBatchActive :: proc() --- // Update and draw internal render batch + rlCheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex + + rlSetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits //------------------------------------------------------------------------------------------------------------------------ // Vertex buffers management - rlLoadVertexArray :: proc() -> u32 --- // Load vertex array (vao) if supported - rlLoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> u32 --- // Load a vertex buffer attribute - rlLoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> u32 --- // Load a new attributes element buffer - rlUpdateVertexBuffer :: proc(bufferId: c.int, data: rawptr, dataSize: c.int, offset: c.int) -> u32 --- // Update GPU buffer with new data - rlUnloadVertexArray :: proc(vaoId: u32) --- - rlUnloadVertexBuffer :: proc(vboId: u32) --- - rlSetVertexAttribute :: proc(index: u32, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: uintptr) --- - rlSetVertexAttributeDivisor :: proc(index: u32, divisor: c.int) --- - rlSetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value + rlLoadVertexArray :: proc() -> c.uint --- // Load vertex array (vao) if supported + rlLoadVertexBuffer :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a vertex buffer attribute + rlLoadVertexBufferElement :: proc(buffer: rawptr, size: c.int, is_dynamic: bool) -> c.uint --- // Load a new attributes element buffer + rlUpdateVertexBuffer :: proc(bufferId: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update GPU buffer with new data + rlUpdateVertexBufferElements :: proc(id: c.uint, data: rawptr, dataSize: c.int, offset: c.int) --- // Update vertex buffer elements with new data + rlUnloadVertexArray :: proc(vaoId: c.uint) --- + rlUnloadVertexBuffer :: proc(vboId: c.uint) --- + rlSetVertexAttribute :: proc(index: c.uint, compSize: c.int, type: c.int, normalized: bool, stride: c.int, pointer: rawptr) --- + rlSetVertexAttributeDivisor :: proc(index: c.uint, divisor: c.int) --- + rlSetVertexAttributeDefault :: proc(locIndex: c.int, value: rawptr, attribType: c.int, count: c.int) --- // Set vertex attribute default value rlDrawVertexArray :: proc(offset: c.int, count: c.int) --- rlDrawVertexArrayElements :: proc(offset: c.int, count: c.int, buffer: rawptr) --- rlDrawVertexArrayInstanced :: proc(offset: c.int, count: c.int, instances: c.int) --- rlDrawVertexArrayElementsInstanced :: proc(offset: c.int, count: c.int, buffer: rawptr, instances: c.int) --- // Textures management - rlLoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> u32 --- // Load texture in GPU - rlLoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> u32 --- // Load depth texture/renderbuffer (to be attached to fbo) - rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> u32 --- // Load texture cubemap - rlUpdateTexture :: proc(id: u32, offsetX, offsetY, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data - rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat: ^u32, glFormat: ^u32, glType: ^u32) --- // Get OpenGL internal formats - rlGetPixelFormatName :: proc(format: PixelFormat) -> cstring --- // Get name string for pixel format - rlUnloadTexture :: proc(id: u32) --- // Unload texture from GPU memory - rlGenerateMipmaps :: proc(texture: ^Texture2D) --- // Generate mipmap data for selected texture - rlReadTexturePixels :: proc(texture: Texture2D) -> rawptr --- // Read texture pixel data - rlReadScreenPixels :: proc(width, height: c.int) -> [^]u8 --- // Read screen pixel data (color buffer) + rlLoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU + rlLoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo) + rlLoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap + rlUpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data + rlGetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats + rlGetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format + rlUnloadTexture :: proc(id: c.uint) --- // Unload texture from GPU memory + rlGenTextureMipmaps :: proc(id: c.uint, width, height: c.int, format: c.int, mipmaps: ^c.int) --- // Generate mipmap data for selected texture + rlReadTexturePixels :: proc(id: c.uint, width, height: c.int, format: c.int) -> rawptr --- // Read texture pixel data + rlReadScreenPixels :: proc(width, height: c.int) -> [^]byte --- // Read screen pixel data (color buffer) // Framebuffer management (fbo) - rlLoadFramebuffer :: proc(width, height: c.int) -> u32 --- // Load an empty framebuffer - rlFramebufferAttach :: proc(fboId: u32, texId: u32, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer - rlFramebufferComplete :: proc(id: u32) -> bool --- // Verify framebuffer is complete - rlUnloadFramebuffer :: proc(id: u32) --- // Delete framebuffer from GPU + rlLoadFramebuffer :: proc(width, height: c.int) -> c.uint --- // Load an empty framebuffer + rlFramebufferAttach :: proc(fboId, texId: c.uint, attachType: c.int, texType: c.int, mipLevel: c.int) --- // Attach texture/renderbuffer to a framebuffer + rlFramebufferComplete :: proc(id: c.uint) -> bool --- // Verify framebuffer is complete + rlUnloadFramebuffer :: proc(id: c.uint) --- // Delete framebuffer from GPU // Shaders management - rlLoadShaderCode :: proc(vsCode, fsCode: cstring) -> u32 --- // Load shader from code strings - rlCompileShader :: proc(shaderCode: cstring, type: c.int) -> u32 --- // Compile custom shader and return shader id (type: GL_VERTEX_SHADER, GL_FRAGMENT_SHADER) - rlLoadShaderProgram :: proc(vShaderId, fShaderId: u32) -> u32 --- // Load custom shader program - rlUnloadShaderProgram :: proc(id: u32) --- // Unload shader program - rlGetLocationUniform :: proc(shaderId: u32, uniformName: cstring) -> c.int --- // Get shader location uniform - rlGetLocationAttrib :: proc(shaderId: u32, attribName: cstring) -> c.int --- // Get shader location attribute + rlLoadShaderCode :: proc(vsCode, fsCode: cstring) -> c.uint --- // Load shader from code strings + rlCompileShader :: proc(shaderCode: cstring, type: c.int) -> c.uint --- // Compile custom shader and return shader id (type: RL_VERTEX_SHADER, RL_FRAGMENT_SHADER, RL_COMPUTE_SHADER) + rlLoadShaderProgram :: proc(vShaderId, fShaderId: c.uint) -> c.uint --- // Load custom shader program + rlUnloadShaderProgram :: proc(id: c.uint) --- // Unload shader program + rlGetLocationUniform :: proc(shaderId: c.uint, uniformName: cstring) -> c.int --- // Get shader location uniform + rlGetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute rlSetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform rlSetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix - rlSetUniformSampler :: proc(locIndex: c.int, textureId: u32) --- // Set shader value sampler - rlSetShader :: proc(shader: Shader) --- // Set shader currently active + rlSetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler + rlSetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations) // Compute shader management - rlLoadComputeShaderProgram :: proc(shaderId: u32) -> u32 --- // Load compute shader program - rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: u32) --- // Dispatch compute shader (equivalent to *draw* for graphics pilepine) + rlLoadComputeShaderProgram :: proc(shaderId: c.uint) -> c.uint --- // Load compute shader program + rlComputeShaderDispatch :: proc(groupX, groupY, groupZ: c.uint) --- // Dispatch compute shader (equivalent to *draw* for graphics pipeline) - // Shader buffer storage object management (ssbo) - rlLoadShaderBuffer :: proc(size: u64, data: rawptr, usageHint: c.int) -> u32 --- // Load shader storage buffer object (SSBO) - rlUnloadShaderBuffer :: proc(ssboId: u32) --- // Unload shader storage buffer object (SSBO) - rlUpdateShaderBufferElements :: proc(id: u32, data: rawptr, dataSize: u64, offset: u64) --- // Update SSBO buffer data - rlGetShaderBufferSize :: proc(id: u32) -> u64 --- // Get SSBO buffer size - rlReadShaderBufferElements :: proc(id: u32, dest: rawptr, count: u64, offset: u64) --- // Bind SSBO buffer - rlBindShaderBuffer :: proc(id: u32, index: u32) --- // Copy SSBO buffer data + rlLoadShaderBuffer :: proc(size: c.uint, data: rawptr, usageHint: c.int) -> c.uint --- // Load shader storage buffer object (SSBO) + rlUnloadShaderBuffer :: proc(ssboId: c.uint) --- // Unload shader storage buffer object (SSBO) + rlUpdateShaderBuffer :: proc(id: c.uint, data: rawptr, dataSize: c.uint, offset: c.uint) --- // Update SSBO buffer data + rlBindShaderBuffer :: proc(id: c.uint, index: c.uint) --- // Bind SSBO buffer + rlReadShaderBuffer :: proc(id: c.uint, dest: rawptr, count: c.uint, offset: c.uint) --- // Read SSBO buffer data (GPU->CPU) + rlCopyShaderBuffer :: proc(destId, srcId: c.uint, destOffset, srcOffset: c.uint, count: c.uint) --- // Copy SSBO data between buffers + rlGetShaderBufferSize :: proc(id: c.uint) -> c.uint --- // Get SSBO buffer size // Buffer management - rlCopyBuffersElements :: proc(destId, srcId: u32, destOffset, srcOffset: u64, count: u64) --- // Copy SSBO buffer data - rlBindImageTexture :: proc(id: u32, index: u32, format: u32, readonly: b32) --- // Bind image texture - + rlBindImageTexture :: proc(id: c.uint, index: c.uint, format: c.int, readonly: bool) --- // Bind image texture // Matrix state management rlGetMatrixModelview :: proc() -> Matrix --- // Get internal modelview matrix diff --git a/vendor/raylib/windows/raylib.dll b/vendor/raylib/windows/raylib.dll new file mode 100644 index 000000000..df58566c0 Binary files /dev/null and b/vendor/raylib/windows/raylib.dll differ diff --git a/vendor/raylib/windows/raylib.lib b/vendor/raylib/windows/raylib.lib new file mode 100644 index 000000000..f42f4714a Binary files /dev/null and b/vendor/raylib/windows/raylib.lib differ diff --git a/vendor/raylib/windows/raylibdll.lib b/vendor/raylib/windows/raylibdll.lib new file mode 100644 index 000000000..d4546131e Binary files /dev/null and b/vendor/raylib/windows/raylibdll.lib differ diff --git a/vendor/wasm/js/dom_all_targets.odin b/vendor/wasm/js/dom_all_targets.odin new file mode 100644 index 000000000..7b3ad1a64 --- /dev/null +++ b/vendor/wasm/js/dom_all_targets.odin @@ -0,0 +1,36 @@ +//+build !js +package wasm_js_interface + +import "core:runtime" + + +get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + + +get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + + +Rect :: struct { + x, y, width, height: f64, +} + +get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + +window_get_rect :: proc "contextless" () -> (rect: Rect) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + +window_get_scroll :: proc "contextless" () -> (x, y: f64) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} diff --git a/vendor/wasm/js/events_all_targets.odin b/vendor/wasm/js/events_all_targets.odin new file mode 100644 index 000000000..2bc7e8ceb --- /dev/null +++ b/vendor/wasm/js/events_all_targets.odin @@ -0,0 +1,288 @@ +//+build !js +package wasm_js_interface + + +Event_Kind :: enum u32 { + Invalid, + + Load, + Unload, + Error, + Resize, + Visibility_Change, + Fullscreen_Change, + Fullscreen_Error, + + Click, + Double_Click, + Mouse_Move, + Mouse_Over, + Mouse_Out, + Mouse_Up, + Mouse_Down, + + Key_Up, + Key_Down, + Key_Press, + + Scroll, + Wheel, + + Focus, + Submit, + Blur, + Change, + Select, + + Animation_Start, + Animation_End, + Animation_Iteration, + Animation_Cancel, + + Copy, + Cut, + Paste, + + // Drag, + // Drag_Start, + // Drag_End, + // Drag_Enter, + // Drag_Leave, + // Drag_Over, + // Drop, + + Pointer_Cancel, + Pointer_Down, + Pointer_Enter, + Pointer_Leave, + Pointer_Move, + Pointer_Over, + Pointer_Up, + Got_Pointer_Capture, + Lost_Pointer_Capture, + Pointer_Lock_Change, + Pointer_Lock_Error, + + Selection_Change, + Selection_Start, + + Touch_Cancel, + Touch_End, + Touch_Move, + Touch_Start, + + Transition_Start, + Transition_End, + Transition_Run, + Transition_Cancel, + + Context_Menu, + + Custom, + +} +event_kind_string := [Event_Kind]string{ + .Invalid = "", + + .Load = "load", + .Unload = "unload", + .Error = "error", + .Resize = "resize", + .Visibility_Change = "visibilitychange", + .Fullscreen_Change = "fullscreenchange", + .Fullscreen_Error = "fullscreenerror", + + .Click = "click", + .Double_Click = "dblclick", + .Mouse_Move = "mousemove", + .Mouse_Over = "mouseover", + .Mouse_Out = "mouseout", + .Mouse_Up = "mouseup", + .Mouse_Down = "mousedown", + + .Key_Up = "keyup", + .Key_Down = "keydown", + .Key_Press = "keypress", + + .Scroll = "scroll", + .Wheel = "wheel", + + .Focus = "focus", + .Submit = "submit", + .Blur = "blur", + .Change = "change", + .Select = "select", + + .Animation_Start = "animationstart", + .Animation_End = "animationend", + .Animation_Iteration = "animationiteration", + .Animation_Cancel = "animationcancel", + + .Copy = "copy", + .Cut = "cut", + .Paste = "paste", + + // .Drag, = "drag", + // .Drag_Start, = "dragstart", + // .Drag_End, = "dragend", + // .Drag_Enter, = "dragenter", + // .Drag_Leave, = "dragleave", + // .Drag_Over, = "dragover", + // .Drop, = "drop", + + .Pointer_Cancel = "pointercancel", + .Pointer_Down = "pointerdown", + .Pointer_Enter = "pointerenter", + .Pointer_Leave = "pointerleave", + .Pointer_Move = "pointermove", + .Pointer_Over = "pointerover", + .Pointer_Up = "pointerup", + .Got_Pointer_Capture = "gotpointercapture", + .Lost_Pointer_Capture = "lostpointercapture", + .Pointer_Lock_Change = "pointerlockchange", + .Pointer_Lock_Error = "pointerlockerror", + + .Selection_Change = "selectionchange", + .Selection_Start = "selectionstart", + + .Transition_Start = "transitionstart", + .Transition_End = "transitionend", + .Transition_Run = "transitionrun", + .Transition_Cancel = "transitioncancel", + + .Touch_Cancel = "touchcancel", + .Touch_End = "touchend", + .Touch_Move = "touchmove", + .Touch_Start = "touchstart", + + .Context_Menu = "contextmenu", + + .Custom = "?custom?", +} + +Delta_Mode :: enum u32 { + Pixel = 0, + Line = 1, + Page = 2, +} + +Key_Location :: enum u8 { + Standard = 0, + Left = 1, + Right = 2, + Numpad = 3, +} + +KEYBOARD_MAX_KEY_SIZE :: 16 +KEYBOARD_MAX_CODE_SIZE :: 16 + +Event_Target_Kind :: enum u32 { + Element = 0, + Document = 1, + Window = 2, +} + +Event_Phase :: enum u8 { + None = 0, + Capturing_Phase = 1, + At_Target = 2, + Bubbling_Phase = 3, +} + +Event_Option :: enum u8 { + Bubbles = 0, + Cancelable = 1, + Composed = 2, +} +Event_Options :: distinct bit_set[Event_Option; u8] + +Event :: struct { + kind: Event_Kind, + target_kind: Event_Target_Kind, + current_target_kind: Event_Target_Kind, + id: string, + timestamp: f64, + + phase: Event_Phase, + options: Event_Options, + is_composing: bool, + is_trusted: bool, + + using data: struct #raw_union #align 8 { + scroll: struct { + delta: [2]f64, + }, + visibility_change: struct { + is_visible: bool, + }, + wheel: struct { + delta: [3]f64, + delta_mode: Delta_Mode, + }, + + key: struct { + key: string, + code: string, + location: Key_Location, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + repeat: bool, + + _key_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + _code_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + }, + + mouse: struct { + screen: [2]i64, + client: [2]i64, + offset: [2]i64, + page: [2]i64, + movement: [2]i64, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + button: i16, + buttons: bit_set[0..<16; u16], + }, + }, + + + user_data: rawptr, + callback: proc(e: Event), +} + + +add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_event_listener_from_event :: proc(e: Event) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} +remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + diff --git a/vendor/wasm/js/memory_all_targets.odin b/vendor/wasm/js/memory_all_targets.odin new file mode 100644 index 000000000..e1de6a696 --- /dev/null +++ b/vendor/wasm/js/memory_all_targets.odin @@ -0,0 +1,14 @@ +//+build !js +package wasm_js_interface + +import "core:mem" + +PAGE_SIZE :: 64 * 1024 +page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) { + panic("vendor:wasm/js not supported on non-js targets") +} + +page_allocator :: proc() -> mem.Allocator { + panic("vendor:wasm/js not supported on non-js targets") +} + diff --git a/vendor/wasm/js/memory.odin b/vendor/wasm/js/memory_js.odin similarity index 100% rename from vendor/wasm/js/memory.odin rename to vendor/wasm/js/memory_js.odin