;` entity.
+*/
+@(private="file")
+_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
+ assert(t != nil && t.r == '&')
+
+ /*
+ All of these would be in the ASCII range.
+ Even if one is not, it doesn't matter. All characters we need to compare to extract are.
+ */
+ using t
+
+ length := len(t.src)
+ found := false
+
+ #no_bounds_check {
+ for read_offset < length {
+ if src[read_offset] == ';' {
+ found = true
+ read_offset += 1
+ break
+ }
+ read_offset += 1
+ }
+ }
+
+ if found {
+ return string(src[offset + 1 : read_offset - 1]), .None
+ }
+ return string(src[offset : read_offset]), .Invalid_Entity_Encoding
+}
+
+/*
+ Private XML helper for CDATA and comments.
+*/
+@(private="file")
+_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) {
+ assert(t != nil && t.r == '<')
+ if t.read_offset + len(CDATA_START) >= len(t.src) { return false, .None }
+
+ if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
+ t.read_offset += len(CDATA_START) - 1
+
+ if .Unbox_CDATA in options && .Decode_CDATA in options {
+ /*
+ We're unboxing _and_ decoding CDATA
+ */
+ return true, .None
+ }
+
+ /*
+ CDATA is passed through.
+ */
+ offset := t.offset
+
+ /*
+ Scan until end of CDATA.
+ */
+ for {
+ advance(t) or_return
+ if t.r < 0 { return true, .CDATA_Not_Terminated }
+
+ if t.read_offset + len(CDATA_END) < len(t.src) {
+ if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
+ t.read_offset += len(CDATA_END) - 1
+
+ cdata := string(t.src[offset : t.read_offset])
+
+ if .Unbox_CDATA in options {
+ cdata = cdata[len(CDATA_START):]
+ cdata = cdata[:len(cdata) - len(CDATA_END)]
+ }
+
+ write_string(builder, cdata)
+ return false, .None
+ }
+ }
+ }
+
+ } else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
+ t.read_offset += len(COMMENT_START)
+ /*
+ Comment is passed through by default.
+ */
+ offset := t.offset
+
+ /*
+ Scan until end of Comment.
+ */
+ for {
+ advance(t) or_return
+ if t.r < 0 { return true, .Comment_Not_Terminated }
+
+ if t.read_offset + len(COMMENT_END) < len(t.src) {
+ if string(t.src[t.offset:][:len(COMMENT_END)]) == COMMENT_END {
+ t.read_offset += len(COMMENT_END) - 1
+
+ if .Comment_Strip not_in options {
+ comment := string(t.src[offset : t.read_offset])
+ write_string(builder, comment)
+ }
+ return false, .None
+ }
+ }
+ }
+
+ }
+ return false, .None
+}
\ No newline at end of file
diff --git a/core/encoding/entity/example/entity_example.odin b/core/encoding/entity/example/entity_example.odin
new file mode 100644
index 000000000..6301eb263
--- /dev/null
+++ b/core/encoding/entity/example/entity_example.odin
@@ -0,0 +1,76 @@
+package unicode_entity_example
+
+import "core:encoding/xml"
+import "core:strings"
+import "core:mem"
+import "core:fmt"
+import "core:time"
+
+doc_print :: proc(doc: ^xml.Document) {
+ buf: strings.Builder
+ defer strings.builder_destroy(&buf)
+ w := strings.to_writer(&buf)
+
+ xml.print(w, doc)
+ fmt.println(strings.to_string(buf))
+}
+
+_entities :: proc() {
+ doc: ^xml.Document
+ err: xml.Error
+
+ DOC :: #load("../../../../tests/core/assets/XML/unicode.xml")
+
+ OPTIONS :: xml.Options{
+ flags = {
+ .Ignore_Unsupported, .Intern_Comments,
+ },
+ expected_doctype = "",
+ }
+
+ parse_duration: time.Duration
+
+ {
+ time.SCOPED_TICK_DURATION(&parse_duration)
+ doc, err = xml.parse(DOC, OPTIONS)
+ }
+ defer xml.destroy(doc)
+
+ doc_print(doc)
+
+ ms := time.duration_milliseconds(parse_duration)
+
+ speed := (f64(1000.0) / ms) * f64(len(DOC)) / 1_024.0 / 1_024.0
+
+ fmt.printf("Parse time: %.2f ms (%.2f MiB/s).\n", ms, speed)
+ fmt.printf("Error: %v\n", err)
+}
+
+_main :: proc() {
+ using fmt
+
+ options := xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities }}
+
+ doc, _ := xml.parse(#load("test.html"), options)
+
+ defer xml.destroy(doc)
+ doc_print(doc)
+}
+
+main :: proc() {
+ using fmt
+
+ track: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&track, context.allocator)
+ context.allocator = mem.tracking_allocator(&track)
+
+ // _main()
+ _entities()
+
+ if len(track.allocation_map) > 0 {
+ println()
+ for _, v in track.allocation_map {
+ printf("%v Leaked %v bytes.\n", v.location, v.size)
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/encoding/entity/example/test.html b/core/encoding/entity/example/test.html
new file mode 100644
index 000000000..ebbc6470c
--- /dev/null
+++ b/core/encoding/entity/example/test.html
@@ -0,0 +1,28 @@
+
+
+ Entity Reference Test
+
+
+
+ Entity Reference Test
+
+
+
+ Foozle]! © 42&;1234&
+
+
+
+ | | | fj ` \ ® ϱ ∳ ⁏
+
+
+
\ No newline at end of file
diff --git a/core/encoding/entity/generated.odin b/core/encoding/entity/generated.odin
new file mode 100644
index 000000000..9afdcae6d
--- /dev/null
+++ b/core/encoding/entity/generated.odin
@@ -0,0 +1,7493 @@
+package unicode_entity
+
+/*
+ ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
+*/
+
+/*
+ This file is generated from "https://www.w3.org/2003/entities/2007xml/unicode.xml".
+
+ UPDATE:
+ - Ensure the XML file was downloaded using "tests\core\download_assets.py".
+ - Run "core/unicode/tools/generate_entity_table.odin"
+
+ Odin unicode generated tables: https://github.com/odin-lang/Odin/tree/master/core/encoding/entity
+
+ Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology,
+ European Research Consortium for Informatics and Mathematics, Keio University, Beihang).
+
+ All Rights Reserved.
+
+ This work is distributed under the W3C® Software License [1] in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ [1] http://www.w3.org/Consortium/Legal/copyright-software
+
+ See also: LICENSE_table.md
+*/
+
+// `<`
+XML_NAME_TO_RUNE_MIN_LENGTH :: 2
+// `∳`
+XML_NAME_TO_RUNE_MAX_LENGTH :: 31
+
+
+/*
+ Input:
+ entity_name - a string, like "copy" that describes a user-encoded Unicode entity as used in XML.
+
+ Output:
+ "decoded" - The decoded rune if found by name, or -1 otherwise.
+ "ok" - true if found, false if not.
+
+ IMPORTANT: XML processors (including browsers) treat these names as case-sensitive. So do we.
+*/
+named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) {
+ /*
+ Early out if the name is too short or too long.
+ min as a precaution in case the generated table has a bogus value.
+ */
+ if len(name) < min(1, XML_NAME_TO_RUNE_MIN_LENGTH) || len(name) > XML_NAME_TO_RUNE_MAX_LENGTH {
+ return -1, false
+ }
+
+ switch rune(name[0]) {
+
+ case 'A':
+ switch name {
+ case "AElig":
+ // LATIN CAPITAL LETTER AE
+ return rune(0xc6), true
+ case "AMP":
+ // AMPERSAND
+ return rune(0x26), true
+ case "Aacgr":
+ // GREEK CAPITAL LETTER ALPHA WITH TONOS
+ return rune(0x0386), true
+ case "Aacute":
+ // LATIN CAPITAL LETTER A WITH ACUTE
+ return rune(0xc1), true
+ case "Abreve":
+ // LATIN CAPITAL LETTER A WITH BREVE
+ return rune(0x0102), true
+ case "Acirc":
+ // LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+ return rune(0xc2), true
+ case "Acy":
+ // CYRILLIC CAPITAL LETTER A
+ return rune(0x0410), true
+ case "Afr":
+ // MATHEMATICAL FRAKTUR CAPITAL A
+ return rune(0x01d504), true
+ case "Agrave":
+ // LATIN CAPITAL LETTER A WITH GRAVE
+ return rune(0xc0), true
+ case "Agr":
+ // GREEK CAPITAL LETTER ALPHA
+ return rune(0x0391), true
+ case "Alpha":
+ // GREEK CAPITAL LETTER ALPHA
+ return rune(0x0391), true
+ case "Amacr":
+ // LATIN CAPITAL LETTER A WITH MACRON
+ return rune(0x0100), true
+ case "And":
+ // DOUBLE LOGICAL AND
+ return rune(0x2a53), true
+ case "Aogon":
+ // LATIN CAPITAL LETTER A WITH OGONEK
+ return rune(0x0104), true
+ case "Aopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL A
+ return rune(0x01d538), true
+ case "ApplyFunction":
+ // FUNCTION APPLICATION
+ return rune(0x2061), true
+ case "Aring":
+ // LATIN CAPITAL LETTER A WITH RING ABOVE
+ return rune(0xc5), true
+ case "Ascr":
+ // MATHEMATICAL SCRIPT CAPITAL A
+ return rune(0x01d49c), true
+ case "Assign":
+ // COLON EQUALS
+ return rune(0x2254), true
+ case "Ast":
+ // TWO ASTERISKS ALIGNED VERTICALLY
+ return rune(0x2051), true
+ case "Atilde":
+ // LATIN CAPITAL LETTER A WITH TILDE
+ return rune(0xc3), true
+ case "Auml":
+ // LATIN CAPITAL LETTER A WITH DIAERESIS
+ return rune(0xc4), true
+ }
+
+ case 'B':
+ switch name {
+ case "Backslash":
+ // SET MINUS
+ return rune(0x2216), true
+ case "Barint":
+ // INTEGRAL WITH DOUBLE STROKE
+ return rune(0x2a0e), true
+ case "Barv":
+ // SHORT DOWN TACK WITH OVERBAR
+ return rune(0x2ae7), true
+ case "Barwedl":
+ // LOGICAL AND WITH DOUBLE OVERBAR
+ return rune(0x2a5e), true
+ case "Barwed":
+ // PERSPECTIVE
+ return rune(0x2306), true
+ case "Bcy":
+ // CYRILLIC CAPITAL LETTER BE
+ return rune(0x0411), true
+ case "Because":
+ // BECAUSE
+ return rune(0x2235), true
+ case "Bernoullis":
+ // SCRIPT CAPITAL B
+ return rune(0x212c), true
+ case "Beta":
+ // GREEK CAPITAL LETTER BETA
+ return rune(0x0392), true
+ case "Bfr":
+ // MATHEMATICAL FRAKTUR CAPITAL B
+ return rune(0x01d505), true
+ case "Bgr":
+ // GREEK CAPITAL LETTER BETA
+ return rune(0x0392), true
+ case "Bopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL B
+ return rune(0x01d539), true
+ case "Breve":
+ // BREVE
+ return rune(0x02d8), true
+ case "Bscr":
+ // SCRIPT CAPITAL B
+ return rune(0x212c), true
+ case "Bumpeq":
+ // GEOMETRICALLY EQUIVALENT TO
+ return rune(0x224e), true
+ case "Bvert":
+ // BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL
+ return rune(0x2506), true
+ }
+
+ case 'C':
+ switch name {
+ case "CHcy":
+ // CYRILLIC CAPITAL LETTER CHE
+ return rune(0x0427), true
+ case "COPY":
+ // COPYRIGHT SIGN
+ return rune(0xa9), true
+ case "Cacute":
+ // LATIN CAPITAL LETTER C WITH ACUTE
+ return rune(0x0106), true
+ case "CapitalDifferentialD":
+ // DOUBLE-STRUCK ITALIC CAPITAL D
+ return rune(0x2145), true
+ case "Cap":
+ // DOUBLE INTERSECTION
+ return rune(0x22d2), true
+ case "Cayleys":
+ // BLACK-LETTER CAPITAL C
+ return rune(0x212d), true
+ case "Ccaron":
+ // LATIN CAPITAL LETTER C WITH CARON
+ return rune(0x010c), true
+ case "Ccedil":
+ // LATIN CAPITAL LETTER C WITH CEDILLA
+ return rune(0xc7), true
+ case "Ccirc":
+ // LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+ return rune(0x0108), true
+ case "Cconint":
+ // VOLUME INTEGRAL
+ return rune(0x2230), true
+ case "Cdot":
+ // LATIN CAPITAL LETTER C WITH DOT ABOVE
+ return rune(0x010a), true
+ case "Cedilla":
+ // CEDILLA
+ return rune(0xb8), true
+ case "CenterDot":
+ // MIDDLE DOT
+ return rune(0xb7), true
+ case "Cfr":
+ // BLACK-LETTER CAPITAL C
+ return rune(0x212d), true
+ case "Chi":
+ // GREEK CAPITAL LETTER CHI
+ return rune(0x03a7), true
+ case "CircleDot":
+ // CIRCLED DOT OPERATOR
+ return rune(0x2299), true
+ case "CircleMinus":
+ // CIRCLED MINUS
+ return rune(0x2296), true
+ case "CirclePlus":
+ // CIRCLED PLUS
+ return rune(0x2295), true
+ case "CircleTimes":
+ // CIRCLED TIMES
+ return rune(0x2297), true
+ case "ClockwiseContourIntegral":
+ // CLOCKWISE CONTOUR INTEGRAL
+ return rune(0x2232), true
+ case "CloseCurlyDoubleQuote":
+ // RIGHT DOUBLE QUOTATION MARK
+ return rune(0x201d), true
+ case "CloseCurlyQuote":
+ // RIGHT SINGLE QUOTATION MARK
+ return rune(0x2019), true
+ case "Colon":
+ // PROPORTION
+ return rune(0x2237), true
+ case "Colone":
+ // DOUBLE COLON EQUAL
+ return rune(0x2a74), true
+ case "Congruent":
+ // IDENTICAL TO
+ return rune(0x2261), true
+ case "Conint":
+ // SURFACE INTEGRAL
+ return rune(0x222f), true
+ case "ContourIntegral":
+ // CONTOUR INTEGRAL
+ return rune(0x222e), true
+ case "Copf":
+ // DOUBLE-STRUCK CAPITAL C
+ return rune(0x2102), true
+ case "Coproduct":
+ // N-ARY COPRODUCT
+ return rune(0x2210), true
+ case "CounterClockwiseContourIntegral":
+ // ANTICLOCKWISE CONTOUR INTEGRAL
+ return rune(0x2233), true
+ case "Cross":
+ // VECTOR OR CROSS PRODUCT
+ return rune(0x2a2f), true
+ case "Cscr":
+ // MATHEMATICAL SCRIPT CAPITAL C
+ return rune(0x01d49e), true
+ case "CupCap":
+ // EQUIVALENT TO
+ return rune(0x224d), true
+ case "Cup":
+ // DOUBLE UNION
+ return rune(0x22d3), true
+ }
+
+ case 'D':
+ switch name {
+ case "DD":
+ // DOUBLE-STRUCK ITALIC CAPITAL D
+ return rune(0x2145), true
+ case "DDotrahd":
+ // RIGHTWARDS ARROW WITH DOTTED STEM
+ return rune(0x2911), true
+ case "DJcy":
+ // CYRILLIC CAPITAL LETTER DJE
+ return rune(0x0402), true
+ case "DScy":
+ // CYRILLIC CAPITAL LETTER DZE
+ return rune(0x0405), true
+ case "DZcy":
+ // CYRILLIC CAPITAL LETTER DZHE
+ return rune(0x040f), true
+ case "Dagger":
+ // DOUBLE DAGGER
+ return rune(0x2021), true
+ case "Darr":
+ // DOWNWARDS TWO HEADED ARROW
+ return rune(0x21a1), true
+ case "Dashv":
+ // VERTICAL BAR DOUBLE LEFT TURNSTILE
+ return rune(0x2ae4), true
+ case "Dcaron":
+ // LATIN CAPITAL LETTER D WITH CARON
+ return rune(0x010e), true
+ case "Dcy":
+ // CYRILLIC CAPITAL LETTER DE
+ return rune(0x0414), true
+ case "Del":
+ // NABLA
+ return rune(0x2207), true
+ case "Delta":
+ // GREEK CAPITAL LETTER DELTA
+ return rune(0x0394), true
+ case "Dfr":
+ // MATHEMATICAL FRAKTUR CAPITAL D
+ return rune(0x01d507), true
+ case "Dgr":
+ // GREEK CAPITAL LETTER DELTA
+ return rune(0x0394), true
+ case "DiacriticalAcute":
+ // ACUTE ACCENT
+ return rune(0xb4), true
+ case "DiacriticalDot":
+ // DOT ABOVE
+ return rune(0x02d9), true
+ case "DiacriticalDoubleAcute":
+ // DOUBLE ACUTE ACCENT
+ return rune(0x02dd), true
+ case "DiacriticalGrave":
+ // GRAVE ACCENT
+ return rune(0x60), true
+ case "DiacriticalTilde":
+ // SMALL TILDE
+ return rune(0x02dc), true
+ case "Diamond":
+ // DIAMOND OPERATOR
+ return rune(0x22c4), true
+ case "DifferentialD":
+ // DOUBLE-STRUCK ITALIC SMALL D
+ return rune(0x2146), true
+ case "Dopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL D
+ return rune(0x01d53b), true
+ case "Dot":
+ // DIAERESIS
+ return rune(0xa8), true
+ case "DotDot":
+ // COMBINING FOUR DOTS ABOVE
+ return rune(0x20dc), true
+ case "DotEqual":
+ // APPROACHES THE LIMIT
+ return rune(0x2250), true
+ case "DoubleContourIntegral":
+ // SURFACE INTEGRAL
+ return rune(0x222f), true
+ case "DoubleDot":
+ // DIAERESIS
+ return rune(0xa8), true
+ case "DoubleDownArrow":
+ // DOWNWARDS DOUBLE ARROW
+ return rune(0x21d3), true
+ case "DoubleLeftArrow":
+ // LEFTWARDS DOUBLE ARROW
+ return rune(0x21d0), true
+ case "DoubleLeftRightArrow":
+ // LEFT RIGHT DOUBLE ARROW
+ return rune(0x21d4), true
+ case "DoubleLeftTee":
+ // VERTICAL BAR DOUBLE LEFT TURNSTILE
+ return rune(0x2ae4), true
+ case "DoubleLongLeftArrow":
+ // LONG LEFTWARDS DOUBLE ARROW
+ return rune(0x27f8), true
+ case "DoubleLongLeftRightArrow":
+ // LONG LEFT RIGHT DOUBLE ARROW
+ return rune(0x27fa), true
+ case "DoubleLongRightArrow":
+ // LONG RIGHTWARDS DOUBLE ARROW
+ return rune(0x27f9), true
+ case "DoubleRightArrow":
+ // RIGHTWARDS DOUBLE ARROW
+ return rune(0x21d2), true
+ case "DoubleRightTee":
+ // TRUE
+ return rune(0x22a8), true
+ case "DoubleUpArrow":
+ // UPWARDS DOUBLE ARROW
+ return rune(0x21d1), true
+ case "DoubleUpDownArrow":
+ // UP DOWN DOUBLE ARROW
+ return rune(0x21d5), true
+ case "DoubleVerticalBar":
+ // PARALLEL TO
+ return rune(0x2225), true
+ case "DownArrowUpArrow":
+ // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
+ return rune(0x21f5), true
+ case "DownArrow":
+ // DOWNWARDS ARROW
+ return rune(0x2193), true
+ case "DownArrowBar":
+ // DOWNWARDS ARROW TO BAR
+ return rune(0x2913), true
+ case "DownBreve":
+ // COMBINING INVERTED BREVE
+ return rune(0x0311), true
+ case "DownLeftRightVector":
+ // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
+ return rune(0x2950), true
+ case "DownLeftTeeVector":
+ // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
+ return rune(0x295e), true
+ case "DownLeftVector":
+ // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21bd), true
+ case "DownLeftVectorBar":
+ // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
+ return rune(0x2956), true
+ case "DownRightTeeVector":
+ // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
+ return rune(0x295f), true
+ case "DownRightVector":
+ // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21c1), true
+ case "DownRightVectorBar":
+ // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
+ return rune(0x2957), true
+ case "DownTeeArrow":
+ // DOWNWARDS ARROW FROM BAR
+ return rune(0x21a7), true
+ case "DownTee":
+ // DOWN TACK
+ return rune(0x22a4), true
+ case "Downarrow":
+ // DOWNWARDS DOUBLE ARROW
+ return rune(0x21d3), true
+ case "Dscr":
+ // MATHEMATICAL SCRIPT CAPITAL D
+ return rune(0x01d49f), true
+ case "Dstrok":
+ // LATIN CAPITAL LETTER D WITH STROKE
+ return rune(0x0110), true
+ }
+
+ case 'E':
+ switch name {
+ case "EEacgr":
+ // GREEK CAPITAL LETTER ETA WITH TONOS
+ return rune(0x0389), true
+ case "EEgr":
+ // GREEK CAPITAL LETTER ETA
+ return rune(0x0397), true
+ case "ENG":
+ // LATIN CAPITAL LETTER ENG
+ return rune(0x014a), true
+ case "ETH":
+ // LATIN CAPITAL LETTER ETH
+ return rune(0xd0), true
+ case "Eacgr":
+ // GREEK CAPITAL LETTER EPSILON WITH TONOS
+ return rune(0x0388), true
+ case "Eacute":
+ // LATIN CAPITAL LETTER E WITH ACUTE
+ return rune(0xc9), true
+ case "Ecaron":
+ // LATIN CAPITAL LETTER E WITH CARON
+ return rune(0x011a), true
+ case "Ecirc":
+ // LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+ return rune(0xca), true
+ case "Ecy":
+ // CYRILLIC CAPITAL LETTER E
+ return rune(0x042d), true
+ case "Edot":
+ // LATIN CAPITAL LETTER E WITH DOT ABOVE
+ return rune(0x0116), true
+ case "Efr":
+ // MATHEMATICAL FRAKTUR CAPITAL E
+ return rune(0x01d508), true
+ case "Egrave":
+ // LATIN CAPITAL LETTER E WITH GRAVE
+ return rune(0xc8), true
+ case "Egr":
+ // GREEK CAPITAL LETTER EPSILON
+ return rune(0x0395), true
+ case "Element":
+ // ELEMENT OF
+ return rune(0x2208), true
+ case "Emacr":
+ // LATIN CAPITAL LETTER E WITH MACRON
+ return rune(0x0112), true
+ case "EmptySmallSquare":
+ // WHITE MEDIUM SQUARE
+ return rune(0x25fb), true
+ case "EmptyVerySmallSquare":
+ // WHITE SMALL SQUARE
+ return rune(0x25ab), true
+ case "Eogon":
+ // LATIN CAPITAL LETTER E WITH OGONEK
+ return rune(0x0118), true
+ case "Eopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL E
+ return rune(0x01d53c), true
+ case "Epsilon":
+ // GREEK CAPITAL LETTER EPSILON
+ return rune(0x0395), true
+ case "EqualTilde":
+ // MINUS TILDE
+ return rune(0x2242), true
+ case "Equal":
+ // TWO CONSECUTIVE EQUALS SIGNS
+ return rune(0x2a75), true
+ case "Equilibrium":
+ // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ return rune(0x21cc), true
+ case "Escr":
+ // SCRIPT CAPITAL E
+ return rune(0x2130), true
+ case "Esim":
+ // EQUALS SIGN ABOVE TILDE OPERATOR
+ return rune(0x2a73), true
+ case "Eta":
+ // GREEK CAPITAL LETTER ETA
+ return rune(0x0397), true
+ case "Euml":
+ // LATIN CAPITAL LETTER E WITH DIAERESIS
+ return rune(0xcb), true
+ case "Exists":
+ // THERE EXISTS
+ return rune(0x2203), true
+ case "ExponentialE":
+ // DOUBLE-STRUCK ITALIC SMALL E
+ return rune(0x2147), true
+ }
+
+ case 'F':
+ switch name {
+ case "Fcy":
+ // CYRILLIC CAPITAL LETTER EF
+ return rune(0x0424), true
+ case "Ffr":
+ // MATHEMATICAL FRAKTUR CAPITAL F
+ return rune(0x01d509), true
+ case "FilledSmallSquare":
+ // BLACK MEDIUM SQUARE
+ return rune(0x25fc), true
+ case "FilledVerySmallSquare":
+ // BLACK SMALL SQUARE
+ return rune(0x25aa), true
+ case "Fopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL F
+ return rune(0x01d53d), true
+ case "ForAll":
+ // FOR ALL
+ return rune(0x2200), true
+ case "Fouriertrf":
+ // SCRIPT CAPITAL F
+ return rune(0x2131), true
+ case "Fscr":
+ // SCRIPT CAPITAL F
+ return rune(0x2131), true
+ }
+
+ case 'G':
+ switch name {
+ case "GJcy":
+ // CYRILLIC CAPITAL LETTER GJE
+ return rune(0x0403), true
+ case "GT":
+ // GREATER-THAN SIGN
+ return rune(0x3e), true
+ case "Game":
+ // TURNED SANS-SERIF CAPITAL G
+ return rune(0x2141), true
+ case "Gamma":
+ // GREEK CAPITAL LETTER GAMMA
+ return rune(0x0393), true
+ case "Gammad":
+ // GREEK LETTER DIGAMMA
+ return rune(0x03dc), true
+ case "Gbreve":
+ // LATIN CAPITAL LETTER G WITH BREVE
+ return rune(0x011e), true
+ case "Gcedil":
+ // LATIN CAPITAL LETTER G WITH CEDILLA
+ return rune(0x0122), true
+ case "Gcirc":
+ // LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+ return rune(0x011c), true
+ case "Gcy":
+ // CYRILLIC CAPITAL LETTER GHE
+ return rune(0x0413), true
+ case "Gdot":
+ // LATIN CAPITAL LETTER G WITH DOT ABOVE
+ return rune(0x0120), true
+ case "Gfr":
+ // MATHEMATICAL FRAKTUR CAPITAL G
+ return rune(0x01d50a), true
+ case "Ggr":
+ // GREEK CAPITAL LETTER GAMMA
+ return rune(0x0393), true
+ case "Gg":
+ // VERY MUCH GREATER-THAN
+ return rune(0x22d9), true
+ case "Gopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL G
+ return rune(0x01d53e), true
+ case "GreaterEqual":
+ // GREATER-THAN OR EQUAL TO
+ return rune(0x2265), true
+ case "GreaterEqualLess":
+ // GREATER-THAN EQUAL TO OR LESS-THAN
+ return rune(0x22db), true
+ case "GreaterFullEqual":
+ // GREATER-THAN OVER EQUAL TO
+ return rune(0x2267), true
+ case "GreaterGreater":
+ // DOUBLE NESTED GREATER-THAN
+ return rune(0x2aa2), true
+ case "GreaterLess":
+ // GREATER-THAN OR LESS-THAN
+ return rune(0x2277), true
+ case "GreaterSlantEqual":
+ // GREATER-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7e), true
+ case "GreaterTilde":
+ // GREATER-THAN OR EQUIVALENT TO
+ return rune(0x2273), true
+ case "Gscr":
+ // MATHEMATICAL SCRIPT CAPITAL G
+ return rune(0x01d4a2), true
+ case "Gt":
+ // MUCH GREATER-THAN
+ return rune(0x226b), true
+ }
+
+ case 'H':
+ switch name {
+ case "HARDcy":
+ // CYRILLIC CAPITAL LETTER HARD SIGN
+ return rune(0x042a), true
+ case "Hacek":
+ // CARON
+ return rune(0x02c7), true
+ case "Hat":
+ // CIRCUMFLEX ACCENT
+ return rune(0x5e), true
+ case "Hcirc":
+ // LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+ return rune(0x0124), true
+ case "Hfr":
+ // BLACK-LETTER CAPITAL H
+ return rune(0x210c), true
+ case "HilbertSpace":
+ // SCRIPT CAPITAL H
+ return rune(0x210b), true
+ case "Hopf":
+ // DOUBLE-STRUCK CAPITAL H
+ return rune(0x210d), true
+ case "HorizontalLine":
+ // BOX DRAWINGS LIGHT HORIZONTAL
+ return rune(0x2500), true
+ case "Hscr":
+ // SCRIPT CAPITAL H
+ return rune(0x210b), true
+ case "Hstrok":
+ // LATIN CAPITAL LETTER H WITH STROKE
+ return rune(0x0126), true
+ case "HumpDownHump":
+ // GEOMETRICALLY EQUIVALENT TO
+ return rune(0x224e), true
+ case "HumpEqual":
+ // DIFFERENCE BETWEEN
+ return rune(0x224f), true
+ }
+
+ case 'I':
+ switch name {
+ case "IEcy":
+ // CYRILLIC CAPITAL LETTER IE
+ return rune(0x0415), true
+ case "IJlig":
+ // LATIN CAPITAL LIGATURE IJ
+ return rune(0x0132), true
+ case "IOcy":
+ // CYRILLIC CAPITAL LETTER IO
+ return rune(0x0401), true
+ case "Iacgr":
+ // GREEK CAPITAL LETTER IOTA WITH TONOS
+ return rune(0x038a), true
+ case "Iacute":
+ // LATIN CAPITAL LETTER I WITH ACUTE
+ return rune(0xcd), true
+ case "Icirc":
+ // LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+ return rune(0xce), true
+ case "Icy":
+ // CYRILLIC CAPITAL LETTER I
+ return rune(0x0418), true
+ case "Idigr":
+ // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+ return rune(0x03aa), true
+ case "Idot":
+ // LATIN CAPITAL LETTER I WITH DOT ABOVE
+ return rune(0x0130), true
+ case "Ifr":
+ // BLACK-LETTER CAPITAL I
+ return rune(0x2111), true
+ case "Igrave":
+ // LATIN CAPITAL LETTER I WITH GRAVE
+ return rune(0xcc), true
+ case "Igr":
+ // GREEK CAPITAL LETTER IOTA
+ return rune(0x0399), true
+ case "Imacr":
+ // LATIN CAPITAL LETTER I WITH MACRON
+ return rune(0x012a), true
+ case "ImaginaryI":
+ // DOUBLE-STRUCK ITALIC SMALL I
+ return rune(0x2148), true
+ case "Implies":
+ // RIGHTWARDS DOUBLE ARROW
+ return rune(0x21d2), true
+ case "Im":
+ // BLACK-LETTER CAPITAL I
+ return rune(0x2111), true
+ case "Integral":
+ // INTEGRAL
+ return rune(0x222b), true
+ case "Int":
+ // DOUBLE INTEGRAL
+ return rune(0x222c), true
+ case "Intersection":
+ // N-ARY INTERSECTION
+ return rune(0x22c2), true
+ case "InvisibleComma":
+ // INVISIBLE SEPARATOR
+ return rune(0x2063), true
+ case "InvisibleTimes":
+ // INVISIBLE TIMES
+ return rune(0x2062), true
+ case "Iogon":
+ // LATIN CAPITAL LETTER I WITH OGONEK
+ return rune(0x012e), true
+ case "Iopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL I
+ return rune(0x01d540), true
+ case "Iota":
+ // GREEK CAPITAL LETTER IOTA
+ return rune(0x0399), true
+ case "Iscr":
+ // SCRIPT CAPITAL I
+ return rune(0x2110), true
+ case "Itilde":
+ // LATIN CAPITAL LETTER I WITH TILDE
+ return rune(0x0128), true
+ case "Iukcy":
+ // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ return rune(0x0406), true
+ case "Iuml":
+ // LATIN CAPITAL LETTER I WITH DIAERESIS
+ return rune(0xcf), true
+ }
+
+ case 'J':
+ switch name {
+ case "Jcirc":
+ // LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+ return rune(0x0134), true
+ case "Jcy":
+ // CYRILLIC CAPITAL LETTER SHORT I
+ return rune(0x0419), true
+ case "Jfr":
+ // MATHEMATICAL FRAKTUR CAPITAL J
+ return rune(0x01d50d), true
+ case "Jopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL J
+ return rune(0x01d541), true
+ case "Jscr":
+ // MATHEMATICAL SCRIPT CAPITAL J
+ return rune(0x01d4a5), true
+ case "Jsercy":
+ // CYRILLIC CAPITAL LETTER JE
+ return rune(0x0408), true
+ case "Jukcy":
+ // CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ return rune(0x0404), true
+ }
+
+ case 'K':
+ switch name {
+ case "KHcy":
+ // CYRILLIC CAPITAL LETTER HA
+ return rune(0x0425), true
+ case "KHgr":
+ // GREEK CAPITAL LETTER CHI
+ return rune(0x03a7), true
+ case "KJcy":
+ // CYRILLIC CAPITAL LETTER KJE
+ return rune(0x040c), true
+ case "Kappa":
+ // GREEK CAPITAL LETTER KAPPA
+ return rune(0x039a), true
+ case "Kcedil":
+ // LATIN CAPITAL LETTER K WITH CEDILLA
+ return rune(0x0136), true
+ case "Kcy":
+ // CYRILLIC CAPITAL LETTER KA
+ return rune(0x041a), true
+ case "Kfr":
+ // MATHEMATICAL FRAKTUR CAPITAL K
+ return rune(0x01d50e), true
+ case "Kgr":
+ // GREEK CAPITAL LETTER KAPPA
+ return rune(0x039a), true
+ case "Kopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL K
+ return rune(0x01d542), true
+ case "Kscr":
+ // MATHEMATICAL SCRIPT CAPITAL K
+ return rune(0x01d4a6), true
+ }
+
+ case 'L':
+ switch name {
+ case "LJcy":
+ // CYRILLIC CAPITAL LETTER LJE
+ return rune(0x0409), true
+ case "LT":
+ // LESS-THAN SIGN
+ return rune(0x3c), true
+ case "Lacute":
+ // LATIN CAPITAL LETTER L WITH ACUTE
+ return rune(0x0139), true
+ case "Lambda":
+ // GREEK CAPITAL LETTER LAMDA
+ return rune(0x039b), true
+ case "Lang":
+ // MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
+ return rune(0x27ea), true
+ case "Laplacetrf":
+ // SCRIPT CAPITAL L
+ return rune(0x2112), true
+ case "Larr":
+ // LEFTWARDS TWO HEADED ARROW
+ return rune(0x219e), true
+ case "Lcaron":
+ // LATIN CAPITAL LETTER L WITH CARON
+ return rune(0x013d), true
+ case "Lcedil":
+ // LATIN CAPITAL LETTER L WITH CEDILLA
+ return rune(0x013b), true
+ case "Lcy":
+ // CYRILLIC CAPITAL LETTER EL
+ return rune(0x041b), true
+ case "LeftAngleBracket":
+ // MATHEMATICAL LEFT ANGLE BRACKET
+ return rune(0x27e8), true
+ case "LeftArrowBar":
+ // LEFTWARDS ARROW TO BAR
+ return rune(0x21e4), true
+ case "LeftArrowRightArrow":
+ // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ return rune(0x21c6), true
+ case "LeftArrow":
+ // LEFTWARDS ARROW
+ return rune(0x2190), true
+ case "LeftCeiling":
+ // LEFT CEILING
+ return rune(0x2308), true
+ case "LeftDoubleBracket":
+ // MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ return rune(0x27e6), true
+ case "LeftDownTeeVector":
+ // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
+ return rune(0x2961), true
+ case "LeftDownVector":
+ // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21c3), true
+ case "LeftDownVectorBar":
+ // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
+ return rune(0x2959), true
+ case "LeftFloor":
+ // LEFT FLOOR
+ return rune(0x230a), true
+ case "LeftRightArrow":
+ // LEFT RIGHT ARROW
+ return rune(0x2194), true
+ case "LeftRightVector":
+ // LEFT BARB UP RIGHT BARB UP HARPOON
+ return rune(0x294e), true
+ case "LeftTeeArrow":
+ // LEFTWARDS ARROW FROM BAR
+ return rune(0x21a4), true
+ case "LeftTeeVector":
+ // LEFTWARDS HARPOON WITH BARB UP FROM BAR
+ return rune(0x295a), true
+ case "LeftTee":
+ // LEFT TACK
+ return rune(0x22a3), true
+ case "LeftTriangleBar":
+ // LEFT TRIANGLE BESIDE VERTICAL BAR
+ return rune(0x29cf), true
+ case "LeftTriangle":
+ // NORMAL SUBGROUP OF
+ return rune(0x22b2), true
+ case "LeftTriangleEqual":
+ // NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22b4), true
+ case "LeftUpDownVector":
+ // UP BARB LEFT DOWN BARB LEFT HARPOON
+ return rune(0x2951), true
+ case "LeftUpTeeVector":
+ // UPWARDS HARPOON WITH BARB LEFT FROM BAR
+ return rune(0x2960), true
+ case "LeftUpVector":
+ // UPWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21bf), true
+ case "LeftUpVectorBar":
+ // UPWARDS HARPOON WITH BARB LEFT TO BAR
+ return rune(0x2958), true
+ case "LeftVector":
+ // LEFTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21bc), true
+ case "LeftVectorBar":
+ // LEFTWARDS HARPOON WITH BARB UP TO BAR
+ return rune(0x2952), true
+ case "Leftarrow":
+ // LEFTWARDS DOUBLE ARROW
+ return rune(0x21d0), true
+ case "Leftrightarrow":
+ // LEFT RIGHT DOUBLE ARROW
+ return rune(0x21d4), true
+ case "LessEqualGreater":
+ // LESS-THAN EQUAL TO OR GREATER-THAN
+ return rune(0x22da), true
+ case "LessFullEqual":
+ // LESS-THAN OVER EQUAL TO
+ return rune(0x2266), true
+ case "LessGreater":
+ // LESS-THAN OR GREATER-THAN
+ return rune(0x2276), true
+ case "LessLess":
+ // DOUBLE NESTED LESS-THAN
+ return rune(0x2aa1), true
+ case "LessSlantEqual":
+ // LESS-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7d), true
+ case "LessTilde":
+ // LESS-THAN OR EQUIVALENT TO
+ return rune(0x2272), true
+ case "Lfr":
+ // MATHEMATICAL FRAKTUR CAPITAL L
+ return rune(0x01d50f), true
+ case "Lgr":
+ // GREEK CAPITAL LETTER LAMDA
+ return rune(0x039b), true
+ case "Lleftarrow":
+ // LEFTWARDS TRIPLE ARROW
+ return rune(0x21da), true
+ case "Ll":
+ // VERY MUCH LESS-THAN
+ return rune(0x22d8), true
+ case "Lmidot":
+ // LATIN CAPITAL LETTER L WITH MIDDLE DOT
+ return rune(0x013f), true
+ case "LongLeftArrow":
+ // LONG LEFTWARDS ARROW
+ return rune(0x27f5), true
+ case "LongLeftRightArrow":
+ // LONG LEFT RIGHT ARROW
+ return rune(0x27f7), true
+ case "LongRightArrow":
+ // LONG RIGHTWARDS ARROW
+ return rune(0x27f6), true
+ case "Longleftarrow":
+ // LONG LEFTWARDS DOUBLE ARROW
+ return rune(0x27f8), true
+ case "Longleftrightarrow":
+ // LONG LEFT RIGHT DOUBLE ARROW
+ return rune(0x27fa), true
+ case "Longrightarrow":
+ // LONG RIGHTWARDS DOUBLE ARROW
+ return rune(0x27f9), true
+ case "Lopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL L
+ return rune(0x01d543), true
+ case "LowerLeftArrow":
+ // SOUTH WEST ARROW
+ return rune(0x2199), true
+ case "LowerRightArrow":
+ // SOUTH EAST ARROW
+ return rune(0x2198), true
+ case "Lscr":
+ // SCRIPT CAPITAL L
+ return rune(0x2112), true
+ case "Lsh":
+ // UPWARDS ARROW WITH TIP LEFTWARDS
+ return rune(0x21b0), true
+ case "Lstrok":
+ // LATIN CAPITAL LETTER L WITH STROKE
+ return rune(0x0141), true
+ case "Ltbar":
+ // DOUBLE NESTED LESS-THAN WITH UNDERBAR
+ return rune(0x2aa3), true
+ case "Lt":
+ // MUCH LESS-THAN
+ return rune(0x226a), true
+ }
+
+ case 'M':
+ switch name {
+ case "Mapfrom":
+ // LEFTWARDS DOUBLE ARROW FROM BAR
+ return rune(0x2906), true
+ case "Mapto":
+ // RIGHTWARDS DOUBLE ARROW FROM BAR
+ return rune(0x2907), true
+ case "Map":
+ // RIGHTWARDS TWO-HEADED ARROW FROM BAR
+ return rune(0x2905), true
+ case "Mcy":
+ // CYRILLIC CAPITAL LETTER EM
+ return rune(0x041c), true
+ case "MediumSpace":
+ // MEDIUM MATHEMATICAL SPACE
+ return rune(0x205f), true
+ case "Mellintrf":
+ // SCRIPT CAPITAL M
+ return rune(0x2133), true
+ case "Mfr":
+ // MATHEMATICAL FRAKTUR CAPITAL M
+ return rune(0x01d510), true
+ case "Mgr":
+ // GREEK CAPITAL LETTER MU
+ return rune(0x039c), true
+ case "MinusPlus":
+ // MINUS-OR-PLUS SIGN
+ return rune(0x2213), true
+ case "Mopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL M
+ return rune(0x01d544), true
+ case "Mscr":
+ // SCRIPT CAPITAL M
+ return rune(0x2133), true
+ case "Mu":
+ // GREEK CAPITAL LETTER MU
+ return rune(0x039c), true
+ }
+
+ case 'N':
+ switch name {
+ case "NJcy":
+ // CYRILLIC CAPITAL LETTER NJE
+ return rune(0x040a), true
+ case "Nacute":
+ // LATIN CAPITAL LETTER N WITH ACUTE
+ return rune(0x0143), true
+ case "Ncaron":
+ // LATIN CAPITAL LETTER N WITH CARON
+ return rune(0x0147), true
+ case "Ncedil":
+ // LATIN CAPITAL LETTER N WITH CEDILLA
+ return rune(0x0145), true
+ case "Ncy":
+ // CYRILLIC CAPITAL LETTER EN
+ return rune(0x041d), true
+ case "NegativeMediumSpace":
+ // ZERO WIDTH SPACE
+ return rune(0x200b), true
+ case "NegativeThickSpace":
+ // ZERO WIDTH SPACE
+ return rune(0x200b), true
+ case "NegativeThinSpace":
+ // ZERO WIDTH SPACE
+ return rune(0x200b), true
+ case "NegativeVeryThinSpace":
+ // ZERO WIDTH SPACE
+ return rune(0x200b), true
+ case "NestedGreaterGreater":
+ // MUCH GREATER-THAN
+ return rune(0x226b), true
+ case "NestedLessLess":
+ // MUCH LESS-THAN
+ return rune(0x226a), true
+ case "NewLine":
+ // LINE FEED (LF)
+ return rune(0x0a), true
+ case "Nfr":
+ // MATHEMATICAL FRAKTUR CAPITAL N
+ return rune(0x01d511), true
+ case "Ngr":
+ // GREEK CAPITAL LETTER NU
+ return rune(0x039d), true
+ case "NoBreak":
+ // WORD JOINER
+ return rune(0x2060), true
+ case "NonBreakingSpace":
+ // NO-BREAK SPACE
+ return rune(0xa0), true
+ case "Nopf":
+ // DOUBLE-STRUCK CAPITAL N
+ return rune(0x2115), true
+ case "NotDoubleVerticalBar":
+ // NOT PARALLEL TO
+ return rune(0x2226), true
+ case "NotElement":
+ // NOT AN ELEMENT OF
+ return rune(0x2209), true
+ case "NotEqualTilde":
+ // MINUS TILDE with slash
+ return rune(0x2242), true
+ case "NotEqual":
+ // NOT EQUAL TO
+ return rune(0x2260), true
+ case "NotExists":
+ // THERE DOES NOT EXIST
+ return rune(0x2204), true
+ case "NotHumpDownHump":
+ // GEOMETRICALLY EQUIVALENT TO with slash
+ return rune(0x224e), true
+ case "NotHumpEqual":
+ // DIFFERENCE BETWEEN with slash
+ return rune(0x224f), true
+ case "NotLessGreater":
+ // NEITHER LESS-THAN NOR GREATER-THAN
+ return rune(0x2278), true
+ case "NotReverseElement":
+ // DOES NOT CONTAIN AS MEMBER
+ return rune(0x220c), true
+ case "NotTilde":
+ // NOT TILDE
+ return rune(0x2241), true
+ case "NotTildeEqual":
+ // NOT ASYMPTOTICALLY EQUAL TO
+ return rune(0x2244), true
+ case "NotTildeFullEqual":
+ // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+ return rune(0x2247), true
+ case "NotTildeTilde":
+ // NOT ALMOST EQUAL TO
+ return rune(0x2249), true
+ case "NotVerticalBar":
+ // DOES NOT DIVIDE
+ return rune(0x2224), true
+ case "Not":
+ // DOUBLE STROKE NOT SIGN
+ return rune(0x2aec), true
+ case "NotCongruent":
+ // NOT IDENTICAL TO
+ return rune(0x2262), true
+ case "NotCupCap":
+ // NOT EQUIVALENT TO
+ return rune(0x226d), true
+ case "NotGreaterFullEqual":
+ // GREATER-THAN OVER EQUAL TO with slash
+ return rune(0x2267), true
+ case "NotGreaterGreater":
+ // MUCH GREATER THAN with slash
+ return rune(0x226b), true
+ case "NotGreaterSlantEqual":
+ // GREATER-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7e), true
+ case "NotGreater":
+ // NOT GREATER-THAN
+ return rune(0x226f), true
+ case "NotGreaterEqual":
+ // NEITHER GREATER-THAN NOR EQUAL TO
+ return rune(0x2271), true
+ case "NotGreaterLess":
+ // NEITHER GREATER-THAN NOR LESS-THAN
+ return rune(0x2279), true
+ case "NotGreaterTilde":
+ // NEITHER GREATER-THAN NOR EQUIVALENT TO
+ return rune(0x2275), true
+ case "NotLeftTriangleBar":
+ // LEFT TRIANGLE BESIDE VERTICAL BAR with slash
+ return rune(0x29cf), true
+ case "NotLeftTriangle":
+ // NOT NORMAL SUBGROUP OF
+ return rune(0x22ea), true
+ case "NotLeftTriangleEqual":
+ // NOT NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22ec), true
+ case "NotLessLess":
+ // MUCH LESS THAN with slash
+ return rune(0x226a), true
+ case "NotLessSlantEqual":
+ // LESS-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7d), true
+ case "NotLess":
+ // NOT LESS-THAN
+ return rune(0x226e), true
+ case "NotLessEqual":
+ // NEITHER LESS-THAN NOR EQUAL TO
+ return rune(0x2270), true
+ case "NotLessTilde":
+ // NEITHER LESS-THAN NOR EQUIVALENT TO
+ return rune(0x2274), true
+ case "NotNestedGreaterGreater":
+ // DOUBLE NESTED GREATER-THAN with slash
+ return rune(0x2aa2), true
+ case "NotNestedLessLess":
+ // DOUBLE NESTED LESS-THAN with slash
+ return rune(0x2aa1), true
+ case "NotPrecedesEqual":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2aaf), true
+ case "NotPrecedes":
+ // DOES NOT PRECEDE
+ return rune(0x2280), true
+ case "NotPrecedesSlantEqual":
+ // DOES NOT PRECEDE OR EQUAL
+ return rune(0x22e0), true
+ case "NotRightTriangleBar":
+ // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash
+ return rune(0x29d0), true
+ case "NotRightTriangle":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP
+ return rune(0x22eb), true
+ case "NotRightTriangleEqual":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ return rune(0x22ed), true
+ case "NotSquareSubset":
+ // SQUARE IMAGE OF with slash
+ return rune(0x228f), true
+ case "NotSquareSubsetEqual":
+ // NOT SQUARE IMAGE OF OR EQUAL TO
+ return rune(0x22e2), true
+ case "NotSquareSuperset":
+ // SQUARE ORIGINAL OF with slash
+ return rune(0x2290), true
+ case "NotSquareSupersetEqual":
+ // NOT SQUARE ORIGINAL OF OR EQUAL TO
+ return rune(0x22e3), true
+ case "NotSubset":
+ // SUBSET OF with vertical line
+ return rune(0x2282), true
+ case "NotSubsetEqual":
+ // NEITHER A SUBSET OF NOR EQUAL TO
+ return rune(0x2288), true
+ case "NotSucceedsEqual":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2ab0), true
+ case "NotSucceedsTilde":
+ // SUCCEEDS OR EQUIVALENT TO with slash
+ return rune(0x227f), true
+ case "NotSucceeds":
+ // DOES NOT SUCCEED
+ return rune(0x2281), true
+ case "NotSucceedsSlantEqual":
+ // DOES NOT SUCCEED OR EQUAL
+ return rune(0x22e1), true
+ case "NotSuperset":
+ // SUPERSET OF with vertical line
+ return rune(0x2283), true
+ case "NotSupersetEqual":
+ // NEITHER A SUPERSET OF NOR EQUAL TO
+ return rune(0x2289), true
+ case "Nscr":
+ // MATHEMATICAL SCRIPT CAPITAL N
+ return rune(0x01d4a9), true
+ case "Ntilde":
+ // LATIN CAPITAL LETTER N WITH TILDE
+ return rune(0xd1), true
+ case "Nu":
+ // GREEK CAPITAL LETTER NU
+ return rune(0x039d), true
+ }
+
+ case 'O':
+ switch name {
+ case "OElig":
+ // LATIN CAPITAL LIGATURE OE
+ return rune(0x0152), true
+ case "OHacgr":
+ // GREEK CAPITAL LETTER OMEGA WITH TONOS
+ return rune(0x038f), true
+ case "OHgr":
+ // GREEK CAPITAL LETTER OMEGA
+ return rune(0x03a9), true
+ case "Oacgr":
+ // GREEK CAPITAL LETTER OMICRON WITH TONOS
+ return rune(0x038c), true
+ case "Oacute":
+ // LATIN CAPITAL LETTER O WITH ACUTE
+ return rune(0xd3), true
+ case "Ocirc":
+ // LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+ return rune(0xd4), true
+ case "Ocy":
+ // CYRILLIC CAPITAL LETTER O
+ return rune(0x041e), true
+ case "Odblac":
+ // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+ return rune(0x0150), true
+ case "Ofr":
+ // MATHEMATICAL FRAKTUR CAPITAL O
+ return rune(0x01d512), true
+ case "Ograve":
+ // LATIN CAPITAL LETTER O WITH GRAVE
+ return rune(0xd2), true
+ case "Ogr":
+ // GREEK CAPITAL LETTER OMICRON
+ return rune(0x039f), true
+ case "Omacr":
+ // LATIN CAPITAL LETTER O WITH MACRON
+ return rune(0x014c), true
+ case "Omega":
+ // GREEK CAPITAL LETTER OMEGA
+ return rune(0x03a9), true
+ case "Omicron":
+ // GREEK CAPITAL LETTER OMICRON
+ return rune(0x039f), true
+ case "Oopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL O
+ return rune(0x01d546), true
+ case "OpenCurlyDoubleQuote":
+ // LEFT DOUBLE QUOTATION MARK
+ return rune(0x201c), true
+ case "OpenCurlyQuote":
+ // LEFT SINGLE QUOTATION MARK
+ return rune(0x2018), true
+ case "Or":
+ // DOUBLE LOGICAL OR
+ return rune(0x2a54), true
+ case "Oscr":
+ // MATHEMATICAL SCRIPT CAPITAL O
+ return rune(0x01d4aa), true
+ case "Oslash":
+ // LATIN CAPITAL LETTER O WITH STROKE
+ return rune(0xd8), true
+ case "Otilde":
+ // LATIN CAPITAL LETTER O WITH TILDE
+ return rune(0xd5), true
+ case "Otimes":
+ // MULTIPLICATION SIGN IN DOUBLE CIRCLE
+ return rune(0x2a37), true
+ case "Ouml":
+ // LATIN CAPITAL LETTER O WITH DIAERESIS
+ return rune(0xd6), true
+ case "OverBar":
+ // OVERLINE
+ return rune(0x203e), true
+ case "OverBrace":
+ // TOP CURLY BRACKET
+ return rune(0x23de), true
+ case "OverBracket":
+ // TOP SQUARE BRACKET
+ return rune(0x23b4), true
+ case "OverParenthesis":
+ // TOP PARENTHESIS
+ return rune(0x23dc), true
+ }
+
+ case 'P':
+ switch name {
+ case "PHgr":
+ // GREEK CAPITAL LETTER PHI
+ return rune(0x03a6), true
+ case "PSgr":
+ // GREEK CAPITAL LETTER PSI
+ return rune(0x03a8), true
+ case "PartialD":
+ // PARTIAL DIFFERENTIAL
+ return rune(0x2202), true
+ case "Pcy":
+ // CYRILLIC CAPITAL LETTER PE
+ return rune(0x041f), true
+ case "Pfr":
+ // MATHEMATICAL FRAKTUR CAPITAL P
+ return rune(0x01d513), true
+ case "Pgr":
+ // GREEK CAPITAL LETTER PI
+ return rune(0x03a0), true
+ case "Phi":
+ // GREEK CAPITAL LETTER PHI
+ return rune(0x03a6), true
+ case "Pi":
+ // GREEK CAPITAL LETTER PI
+ return rune(0x03a0), true
+ case "PlusMinus":
+ // PLUS-MINUS SIGN
+ return rune(0xb1), true
+ case "Poincareplane":
+ // BLACK-LETTER CAPITAL H
+ return rune(0x210c), true
+ case "Popf":
+ // DOUBLE-STRUCK CAPITAL P
+ return rune(0x2119), true
+ case "Product":
+ // N-ARY PRODUCT
+ return rune(0x220f), true
+ case "Proportional":
+ // PROPORTIONAL TO
+ return rune(0x221d), true
+ case "Proportion":
+ // PROPORTION
+ return rune(0x2237), true
+ case "Pr":
+ // DOUBLE PRECEDES
+ return rune(0x2abb), true
+ case "PrecedesEqual":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2aaf), true
+ case "Precedes":
+ // PRECEDES
+ return rune(0x227a), true
+ case "PrecedesSlantEqual":
+ // PRECEDES OR EQUAL TO
+ return rune(0x227c), true
+ case "PrecedesTilde":
+ // PRECEDES OR EQUIVALENT TO
+ return rune(0x227e), true
+ case "Prime":
+ // DOUBLE PRIME
+ return rune(0x2033), true
+ case "Pscr":
+ // MATHEMATICAL SCRIPT CAPITAL P
+ return rune(0x01d4ab), true
+ case "Psi":
+ // GREEK CAPITAL LETTER PSI
+ return rune(0x03a8), true
+ }
+
+ case 'Q':
+ switch name {
+ case "QUOT":
+ // QUOTATION MARK
+ return rune(0x22), true
+ case "Qfr":
+ // MATHEMATICAL FRAKTUR CAPITAL Q
+ return rune(0x01d514), true
+ case "Qopf":
+ // DOUBLE-STRUCK CAPITAL Q
+ return rune(0x211a), true
+ case "Qscr":
+ // MATHEMATICAL SCRIPT CAPITAL Q
+ return rune(0x01d4ac), true
+ }
+
+ case 'R':
+ switch name {
+ case "RBarr":
+ // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
+ return rune(0x2910), true
+ case "REG":
+ // REGISTERED SIGN
+ return rune(0xae), true
+ case "Racute":
+ // LATIN CAPITAL LETTER R WITH ACUTE
+ return rune(0x0154), true
+ case "Rang":
+ // MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
+ return rune(0x27eb), true
+ case "Rarr":
+ // RIGHTWARDS TWO HEADED ARROW
+ return rune(0x21a0), true
+ case "Rarrtl":
+ // RIGHTWARDS TWO-HEADED ARROW WITH TAIL
+ return rune(0x2916), true
+ case "Rcaron":
+ // LATIN CAPITAL LETTER R WITH CARON
+ return rune(0x0158), true
+ case "Rcedil":
+ // LATIN CAPITAL LETTER R WITH CEDILLA
+ return rune(0x0156), true
+ case "Rcy":
+ // CYRILLIC CAPITAL LETTER ER
+ return rune(0x0420), true
+ case "ReverseElement":
+ // CONTAINS AS MEMBER
+ return rune(0x220b), true
+ case "ReverseEquilibrium":
+ // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ return rune(0x21cb), true
+ case "Re":
+ // BLACK-LETTER CAPITAL R
+ return rune(0x211c), true
+ case "ReverseUpEquilibrium":
+ // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ return rune(0x296f), true
+ case "Rfr":
+ // BLACK-LETTER CAPITAL R
+ return rune(0x211c), true
+ case "Rgr":
+ // GREEK CAPITAL LETTER RHO
+ return rune(0x03a1), true
+ case "Rho":
+ // GREEK CAPITAL LETTER RHO
+ return rune(0x03a1), true
+ case "RightAngleBracket":
+ // MATHEMATICAL RIGHT ANGLE BRACKET
+ return rune(0x27e9), true
+ case "RightArrowBar":
+ // RIGHTWARDS ARROW TO BAR
+ return rune(0x21e5), true
+ case "RightArrow":
+ // RIGHTWARDS ARROW
+ return rune(0x2192), true
+ case "RightArrowLeftArrow":
+ // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ return rune(0x21c4), true
+ case "RightCeiling":
+ // RIGHT CEILING
+ return rune(0x2309), true
+ case "RightDoubleBracket":
+ // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ return rune(0x27e7), true
+ case "RightDownTeeVector":
+ // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
+ return rune(0x295d), true
+ case "RightDownVector":
+ // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21c2), true
+ case "RightDownVectorBar":
+ // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
+ return rune(0x2955), true
+ case "RightFloor":
+ // RIGHT FLOOR
+ return rune(0x230b), true
+ case "RightTeeArrow":
+ // RIGHTWARDS ARROW FROM BAR
+ return rune(0x21a6), true
+ case "RightTeeVector":
+ // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
+ return rune(0x295b), true
+ case "RightTee":
+ // RIGHT TACK
+ return rune(0x22a2), true
+ case "RightTriangleBar":
+ // VERTICAL BAR BESIDE RIGHT TRIANGLE
+ return rune(0x29d0), true
+ case "RightTriangle":
+ // CONTAINS AS NORMAL SUBGROUP
+ return rune(0x22b3), true
+ case "RightTriangleEqual":
+ // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ return rune(0x22b5), true
+ case "RightUpDownVector":
+ // UP BARB RIGHT DOWN BARB RIGHT HARPOON
+ return rune(0x294f), true
+ case "RightUpTeeVector":
+ // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
+ return rune(0x295c), true
+ case "RightUpVector":
+ // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21be), true
+ case "RightUpVectorBar":
+ // UPWARDS HARPOON WITH BARB RIGHT TO BAR
+ return rune(0x2954), true
+ case "RightVector":
+ // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21c0), true
+ case "RightVectorBar":
+ // RIGHTWARDS HARPOON WITH BARB UP TO BAR
+ return rune(0x2953), true
+ case "Rightarrow":
+ // RIGHTWARDS DOUBLE ARROW
+ return rune(0x21d2), true
+ case "Ropf":
+ // DOUBLE-STRUCK CAPITAL R
+ return rune(0x211d), true
+ case "RoundImplies":
+ // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
+ return rune(0x2970), true
+ case "Rrightarrow":
+ // RIGHTWARDS TRIPLE ARROW
+ return rune(0x21db), true
+ case "Rscr":
+ // SCRIPT CAPITAL R
+ return rune(0x211b), true
+ case "Rsh":
+ // UPWARDS ARROW WITH TIP RIGHTWARDS
+ return rune(0x21b1), true
+ case "RuleDelayed":
+ // RULE-DELAYED
+ return rune(0x29f4), true
+ }
+
+ case 'S':
+ switch name {
+ case "SHCHcy":
+ // CYRILLIC CAPITAL LETTER SHCHA
+ return rune(0x0429), true
+ case "SHcy":
+ // CYRILLIC CAPITAL LETTER SHA
+ return rune(0x0428), true
+ case "SOFTcy":
+ // CYRILLIC CAPITAL LETTER SOFT SIGN
+ return rune(0x042c), true
+ case "Sacute":
+ // LATIN CAPITAL LETTER S WITH ACUTE
+ return rune(0x015a), true
+ case "Sc":
+ // DOUBLE SUCCEEDS
+ return rune(0x2abc), true
+ case "Scaron":
+ // LATIN CAPITAL LETTER S WITH CARON
+ return rune(0x0160), true
+ case "Scedil":
+ // LATIN CAPITAL LETTER S WITH CEDILLA
+ return rune(0x015e), true
+ case "Scirc":
+ // LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+ return rune(0x015c), true
+ case "Scy":
+ // CYRILLIC CAPITAL LETTER ES
+ return rune(0x0421), true
+ case "Sfr":
+ // MATHEMATICAL FRAKTUR CAPITAL S
+ return rune(0x01d516), true
+ case "Sgr":
+ // GREEK CAPITAL LETTER SIGMA
+ return rune(0x03a3), true
+ case "ShortDownArrow":
+ // DOWNWARDS ARROW
+ return rune(0x2193), true
+ case "ShortLeftArrow":
+ // LEFTWARDS ARROW
+ return rune(0x2190), true
+ case "ShortRightArrow":
+ // RIGHTWARDS ARROW
+ return rune(0x2192), true
+ case "ShortUpArrow":
+ // UPWARDS ARROW
+ return rune(0x2191), true
+ case "Sigma":
+ // GREEK CAPITAL LETTER SIGMA
+ return rune(0x03a3), true
+ case "SmallCircle":
+ // RING OPERATOR
+ return rune(0x2218), true
+ case "Sopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL S
+ return rune(0x01d54a), true
+ case "Sqrt":
+ // SQUARE ROOT
+ return rune(0x221a), true
+ case "SquareIntersection":
+ // SQUARE CAP
+ return rune(0x2293), true
+ case "SquareSubset":
+ // SQUARE IMAGE OF
+ return rune(0x228f), true
+ case "SquareSubsetEqual":
+ // SQUARE IMAGE OF OR EQUAL TO
+ return rune(0x2291), true
+ case "Square":
+ // WHITE SQUARE
+ return rune(0x25a1), true
+ case "SquareSuperset":
+ // SQUARE ORIGINAL OF
+ return rune(0x2290), true
+ case "SquareSupersetEqual":
+ // SQUARE ORIGINAL OF OR EQUAL TO
+ return rune(0x2292), true
+ case "SquareUnion":
+ // SQUARE CUP
+ return rune(0x2294), true
+ case "Sscr":
+ // MATHEMATICAL SCRIPT CAPITAL S
+ return rune(0x01d4ae), true
+ case "Star":
+ // STAR OPERATOR
+ return rune(0x22c6), true
+ case "Sub":
+ // DOUBLE SUBSET
+ return rune(0x22d0), true
+ case "Subset":
+ // DOUBLE SUBSET
+ return rune(0x22d0), true
+ case "SubsetEqual":
+ // SUBSET OF OR EQUAL TO
+ return rune(0x2286), true
+ case "Succeeds":
+ // SUCCEEDS
+ return rune(0x227b), true
+ case "SucceedsEqual":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2ab0), true
+ case "SucceedsSlantEqual":
+ // SUCCEEDS OR EQUAL TO
+ return rune(0x227d), true
+ case "SucceedsTilde":
+ // SUCCEEDS OR EQUIVALENT TO
+ return rune(0x227f), true
+ case "SuchThat":
+ // CONTAINS AS MEMBER
+ return rune(0x220b), true
+ case "Sum":
+ // N-ARY SUMMATION
+ return rune(0x2211), true
+ case "SupersetEqual":
+ // SUPERSET OF OR EQUAL TO
+ return rune(0x2287), true
+ case "Sup":
+ // DOUBLE SUPERSET
+ return rune(0x22d1), true
+ case "Superset":
+ // SUPERSET OF
+ return rune(0x2283), true
+ case "Supset":
+ // DOUBLE SUPERSET
+ return rune(0x22d1), true
+ }
+
+ case 'T':
+ switch name {
+ case "THORN":
+ // LATIN CAPITAL LETTER THORN
+ return rune(0xde), true
+ case "THgr":
+ // GREEK CAPITAL LETTER THETA
+ return rune(0x0398), true
+ case "TRADE":
+ // TRADE MARK SIGN
+ return rune(0x2122), true
+ case "TSHcy":
+ // CYRILLIC CAPITAL LETTER TSHE
+ return rune(0x040b), true
+ case "TScy":
+ // CYRILLIC CAPITAL LETTER TSE
+ return rune(0x0426), true
+ case "Tab":
+ // CHARACTER TABULATION
+ return rune(0x09), true
+ case "Tau":
+ // GREEK CAPITAL LETTER TAU
+ return rune(0x03a4), true
+ case "Tcaron":
+ // LATIN CAPITAL LETTER T WITH CARON
+ return rune(0x0164), true
+ case "Tcedil":
+ // LATIN CAPITAL LETTER T WITH CEDILLA
+ return rune(0x0162), true
+ case "Tcy":
+ // CYRILLIC CAPITAL LETTER TE
+ return rune(0x0422), true
+ case "Tfr":
+ // MATHEMATICAL FRAKTUR CAPITAL T
+ return rune(0x01d517), true
+ case "Tgr":
+ // GREEK CAPITAL LETTER TAU
+ return rune(0x03a4), true
+ case "Therefore":
+ // THEREFORE
+ return rune(0x2234), true
+ case "Theta":
+ // GREEK CAPITAL LETTER THETA
+ return rune(0x0398), true
+ case "Thetav":
+ // GREEK CAPITAL THETA SYMBOL
+ return rune(0x03f4), true
+ case "ThickSpace":
+ // space of width 5/18 em
+ return rune(0x205f), true
+ case "ThinSpace":
+ // THIN SPACE
+ return rune(0x2009), true
+ case "Tilde":
+ // TILDE OPERATOR
+ return rune(0x223c), true
+ case "TildeEqual":
+ // ASYMPTOTICALLY EQUAL TO
+ return rune(0x2243), true
+ case "TildeFullEqual":
+ // APPROXIMATELY EQUAL TO
+ return rune(0x2245), true
+ case "TildeTilde":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "Topf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL T
+ return rune(0x01d54b), true
+ case "TripleDot":
+ // COMBINING THREE DOTS ABOVE
+ return rune(0x20db), true
+ case "Tscr":
+ // MATHEMATICAL SCRIPT CAPITAL T
+ return rune(0x01d4af), true
+ case "Tstrok":
+ // LATIN CAPITAL LETTER T WITH STROKE
+ return rune(0x0166), true
+ }
+
+ case 'U':
+ switch name {
+ case "Uacgr":
+ // GREEK CAPITAL LETTER UPSILON WITH TONOS
+ return rune(0x038e), true
+ case "Uacute":
+ // LATIN CAPITAL LETTER U WITH ACUTE
+ return rune(0xda), true
+ case "Uarrocir":
+ // UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE
+ return rune(0x2949), true
+ case "Uarr":
+ // UPWARDS TWO HEADED ARROW
+ return rune(0x219f), true
+ case "Ubrcy":
+ // CYRILLIC CAPITAL LETTER SHORT U
+ return rune(0x040e), true
+ case "Ubreve":
+ // LATIN CAPITAL LETTER U WITH BREVE
+ return rune(0x016c), true
+ case "Ucirc":
+ // LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+ return rune(0xdb), true
+ case "Ucy":
+ // CYRILLIC CAPITAL LETTER U
+ return rune(0x0423), true
+ case "Udblac":
+ // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+ return rune(0x0170), true
+ case "Udigr":
+ // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+ return rune(0x03ab), true
+ case "Ufr":
+ // MATHEMATICAL FRAKTUR CAPITAL U
+ return rune(0x01d518), true
+ case "Ugrave":
+ // LATIN CAPITAL LETTER U WITH GRAVE
+ return rune(0xd9), true
+ case "Ugr":
+ // GREEK CAPITAL LETTER UPSILON
+ return rune(0x03a5), true
+ case "Umacr":
+ // LATIN CAPITAL LETTER U WITH MACRON
+ return rune(0x016a), true
+ case "UnderBar":
+ // LOW LINE
+ return rune(0x5f), true
+ case "UnderBrace":
+ // BOTTOM CURLY BRACKET
+ return rune(0x23df), true
+ case "UnderBracket":
+ // BOTTOM SQUARE BRACKET
+ return rune(0x23b5), true
+ case "UnderParenthesis":
+ // BOTTOM PARENTHESIS
+ return rune(0x23dd), true
+ case "Union":
+ // N-ARY UNION
+ return rune(0x22c3), true
+ case "UnionPlus":
+ // MULTISET UNION
+ return rune(0x228e), true
+ case "Uogon":
+ // LATIN CAPITAL LETTER U WITH OGONEK
+ return rune(0x0172), true
+ case "Uopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL U
+ return rune(0x01d54c), true
+ case "UpArrow":
+ // UPWARDS ARROW
+ return rune(0x2191), true
+ case "UpArrowBar":
+ // UPWARDS ARROW TO BAR
+ return rune(0x2912), true
+ case "UpArrowDownArrow":
+ // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
+ return rune(0x21c5), true
+ case "UpDownArrow":
+ // UP DOWN ARROW
+ return rune(0x2195), true
+ case "UpEquilibrium":
+ // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ return rune(0x296e), true
+ case "UpTee":
+ // UP TACK
+ return rune(0x22a5), true
+ case "UpTeeArrow":
+ // UPWARDS ARROW FROM BAR
+ return rune(0x21a5), true
+ case "Uparrow":
+ // UPWARDS DOUBLE ARROW
+ return rune(0x21d1), true
+ case "Updownarrow":
+ // UP DOWN DOUBLE ARROW
+ return rune(0x21d5), true
+ case "UpperLeftArrow":
+ // NORTH WEST ARROW
+ return rune(0x2196), true
+ case "UpperRightArrow":
+ // NORTH EAST ARROW
+ return rune(0x2197), true
+ case "Upsilon":
+ // GREEK CAPITAL LETTER UPSILON
+ return rune(0x03a5), true
+ case "Upsi":
+ // GREEK UPSILON WITH HOOK SYMBOL
+ return rune(0x03d2), true
+ case "Uring":
+ // LATIN CAPITAL LETTER U WITH RING ABOVE
+ return rune(0x016e), true
+ case "Uscr":
+ // MATHEMATICAL SCRIPT CAPITAL U
+ return rune(0x01d4b0), true
+ case "Utilde":
+ // LATIN CAPITAL LETTER U WITH TILDE
+ return rune(0x0168), true
+ case "Uuml":
+ // LATIN CAPITAL LETTER U WITH DIAERESIS
+ return rune(0xdc), true
+ }
+
+ case 'V':
+ switch name {
+ case "VDash":
+ // DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ return rune(0x22ab), true
+ case "Vbar":
+ // DOUBLE UP TACK
+ return rune(0x2aeb), true
+ case "Vcy":
+ // CYRILLIC CAPITAL LETTER VE
+ return rune(0x0412), true
+ case "Vdashl":
+ // LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL
+ return rune(0x2ae6), true
+ case "Vdash":
+ // FORCES
+ return rune(0x22a9), true
+ case "Vee":
+ // N-ARY LOGICAL OR
+ return rune(0x22c1), true
+ case "Verbar":
+ // DOUBLE VERTICAL LINE
+ return rune(0x2016), true
+ case "Vert":
+ // DOUBLE VERTICAL LINE
+ return rune(0x2016), true
+ case "VerticalBar":
+ // DIVIDES
+ return rune(0x2223), true
+ case "VerticalLine":
+ // VERTICAL LINE
+ return rune(0x7c), true
+ case "VerticalSeparator":
+ // LIGHT VERTICAL BAR
+ return rune(0x2758), true
+ case "VerticalTilde":
+ // WREATH PRODUCT
+ return rune(0x2240), true
+ case "VeryThinSpace":
+ // HAIR SPACE
+ return rune(0x200a), true
+ case "Vfr":
+ // MATHEMATICAL FRAKTUR CAPITAL V
+ return rune(0x01d519), true
+ case "Vopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL V
+ return rune(0x01d54d), true
+ case "Vscr":
+ // MATHEMATICAL SCRIPT CAPITAL V
+ return rune(0x01d4b1), true
+ case "Vvdash":
+ // TRIPLE VERTICAL BAR RIGHT TURNSTILE
+ return rune(0x22aa), true
+ }
+
+ case 'W':
+ switch name {
+ case "Wcirc":
+ // LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+ return rune(0x0174), true
+ case "Wedge":
+ // N-ARY LOGICAL AND
+ return rune(0x22c0), true
+ case "Wfr":
+ // MATHEMATICAL FRAKTUR CAPITAL W
+ return rune(0x01d51a), true
+ case "Wopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL W
+ return rune(0x01d54e), true
+ case "Wscr":
+ // MATHEMATICAL SCRIPT CAPITAL W
+ return rune(0x01d4b2), true
+ }
+
+ case 'X':
+ switch name {
+ case "Xfr":
+ // MATHEMATICAL FRAKTUR CAPITAL X
+ return rune(0x01d51b), true
+ case "Xgr":
+ // GREEK CAPITAL LETTER XI
+ return rune(0x039e), true
+ case "Xi":
+ // GREEK CAPITAL LETTER XI
+ return rune(0x039e), true
+ case "Xopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL X
+ return rune(0x01d54f), true
+ case "Xscr":
+ // MATHEMATICAL SCRIPT CAPITAL X
+ return rune(0x01d4b3), true
+ }
+
+ case 'Y':
+ switch name {
+ case "YAcy":
+ // CYRILLIC CAPITAL LETTER YA
+ return rune(0x042f), true
+ case "YIcy":
+ // CYRILLIC CAPITAL LETTER YI
+ return rune(0x0407), true
+ case "YUcy":
+ // CYRILLIC CAPITAL LETTER YU
+ return rune(0x042e), true
+ case "Yacute":
+ // LATIN CAPITAL LETTER Y WITH ACUTE
+ return rune(0xdd), true
+ case "Ycirc":
+ // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+ return rune(0x0176), true
+ case "Ycy":
+ // CYRILLIC CAPITAL LETTER YERU
+ return rune(0x042b), true
+ case "Yfr":
+ // MATHEMATICAL FRAKTUR CAPITAL Y
+ return rune(0x01d51c), true
+ case "Yopf":
+ // MATHEMATICAL DOUBLE-STRUCK CAPITAL Y
+ return rune(0x01d550), true
+ case "Yscr":
+ // MATHEMATICAL SCRIPT CAPITAL Y
+ return rune(0x01d4b4), true
+ case "Yuml":
+ // LATIN CAPITAL LETTER Y WITH DIAERESIS
+ return rune(0x0178), true
+ }
+
+ case 'Z':
+ switch name {
+ case "ZHcy":
+ // CYRILLIC CAPITAL LETTER ZHE
+ return rune(0x0416), true
+ case "Zacute":
+ // LATIN CAPITAL LETTER Z WITH ACUTE
+ return rune(0x0179), true
+ case "Zcaron":
+ // LATIN CAPITAL LETTER Z WITH CARON
+ return rune(0x017d), true
+ case "Zcy":
+ // CYRILLIC CAPITAL LETTER ZE
+ return rune(0x0417), true
+ case "Zdot":
+ // LATIN CAPITAL LETTER Z WITH DOT ABOVE
+ return rune(0x017b), true
+ case "ZeroWidthSpace":
+ // ZERO WIDTH SPACE
+ return rune(0x200b), true
+ case "Zeta":
+ // GREEK CAPITAL LETTER ZETA
+ return rune(0x0396), true
+ case "Zfr":
+ // BLACK-LETTER CAPITAL Z
+ return rune(0x2128), true
+ case "Zgr":
+ // GREEK CAPITAL LETTER ZETA
+ return rune(0x0396), true
+ case "Zopf":
+ // DOUBLE-STRUCK CAPITAL Z
+ return rune(0x2124), true
+ case "Zscr":
+ // MATHEMATICAL SCRIPT CAPITAL Z
+ return rune(0x01d4b5), true
+ }
+
+ case 'a':
+ switch name {
+ case "aacgr":
+ // GREEK SMALL LETTER ALPHA WITH TONOS
+ return rune(0x03ac), true
+ case "aacute":
+ // LATIN SMALL LETTER A WITH ACUTE
+ return rune(0xe1), true
+ case "abreve":
+ // LATIN SMALL LETTER A WITH BREVE
+ return rune(0x0103), true
+ case "acE":
+ // INVERTED LAZY S with double underline
+ return rune(0x223e), true
+ case "acd":
+ // SINE WAVE
+ return rune(0x223f), true
+ case "acute":
+ // ACUTE ACCENT
+ return rune(0xb4), true
+ case "ac":
+ // INVERTED LAZY S
+ return rune(0x223e), true
+ case "acirc":
+ // LATIN SMALL LETTER A WITH CIRCUMFLEX
+ return rune(0xe2), true
+ case "actuary":
+ // COMBINING ANNUITY SYMBOL
+ return rune(0x20e7), true
+ case "acy":
+ // CYRILLIC SMALL LETTER A
+ return rune(0x0430), true
+ case "aelig":
+ // LATIN SMALL LETTER AE
+ return rune(0xe6), true
+ case "af":
+ // FUNCTION APPLICATION
+ return rune(0x2061), true
+ case "afr":
+ // MATHEMATICAL FRAKTUR SMALL A
+ return rune(0x01d51e), true
+ case "agr":
+ // GREEK SMALL LETTER ALPHA
+ return rune(0x03b1), true
+ case "agrave":
+ // LATIN SMALL LETTER A WITH GRAVE
+ return rune(0xe0), true
+ case "alefsym":
+ // ALEF SYMBOL
+ return rune(0x2135), true
+ case "aleph":
+ // ALEF SYMBOL
+ return rune(0x2135), true
+ case "alpha":
+ // GREEK SMALL LETTER ALPHA
+ return rune(0x03b1), true
+ case "amacr":
+ // LATIN SMALL LETTER A WITH MACRON
+ return rune(0x0101), true
+ case "amalg":
+ // AMALGAMATION OR COPRODUCT
+ return rune(0x2a3f), true
+ case "amp":
+ // AMPERSAND
+ return rune(0x26), true
+ case "andand":
+ // TWO INTERSECTING LOGICAL AND
+ return rune(0x2a55), true
+ case "andd":
+ // LOGICAL AND WITH HORIZONTAL DASH
+ return rune(0x2a5c), true
+ case "andslope":
+ // SLOPING LARGE AND
+ return rune(0x2a58), true
+ case "andv":
+ // LOGICAL AND WITH MIDDLE STEM
+ return rune(0x2a5a), true
+ case "and":
+ // LOGICAL AND
+ return rune(0x2227), true
+ case "angdnl":
+ // TURNED ANGLE
+ return rune(0x29a2), true
+ case "angdnr":
+ // ACUTE ANGLE
+ return rune(0x299f), true
+ case "ange":
+ // ANGLE WITH UNDERBAR
+ return rune(0x29a4), true
+ case "angles":
+ // ANGLE WITH S INSIDE
+ return rune(0x299e), true
+ case "angle":
+ // ANGLE
+ return rune(0x2220), true
+ case "angmsdaa":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT
+ return rune(0x29a8), true
+ case "angmsdab":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT
+ return rune(0x29a9), true
+ case "angmsdac":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT
+ return rune(0x29aa), true
+ case "angmsdad":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT
+ return rune(0x29ab), true
+ case "angmsdae":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP
+ return rune(0x29ac), true
+ case "angmsdaf":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP
+ return rune(0x29ad), true
+ case "angmsdag":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN
+ return rune(0x29ae), true
+ case "angmsdah":
+ // MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN
+ return rune(0x29af), true
+ case "angmsd":
+ // MEASURED ANGLE
+ return rune(0x2221), true
+ case "angrtvbd":
+ // MEASURED RIGHT ANGLE WITH DOT
+ return rune(0x299d), true
+ case "angrtvb":
+ // RIGHT ANGLE WITH ARC
+ return rune(0x22be), true
+ case "angsph":
+ // SPHERICAL ANGLE
+ return rune(0x2222), true
+ case "angst":
+ // LATIN CAPITAL LETTER A WITH RING ABOVE
+ return rune(0xc5), true
+ case "angupl":
+ // REVERSED ANGLE
+ return rune(0x29a3), true
+ case "angzarr":
+ // RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW
+ return rune(0x237c), true
+ case "ang":
+ // ANGLE
+ return rune(0x2220), true
+ case "ang90":
+ // RIGHT ANGLE
+ return rune(0x221f), true
+ case "angrt":
+ // RIGHT ANGLE
+ return rune(0x221f), true
+ case "aogon":
+ // LATIN SMALL LETTER A WITH OGONEK
+ return rune(0x0105), true
+ case "aopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL A
+ return rune(0x01d552), true
+ case "apE":
+ // APPROXIMATELY EQUAL OR EQUAL TO
+ return rune(0x2a70), true
+ case "apacir":
+ // ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT
+ return rune(0x2a6f), true
+ case "ape":
+ // ALMOST EQUAL OR EQUAL TO
+ return rune(0x224a), true
+ case "apid":
+ // TRIPLE TILDE
+ return rune(0x224b), true
+ case "approxeq":
+ // ALMOST EQUAL OR EQUAL TO
+ return rune(0x224a), true
+ case "approx":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "ap":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "apos":
+ // APOSTROPHE
+ return rune(0x27), true
+ case "aring":
+ // LATIN SMALL LETTER A WITH RING ABOVE
+ return rune(0xe5), true
+ case "arrllsr":
+ // LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW
+ return rune(0x2943), true
+ case "arrlrsl":
+ // RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW
+ return rune(0x2942), true
+ case "arrsrll":
+ // SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW
+ return rune(0x2944), true
+ case "ascr":
+ // MATHEMATICAL SCRIPT SMALL A
+ return rune(0x01d4b6), true
+ case "astb":
+ // SQUARED ASTERISK
+ return rune(0x29c6), true
+ case "ast":
+ // ASTERISK
+ return rune(0x2a), true
+ case "asympeq":
+ // EQUIVALENT TO
+ return rune(0x224d), true
+ case "asymp":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "atilde":
+ // LATIN SMALL LETTER A WITH TILDE
+ return rune(0xe3), true
+ case "auml":
+ // LATIN SMALL LETTER A WITH DIAERESIS
+ return rune(0xe4), true
+ case "awconint":
+ // ANTICLOCKWISE CONTOUR INTEGRAL
+ return rune(0x2233), true
+ case "awint":
+ // ANTICLOCKWISE INTEGRATION
+ return rune(0x2a11), true
+ }
+
+ case 'b':
+ switch name {
+ case "b.Delta":
+ // MATHEMATICAL BOLD CAPITAL DELTA
+ return rune(0x01d6ab), true
+ case "b.Gamma":
+ // MATHEMATICAL BOLD CAPITAL GAMMA
+ return rune(0x01d6aa), true
+ case "b.Gammad":
+ // MATHEMATICAL BOLD CAPITAL DIGAMMA
+ return rune(0x01d7ca), true
+ case "b.Lambda":
+ // MATHEMATICAL BOLD CAPITAL LAMDA
+ return rune(0x01d6b2), true
+ case "b.Omega":
+ // MATHEMATICAL BOLD CAPITAL OMEGA
+ return rune(0x01d6c0), true
+ case "b.Phi":
+ // MATHEMATICAL BOLD CAPITAL PHI
+ return rune(0x01d6bd), true
+ case "b.Pi":
+ // MATHEMATICAL BOLD CAPITAL PI
+ return rune(0x01d6b7), true
+ case "b.Psi":
+ // MATHEMATICAL BOLD CAPITAL PSI
+ return rune(0x01d6bf), true
+ case "b.Sigma":
+ // MATHEMATICAL BOLD CAPITAL SIGMA
+ return rune(0x01d6ba), true
+ case "b.Theta":
+ // MATHEMATICAL BOLD CAPITAL THETA
+ return rune(0x01d6af), true
+ case "b.Upsi":
+ // MATHEMATICAL BOLD CAPITAL UPSILON
+ return rune(0x01d6bc), true
+ case "b.Xi":
+ // MATHEMATICAL BOLD CAPITAL XI
+ return rune(0x01d6b5), true
+ case "b.alpha":
+ // MATHEMATICAL BOLD SMALL ALPHA
+ return rune(0x01d6c2), true
+ case "b.beta":
+ // MATHEMATICAL BOLD SMALL BETA
+ return rune(0x01d6c3), true
+ case "b.chi":
+ // MATHEMATICAL BOLD SMALL CHI
+ return rune(0x01d6d8), true
+ case "b.delta":
+ // MATHEMATICAL BOLD SMALL DELTA
+ return rune(0x01d6c5), true
+ case "b.epsi":
+ // MATHEMATICAL BOLD SMALL EPSILON
+ return rune(0x01d6c6), true
+ case "b.epsiv":
+ // MATHEMATICAL BOLD EPSILON SYMBOL
+ return rune(0x01d6dc), true
+ case "b.eta":
+ // MATHEMATICAL BOLD SMALL ETA
+ return rune(0x01d6c8), true
+ case "b.gammad":
+ // MATHEMATICAL BOLD SMALL DIGAMMA
+ return rune(0x01d7cb), true
+ case "b.gamma":
+ // MATHEMATICAL BOLD SMALL GAMMA
+ return rune(0x01d6c4), true
+ case "b.iota":
+ // MATHEMATICAL BOLD SMALL IOTA
+ return rune(0x01d6ca), true
+ case "b.kappa":
+ // MATHEMATICAL BOLD SMALL KAPPA
+ return rune(0x01d6cb), true
+ case "b.kappav":
+ // MATHEMATICAL BOLD KAPPA SYMBOL
+ return rune(0x01d6de), true
+ case "b.lambda":
+ // MATHEMATICAL BOLD SMALL LAMDA
+ return rune(0x01d6cc), true
+ case "b.mu":
+ // MATHEMATICAL BOLD SMALL MU
+ return rune(0x01d6cd), true
+ case "b.nu":
+ // MATHEMATICAL BOLD SMALL NU
+ return rune(0x01d6ce), true
+ case "b.omega":
+ // MATHEMATICAL BOLD SMALL OMEGA
+ return rune(0x01d6da), true
+ case "b.phi":
+ // MATHEMATICAL BOLD SMALL PHI
+ return rune(0x01d6d7), true
+ case "b.phiv":
+ // MATHEMATICAL BOLD PHI SYMBOL
+ return rune(0x01d6df), true
+ case "b.pi":
+ // MATHEMATICAL BOLD SMALL PI
+ return rune(0x01d6d1), true
+ case "b.piv":
+ // MATHEMATICAL BOLD PI SYMBOL
+ return rune(0x01d6e1), true
+ case "b.psi":
+ // MATHEMATICAL BOLD SMALL PSI
+ return rune(0x01d6d9), true
+ case "b.rho":
+ // MATHEMATICAL BOLD SMALL RHO
+ return rune(0x01d6d2), true
+ case "b.rhov":
+ // MATHEMATICAL BOLD RHO SYMBOL
+ return rune(0x01d6e0), true
+ case "b.sigmav":
+ // MATHEMATICAL BOLD SMALL FINAL SIGMA
+ return rune(0x01d6d3), true
+ case "b.sigma":
+ // MATHEMATICAL BOLD SMALL SIGMA
+ return rune(0x01d6d4), true
+ case "b.tau":
+ // MATHEMATICAL BOLD SMALL TAU
+ return rune(0x01d6d5), true
+ case "b.thetas":
+ // MATHEMATICAL BOLD SMALL THETA
+ return rune(0x01d6c9), true
+ case "b.thetav":
+ // MATHEMATICAL BOLD THETA SYMBOL
+ return rune(0x01d6dd), true
+ case "b.upsi":
+ // MATHEMATICAL BOLD SMALL UPSILON
+ return rune(0x01d6d6), true
+ case "b.xi":
+ // MATHEMATICAL BOLD SMALL XI
+ return rune(0x01d6cf), true
+ case "b.zeta":
+ // MATHEMATICAL BOLD SMALL ZETA
+ return rune(0x01d6c7), true
+ case "bNot":
+ // REVERSED DOUBLE STROKE NOT SIGN
+ return rune(0x2aed), true
+ case "backcong":
+ // ALL EQUAL TO
+ return rune(0x224c), true
+ case "backepsilon":
+ // GREEK REVERSED LUNATE EPSILON SYMBOL
+ return rune(0x03f6), true
+ case "backprime":
+ // REVERSED PRIME
+ return rune(0x2035), true
+ case "backsimeq":
+ // REVERSED TILDE EQUALS
+ return rune(0x22cd), true
+ case "backsim":
+ // REVERSED TILDE
+ return rune(0x223d), true
+ case "barV":
+ // DOUBLE DOWN TACK
+ return rune(0x2aea), true
+ case "barvee":
+ // NOR
+ return rune(0x22bd), true
+ case "barwed":
+ // PROJECTIVE
+ return rune(0x2305), true
+ case "barwedge":
+ // PROJECTIVE
+ return rune(0x2305), true
+ case "bbrk":
+ // BOTTOM SQUARE BRACKET
+ return rune(0x23b5), true
+ case "bbrktbrk":
+ // BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET
+ return rune(0x23b6), true
+ case "bcong":
+ // ALL EQUAL TO
+ return rune(0x224c), true
+ case "bcy":
+ // CYRILLIC SMALL LETTER BE
+ return rune(0x0431), true
+ case "bdlhar":
+ // DOWNWARDS HARPOON WITH BARB LEFT FROM BAR
+ return rune(0x2961), true
+ case "bdquo":
+ // DOUBLE LOW-9 QUOTATION MARK
+ return rune(0x201e), true
+ case "bdrhar":
+ // DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR
+ return rune(0x295d), true
+ case "because":
+ // BECAUSE
+ return rune(0x2235), true
+ case "becaus":
+ // BECAUSE
+ return rune(0x2235), true
+ case "bemptyv":
+ // REVERSED EMPTY SET
+ return rune(0x29b0), true
+ case "bepsi":
+ // GREEK REVERSED LUNATE EPSILON SYMBOL
+ return rune(0x03f6), true
+ case "bernou":
+ // SCRIPT CAPITAL B
+ return rune(0x212c), true
+ case "beta":
+ // GREEK SMALL LETTER BETA
+ return rune(0x03b2), true
+ case "beth":
+ // BET SYMBOL
+ return rune(0x2136), true
+ case "between":
+ // BETWEEN
+ return rune(0x226c), true
+ case "bfr":
+ // MATHEMATICAL FRAKTUR SMALL B
+ return rune(0x01d51f), true
+ case "bgr":
+ // GREEK SMALL LETTER BETA
+ return rune(0x03b2), true
+ case "bigcap":
+ // N-ARY INTERSECTION
+ return rune(0x22c2), true
+ case "bigcirc":
+ // LARGE CIRCLE
+ return rune(0x25ef), true
+ case "bigcup":
+ // N-ARY UNION
+ return rune(0x22c3), true
+ case "bigodot":
+ // N-ARY CIRCLED DOT OPERATOR
+ return rune(0x2a00), true
+ case "bigoplus":
+ // N-ARY CIRCLED PLUS OPERATOR
+ return rune(0x2a01), true
+ case "bigotimes":
+ // N-ARY CIRCLED TIMES OPERATOR
+ return rune(0x2a02), true
+ case "bigsqcup":
+ // N-ARY SQUARE UNION OPERATOR
+ return rune(0x2a06), true
+ case "bigstar":
+ // BLACK STAR
+ return rune(0x2605), true
+ case "bigtriangledown":
+ // WHITE DOWN-POINTING TRIANGLE
+ return rune(0x25bd), true
+ case "bigtriangleup":
+ // WHITE UP-POINTING TRIANGLE
+ return rune(0x25b3), true
+ case "biguplus":
+ // N-ARY UNION OPERATOR WITH PLUS
+ return rune(0x2a04), true
+ case "bigvee":
+ // N-ARY LOGICAL OR
+ return rune(0x22c1), true
+ case "bigwedge":
+ // N-ARY LOGICAL AND
+ return rune(0x22c0), true
+ case "bkarow":
+ // RIGHTWARDS DOUBLE DASH ARROW
+ return rune(0x290d), true
+ case "blacklozenge":
+ // BLACK LOZENGE
+ return rune(0x29eb), true
+ case "blacksquare":
+ // BLACK SMALL SQUARE
+ return rune(0x25aa), true
+ case "blacktriangledown":
+ // BLACK DOWN-POINTING SMALL TRIANGLE
+ return rune(0x25be), true
+ case "blacktriangleleft":
+ // BLACK LEFT-POINTING SMALL TRIANGLE
+ return rune(0x25c2), true
+ case "blacktriangleright":
+ // BLACK RIGHT-POINTING SMALL TRIANGLE
+ return rune(0x25b8), true
+ case "blacktriangle":
+ // BLACK UP-POINTING SMALL TRIANGLE
+ return rune(0x25b4), true
+ case "blank":
+ // BLANK SYMBOL
+ return rune(0x2422), true
+ case "bldhar":
+ // LEFTWARDS HARPOON WITH BARB DOWN FROM BAR
+ return rune(0x295e), true
+ case "blk12":
+ // MEDIUM SHADE
+ return rune(0x2592), true
+ case "blk14":
+ // LIGHT SHADE
+ return rune(0x2591), true
+ case "blk34":
+ // DARK SHADE
+ return rune(0x2593), true
+ case "block":
+ // FULL BLOCK
+ return rune(0x2588), true
+ case "bluhar":
+ // LEFTWARDS HARPOON WITH BARB UP FROM BAR
+ return rune(0x295a), true
+ case "bnequiv":
+ // IDENTICAL TO with reverse slash
+ return rune(0x2261), true
+ case "bne":
+ // EQUALS SIGN with reverse slash
+ return rune(0x3d), true
+ case "bnot":
+ // REVERSED NOT SIGN
+ return rune(0x2310), true
+ case "bopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL B
+ return rune(0x01d553), true
+ case "bot":
+ // UP TACK
+ return rune(0x22a5), true
+ case "bottom":
+ // UP TACK
+ return rune(0x22a5), true
+ case "bowtie":
+ // BOWTIE
+ return rune(0x22c8), true
+ case "boxDL":
+ // BOX DRAWINGS DOUBLE DOWN AND LEFT
+ return rune(0x2557), true
+ case "boxDR":
+ // BOX DRAWINGS DOUBLE DOWN AND RIGHT
+ return rune(0x2554), true
+ case "boxDl":
+ // BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+ return rune(0x2556), true
+ case "boxDr":
+ // BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+ return rune(0x2553), true
+ case "boxHD":
+ // BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+ return rune(0x2566), true
+ case "boxHU":
+ // BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+ return rune(0x2569), true
+ case "boxHd":
+ // BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+ return rune(0x2564), true
+ case "boxHu":
+ // BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+ return rune(0x2567), true
+ case "boxH":
+ // BOX DRAWINGS DOUBLE HORIZONTAL
+ return rune(0x2550), true
+ case "boxUL":
+ // BOX DRAWINGS DOUBLE UP AND LEFT
+ return rune(0x255d), true
+ case "boxUR":
+ // BOX DRAWINGS DOUBLE UP AND RIGHT
+ return rune(0x255a), true
+ case "boxUl":
+ // BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+ return rune(0x255c), true
+ case "boxUr":
+ // BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+ return rune(0x2559), true
+ case "boxVH":
+ // BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+ return rune(0x256c), true
+ case "boxVL":
+ // BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+ return rune(0x2563), true
+ case "boxVR":
+ // BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+ return rune(0x2560), true
+ case "boxVh":
+ // BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+ return rune(0x256b), true
+ case "boxVl":
+ // BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+ return rune(0x2562), true
+ case "boxVr":
+ // BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+ return rune(0x255f), true
+ case "boxV":
+ // BOX DRAWINGS DOUBLE VERTICAL
+ return rune(0x2551), true
+ case "boxbox":
+ // TWO JOINED SQUARES
+ return rune(0x29c9), true
+ case "boxdL":
+ // BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+ return rune(0x2555), true
+ case "boxdR":
+ // BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+ return rune(0x2552), true
+ case "boxdl":
+ // BOX DRAWINGS LIGHT DOWN AND LEFT
+ return rune(0x2510), true
+ case "boxdr":
+ // BOX DRAWINGS LIGHT DOWN AND RIGHT
+ return rune(0x250c), true
+ case "boxhU":
+ // BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+ return rune(0x2568), true
+ case "boxh":
+ // BOX DRAWINGS LIGHT HORIZONTAL
+ return rune(0x2500), true
+ case "boxhD":
+ // BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+ return rune(0x2565), true
+ case "boxhd":
+ // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ return rune(0x252c), true
+ case "boxhu":
+ // BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ return rune(0x2534), true
+ case "boxminus":
+ // SQUARED MINUS
+ return rune(0x229f), true
+ case "boxplus":
+ // SQUARED PLUS
+ return rune(0x229e), true
+ case "boxtimes":
+ // SQUARED TIMES
+ return rune(0x22a0), true
+ case "boxuL":
+ // BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+ return rune(0x255b), true
+ case "boxuR":
+ // BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+ return rune(0x2558), true
+ case "boxul":
+ // BOX DRAWINGS LIGHT UP AND LEFT
+ return rune(0x2518), true
+ case "boxur":
+ // BOX DRAWINGS LIGHT UP AND RIGHT
+ return rune(0x2514), true
+ case "boxvL":
+ // BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+ return rune(0x2561), true
+ case "boxvR":
+ // BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+ return rune(0x255e), true
+ case "boxvl":
+ // BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ return rune(0x2524), true
+ case "boxvr":
+ // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ return rune(0x251c), true
+ case "boxv":
+ // BOX DRAWINGS LIGHT VERTICAL
+ return rune(0x2502), true
+ case "boxvH":
+ // BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+ return rune(0x256a), true
+ case "boxvh":
+ // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ return rune(0x253c), true
+ case "bprime":
+ // REVERSED PRIME
+ return rune(0x2035), true
+ case "brdhar":
+ // RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR
+ return rune(0x295f), true
+ case "breve":
+ // BREVE
+ return rune(0x02d8), true
+ case "bruhar":
+ // RIGHTWARDS HARPOON WITH BARB UP FROM BAR
+ return rune(0x295b), true
+ case "brvbar":
+ // BROKEN BAR
+ return rune(0xa6), true
+ case "bscr":
+ // MATHEMATICAL SCRIPT SMALL B
+ return rune(0x01d4b7), true
+ case "bsemi":
+ // REVERSED SEMICOLON
+ return rune(0x204f), true
+ case "bsim":
+ // REVERSED TILDE
+ return rune(0x223d), true
+ case "bsime":
+ // REVERSED TILDE EQUALS
+ return rune(0x22cd), true
+ case "bsolb":
+ // SQUARED FALLING DIAGONAL SLASH
+ return rune(0x29c5), true
+ case "bsolhsub":
+ // REVERSE SOLIDUS PRECEDING SUBSET
+ return rune(0x27c8), true
+ case "bsol":
+ // REVERSE SOLIDUS
+ return rune(0x5c), true
+ case "btimes":
+ // SEMIDIRECT PRODUCT WITH BOTTOM CLOSED
+ return rune(0x2a32), true
+ case "bulhar":
+ // UPWARDS HARPOON WITH BARB LEFT FROM BAR
+ return rune(0x2960), true
+ case "bullet":
+ // BULLET
+ return rune(0x2022), true
+ case "bull":
+ // BULLET
+ return rune(0x2022), true
+ case "bump":
+ // GEOMETRICALLY EQUIVALENT TO
+ return rune(0x224e), true
+ case "bumpE":
+ // EQUALS SIGN WITH BUMPY ABOVE
+ return rune(0x2aae), true
+ case "bumpe":
+ // DIFFERENCE BETWEEN
+ return rune(0x224f), true
+ case "bumpeq":
+ // DIFFERENCE BETWEEN
+ return rune(0x224f), true
+ case "burhar":
+ // UPWARDS HARPOON WITH BARB RIGHT FROM BAR
+ return rune(0x295c), true
+ }
+
+ case 'c':
+ switch name {
+ case "cacute":
+ // LATIN SMALL LETTER C WITH ACUTE
+ return rune(0x0107), true
+ case "cap":
+ // INTERSECTION
+ return rune(0x2229), true
+ case "capand":
+ // INTERSECTION WITH LOGICAL AND
+ return rune(0x2a44), true
+ case "capbrcup":
+ // INTERSECTION ABOVE BAR ABOVE UNION
+ return rune(0x2a49), true
+ case "capcap":
+ // INTERSECTION BESIDE AND JOINED WITH INTERSECTION
+ return rune(0x2a4b), true
+ case "capcup":
+ // INTERSECTION ABOVE UNION
+ return rune(0x2a47), true
+ case "capdot":
+ // INTERSECTION WITH DOT
+ return rune(0x2a40), true
+ case "capint":
+ // INTEGRAL WITH INTERSECTION
+ return rune(0x2a19), true
+ case "caps":
+ // INTERSECTION with serifs
+ return rune(0x2229), true
+ case "caret":
+ // CARET INSERTION POINT
+ return rune(0x2041), true
+ case "caron":
+ // CARON
+ return rune(0x02c7), true
+ case "ccaps":
+ // CLOSED INTERSECTION WITH SERIFS
+ return rune(0x2a4d), true
+ case "ccaron":
+ // LATIN SMALL LETTER C WITH CARON
+ return rune(0x010d), true
+ case "ccedil":
+ // LATIN SMALL LETTER C WITH CEDILLA
+ return rune(0xe7), true
+ case "ccirc":
+ // LATIN SMALL LETTER C WITH CIRCUMFLEX
+ return rune(0x0109), true
+ case "ccups":
+ // CLOSED UNION WITH SERIFS
+ return rune(0x2a4c), true
+ case "ccupssm":
+ // CLOSED UNION WITH SERIFS AND SMASH PRODUCT
+ return rune(0x2a50), true
+ case "cdot":
+ // LATIN SMALL LETTER C WITH DOT ABOVE
+ return rune(0x010b), true
+ case "cedil":
+ // CEDILLA
+ return rune(0xb8), true
+ case "cemptyv":
+ // EMPTY SET WITH SMALL CIRCLE ABOVE
+ return rune(0x29b2), true
+ case "centerdot":
+ // MIDDLE DOT
+ return rune(0xb7), true
+ case "cent":
+ // CENT SIGN
+ return rune(0xa2), true
+ case "cfr":
+ // MATHEMATICAL FRAKTUR SMALL C
+ return rune(0x01d520), true
+ case "chcy":
+ // CYRILLIC SMALL LETTER CHE
+ return rune(0x0447), true
+ case "check":
+ // CHECK MARK
+ return rune(0x2713), true
+ case "checkmark":
+ // CHECK MARK
+ return rune(0x2713), true
+ case "chi":
+ // GREEK SMALL LETTER CHI
+ return rune(0x03c7), true
+ case "circeq":
+ // RING EQUAL TO
+ return rune(0x2257), true
+ case "circlearrowleft":
+ // ANTICLOCKWISE OPEN CIRCLE ARROW
+ return rune(0x21ba), true
+ case "circlearrowright":
+ // CLOCKWISE OPEN CIRCLE ARROW
+ return rune(0x21bb), true
+ case "circledS":
+ // CIRCLED LATIN CAPITAL LETTER S
+ return rune(0x24c8), true
+ case "circledast":
+ // CIRCLED ASTERISK OPERATOR
+ return rune(0x229b), true
+ case "circledcirc":
+ // CIRCLED RING OPERATOR
+ return rune(0x229a), true
+ case "circleddash":
+ // CIRCLED DASH
+ return rune(0x229d), true
+ case "cire":
+ // RING EQUAL TO
+ return rune(0x2257), true
+ case "cir":
+ // WHITE CIRCLE
+ return rune(0x25cb), true
+ case "cirE":
+ // CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT
+ return rune(0x29c3), true
+ case "cirb":
+ // SQUARED SMALL CIRCLE
+ return rune(0x29c7), true
+ case "circ":
+ // MODIFIER LETTER CIRCUMFLEX ACCENT
+ return rune(0x02c6), true
+ case "circledR":
+ // REGISTERED SIGN
+ return rune(0xae), true
+ case "cirdarr":
+ // WHITE CIRCLE WITH DOWN ARROW
+ return rune(0x29ec), true
+ case "cirerr":
+ // ERROR-BARRED WHITE CIRCLE
+ return rune(0x29f2), true
+ case "cirfdarr":
+ // BLACK CIRCLE WITH DOWN ARROW
+ return rune(0x29ed), true
+ case "cirferr":
+ // ERROR-BARRED BLACK CIRCLE
+ return rune(0x29f3), true
+ case "cirfnint":
+ // CIRCULATION FUNCTION
+ return rune(0x2a10), true
+ case "cirmid":
+ // VERTICAL LINE WITH CIRCLE ABOVE
+ return rune(0x2aef), true
+ case "cirscir":
+ // CIRCLE WITH SMALL CIRCLE TO THE RIGHT
+ return rune(0x29c2), true
+ case "closur":
+ // CLOSE UP
+ return rune(0x2050), true
+ case "clubs":
+ // BLACK CLUB SUIT
+ return rune(0x2663), true
+ case "clubsuit":
+ // BLACK CLUB SUIT
+ return rune(0x2663), true
+ case "colone":
+ // COLON EQUALS
+ return rune(0x2254), true
+ case "coloneq":
+ // COLON EQUALS
+ return rune(0x2254), true
+ case "colon":
+ // COLON
+ return rune(0x3a), true
+ case "commat":
+ // COMMERCIAL AT
+ return rune(0x40), true
+ case "comma":
+ // COMMA
+ return rune(0x2c), true
+ case "comp":
+ // COMPLEMENT
+ return rune(0x2201), true
+ case "compfn":
+ // RING OPERATOR
+ return rune(0x2218), true
+ case "complement":
+ // COMPLEMENT
+ return rune(0x2201), true
+ case "complexes":
+ // DOUBLE-STRUCK CAPITAL C
+ return rune(0x2102), true
+ case "cong":
+ // APPROXIMATELY EQUAL TO
+ return rune(0x2245), true
+ case "congdot":
+ // CONGRUENT WITH DOT ABOVE
+ return rune(0x2a6d), true
+ case "conint":
+ // CONTOUR INTEGRAL
+ return rune(0x222e), true
+ case "copf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL C
+ return rune(0x01d554), true
+ case "coprod":
+ // N-ARY COPRODUCT
+ return rune(0x2210), true
+ case "copysr":
+ // SOUND RECORDING COPYRIGHT
+ return rune(0x2117), true
+ case "copy":
+ // COPYRIGHT SIGN
+ return rune(0xa9), true
+ case "crarr":
+ // DOWNWARDS ARROW WITH CORNER LEFTWARDS
+ return rune(0x21b5), true
+ case "cross":
+ // BALLOT X
+ return rune(0x2717), true
+ case "cscr":
+ // MATHEMATICAL SCRIPT SMALL C
+ return rune(0x01d4b8), true
+ case "csub":
+ // CLOSED SUBSET
+ return rune(0x2acf), true
+ case "csube":
+ // CLOSED SUBSET OR EQUAL TO
+ return rune(0x2ad1), true
+ case "csup":
+ // CLOSED SUPERSET
+ return rune(0x2ad0), true
+ case "csupe":
+ // CLOSED SUPERSET OR EQUAL TO
+ return rune(0x2ad2), true
+ case "ctdot":
+ // MIDLINE HORIZONTAL ELLIPSIS
+ return rune(0x22ef), true
+ case "cudarrl":
+ // RIGHT-SIDE ARC CLOCKWISE ARROW
+ return rune(0x2938), true
+ case "cudarrr":
+ // ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS
+ return rune(0x2935), true
+ case "cuepr":
+ // EQUAL TO OR PRECEDES
+ return rune(0x22de), true
+ case "cuesc":
+ // EQUAL TO OR SUCCEEDS
+ return rune(0x22df), true
+ case "cularr":
+ // ANTICLOCKWISE TOP SEMICIRCLE ARROW
+ return rune(0x21b6), true
+ case "cularrp":
+ // TOP ARC ANTICLOCKWISE ARROW WITH PLUS
+ return rune(0x293d), true
+ case "cup":
+ // UNION
+ return rune(0x222a), true
+ case "cupbrcap":
+ // UNION ABOVE BAR ABOVE INTERSECTION
+ return rune(0x2a48), true
+ case "cupcap":
+ // UNION ABOVE INTERSECTION
+ return rune(0x2a46), true
+ case "cupcup":
+ // UNION BESIDE AND JOINED WITH UNION
+ return rune(0x2a4a), true
+ case "cupdot":
+ // MULTISET MULTIPLICATION
+ return rune(0x228d), true
+ case "cupint":
+ // INTEGRAL WITH UNION
+ return rune(0x2a1a), true
+ case "cupor":
+ // UNION WITH LOGICAL OR
+ return rune(0x2a45), true
+ case "cupre":
+ // PRECEDES OR EQUAL TO
+ return rune(0x227c), true
+ case "cups":
+ // UNION with serifs
+ return rune(0x222a), true
+ case "curarr":
+ // CLOCKWISE TOP SEMICIRCLE ARROW
+ return rune(0x21b7), true
+ case "curarrm":
+ // TOP ARC CLOCKWISE ARROW WITH MINUS
+ return rune(0x293c), true
+ case "curlyeqprec":
+ // EQUAL TO OR PRECEDES
+ return rune(0x22de), true
+ case "curlyeqsucc":
+ // EQUAL TO OR SUCCEEDS
+ return rune(0x22df), true
+ case "curlyvee":
+ // CURLY LOGICAL OR
+ return rune(0x22ce), true
+ case "curlywedge":
+ // CURLY LOGICAL AND
+ return rune(0x22cf), true
+ case "curren":
+ // CURRENCY SIGN
+ return rune(0xa4), true
+ case "curvearrowleft":
+ // ANTICLOCKWISE TOP SEMICIRCLE ARROW
+ return rune(0x21b6), true
+ case "curvearrowright":
+ // CLOCKWISE TOP SEMICIRCLE ARROW
+ return rune(0x21b7), true
+ case "cuvee":
+ // CURLY LOGICAL OR
+ return rune(0x22ce), true
+ case "cuwed":
+ // CURLY LOGICAL AND
+ return rune(0x22cf), true
+ case "cwconint":
+ // CLOCKWISE CONTOUR INTEGRAL
+ return rune(0x2232), true
+ case "cwint":
+ // CLOCKWISE INTEGRAL
+ return rune(0x2231), true
+ case "cylcty":
+ // CYLINDRICITY
+ return rune(0x232d), true
+ }
+
+ case 'd':
+ switch name {
+ case "dAarr":
+ // DOWNWARDS TRIPLE ARROW
+ return rune(0x290b), true
+ case "dArr":
+ // DOWNWARDS DOUBLE ARROW
+ return rune(0x21d3), true
+ case "dHar":
+ // DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ return rune(0x2965), true
+ case "dagger":
+ // DAGGER
+ return rune(0x2020), true
+ case "dalembrt":
+ // SQUARE WITH CONTOURED OUTLINE
+ return rune(0x29e0), true
+ case "daleth":
+ // DALET SYMBOL
+ return rune(0x2138), true
+ case "darr2":
+ // DOWNWARDS PAIRED ARROWS
+ return rune(0x21ca), true
+ case "darr":
+ // DOWNWARDS ARROW
+ return rune(0x2193), true
+ case "darrb":
+ // DOWNWARDS ARROW TO BAR
+ return rune(0x2913), true
+ case "darrln":
+ // DOWNWARDS ARROW WITH HORIZONTAL STROKE
+ return rune(0x2908), true
+ case "dashv":
+ // LEFT TACK
+ return rune(0x22a3), true
+ case "dash":
+ // HYPHEN
+ return rune(0x2010), true
+ case "dashV":
+ // DOUBLE VERTICAL BAR LEFT TURNSTILE
+ return rune(0x2ae3), true
+ case "dbkarow":
+ // RIGHTWARDS TRIPLE DASH ARROW
+ return rune(0x290f), true
+ case "dblac":
+ // DOUBLE ACUTE ACCENT
+ return rune(0x02dd), true
+ case "dcaron":
+ // LATIN SMALL LETTER D WITH CARON
+ return rune(0x010f), true
+ case "dcy":
+ // CYRILLIC SMALL LETTER DE
+ return rune(0x0434), true
+ case "ddarr":
+ // DOWNWARDS PAIRED ARROWS
+ return rune(0x21ca), true
+ case "dd":
+ // DOUBLE-STRUCK ITALIC SMALL D
+ return rune(0x2146), true
+ case "ddagger":
+ // DOUBLE DAGGER
+ return rune(0x2021), true
+ case "ddotseq":
+ // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
+ return rune(0x2a77), true
+ case "deg":
+ // DEGREE SIGN
+ return rune(0xb0), true
+ case "delta":
+ // GREEK SMALL LETTER DELTA
+ return rune(0x03b4), true
+ case "demptyv":
+ // EMPTY SET WITH OVERBAR
+ return rune(0x29b1), true
+ case "dfisht":
+ // DOWN FISH TAIL
+ return rune(0x297f), true
+ case "dfr":
+ // MATHEMATICAL FRAKTUR SMALL D
+ return rune(0x01d521), true
+ case "dgr":
+ // GREEK SMALL LETTER DELTA
+ return rune(0x03b4), true
+ case "dharl":
+ // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21c3), true
+ case "dharr":
+ // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21c2), true
+ case "diam":
+ // DIAMOND OPERATOR
+ return rune(0x22c4), true
+ case "diamdarr":
+ // BLACK DIAMOND WITH DOWN ARROW
+ return rune(0x29ea), true
+ case "diamerr":
+ // ERROR-BARRED WHITE DIAMOND
+ return rune(0x29f0), true
+ case "diamerrf":
+ // ERROR-BARRED BLACK DIAMOND
+ return rune(0x29f1), true
+ case "diamond":
+ // DIAMOND OPERATOR
+ return rune(0x22c4), true
+ case "diamondsuit":
+ // BLACK DIAMOND SUIT
+ return rune(0x2666), true
+ case "diams":
+ // BLACK DIAMOND SUIT
+ return rune(0x2666), true
+ case "die":
+ // DIAERESIS
+ return rune(0xa8), true
+ case "digamma":
+ // GREEK SMALL LETTER DIGAMMA
+ return rune(0x03dd), true
+ case "disin":
+ // ELEMENT OF WITH LONG HORIZONTAL STROKE
+ return rune(0x22f2), true
+ case "divideontimes":
+ // DIVISION TIMES
+ return rune(0x22c7), true
+ case "divonx":
+ // DIVISION TIMES
+ return rune(0x22c7), true
+ case "div":
+ // DIVISION SIGN
+ return rune(0xf7), true
+ case "divide":
+ // DIVISION SIGN
+ return rune(0xf7), true
+ case "djcy":
+ // CYRILLIC SMALL LETTER DJE
+ return rune(0x0452), true
+ case "dlarr":
+ // SOUTH WEST ARROW
+ return rune(0x2199), true
+ case "dlcorn":
+ // BOTTOM LEFT CORNER
+ return rune(0x231e), true
+ case "dlcrop":
+ // BOTTOM LEFT CROP
+ return rune(0x230d), true
+ case "dlharb":
+ // DOWNWARDS HARPOON WITH BARB LEFT TO BAR
+ return rune(0x2959), true
+ case "dollar":
+ // DOLLAR SIGN
+ return rune(0x24), true
+ case "dopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL D
+ return rune(0x01d555), true
+ case "doteq":
+ // APPROACHES THE LIMIT
+ return rune(0x2250), true
+ case "doteqdot":
+ // GEOMETRICALLY EQUAL TO
+ return rune(0x2251), true
+ case "dotminus":
+ // DOT MINUS
+ return rune(0x2238), true
+ case "dotplus":
+ // DOT PLUS
+ return rune(0x2214), true
+ case "dotsquare":
+ // SQUARED DOT OPERATOR
+ return rune(0x22a1), true
+ case "dot":
+ // DOT ABOVE
+ return rune(0x02d9), true
+ case "doublebarwedge":
+ // PERSPECTIVE
+ return rune(0x2306), true
+ case "downarrow":
+ // DOWNWARDS ARROW
+ return rune(0x2193), true
+ case "downdownarrows":
+ // DOWNWARDS PAIRED ARROWS
+ return rune(0x21ca), true
+ case "downharpoonleft":
+ // DOWNWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21c3), true
+ case "downharpoonright":
+ // DOWNWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21c2), true
+ case "drarr":
+ // SOUTH EAST ARROW
+ return rune(0x2198), true
+ case "drbkarow":
+ // RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW
+ return rune(0x2910), true
+ case "drcorn":
+ // BOTTOM RIGHT CORNER
+ return rune(0x231f), true
+ case "drcrop":
+ // BOTTOM RIGHT CROP
+ return rune(0x230c), true
+ case "drharb":
+ // DOWNWARDS HARPOON WITH BARB RIGHT TO BAR
+ return rune(0x2955), true
+ case "dscr":
+ // MATHEMATICAL SCRIPT SMALL D
+ return rune(0x01d4b9), true
+ case "dscy":
+ // CYRILLIC SMALL LETTER DZE
+ return rune(0x0455), true
+ case "dsol":
+ // SOLIDUS WITH OVERBAR
+ return rune(0x29f6), true
+ case "dstrok":
+ // LATIN SMALL LETTER D WITH STROKE
+ return rune(0x0111), true
+ case "dtdot":
+ // DOWN RIGHT DIAGONAL ELLIPSIS
+ return rune(0x22f1), true
+ case "dtrif":
+ // BLACK DOWN-POINTING SMALL TRIANGLE
+ return rune(0x25be), true
+ case "dtri":
+ // WHITE DOWN-POINTING SMALL TRIANGLE
+ return rune(0x25bf), true
+ case "dtrilf":
+ // DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK
+ return rune(0x29e8), true
+ case "dtrirf":
+ // DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK
+ return rune(0x29e9), true
+ case "duarr":
+ // DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW
+ return rune(0x21f5), true
+ case "duhar":
+ // DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ return rune(0x296f), true
+ case "dumap":
+ // DOUBLE-ENDED MULTIMAP
+ return rune(0x29df), true
+ case "dwangle":
+ // OBLIQUE ANGLE OPENING UP
+ return rune(0x29a6), true
+ case "dzcy":
+ // CYRILLIC SMALL LETTER DZHE
+ return rune(0x045f), true
+ case "dzigrarr":
+ // LONG RIGHTWARDS SQUIGGLE ARROW
+ return rune(0x27ff), true
+ }
+
+ case 'e':
+ switch name {
+ case "eDDot":
+ // EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW
+ return rune(0x2a77), true
+ case "eDot":
+ // GEOMETRICALLY EQUAL TO
+ return rune(0x2251), true
+ case "eacgr":
+ // GREEK SMALL LETTER EPSILON WITH TONOS
+ return rune(0x03ad), true
+ case "eacute":
+ // LATIN SMALL LETTER E WITH ACUTE
+ return rune(0xe9), true
+ case "easter":
+ // EQUALS WITH ASTERISK
+ return rune(0x2a6e), true
+ case "ecaron":
+ // LATIN SMALL LETTER E WITH CARON
+ return rune(0x011b), true
+ case "ecir":
+ // RING IN EQUAL TO
+ return rune(0x2256), true
+ case "ecirc":
+ // LATIN SMALL LETTER E WITH CIRCUMFLEX
+ return rune(0xea), true
+ case "ecolon":
+ // EQUALS COLON
+ return rune(0x2255), true
+ case "ecy":
+ // CYRILLIC SMALL LETTER E
+ return rune(0x044d), true
+ case "edot":
+ // LATIN SMALL LETTER E WITH DOT ABOVE
+ return rune(0x0117), true
+ case "ee":
+ // DOUBLE-STRUCK ITALIC SMALL E
+ return rune(0x2147), true
+ case "eeacgr":
+ // GREEK SMALL LETTER ETA WITH TONOS
+ return rune(0x03ae), true
+ case "eegr":
+ // GREEK SMALL LETTER ETA
+ return rune(0x03b7), true
+ case "efDot":
+ // APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ return rune(0x2252), true
+ case "efr":
+ // MATHEMATICAL FRAKTUR SMALL E
+ return rune(0x01d522), true
+ case "egr":
+ // GREEK SMALL LETTER EPSILON
+ return rune(0x03b5), true
+ case "egs":
+ // SLANTED EQUAL TO OR GREATER-THAN
+ return rune(0x2a96), true
+ case "egsdot":
+ // SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE
+ return rune(0x2a98), true
+ case "eg":
+ // DOUBLE-LINE EQUAL TO OR GREATER-THAN
+ return rune(0x2a9a), true
+ case "egrave":
+ // LATIN SMALL LETTER E WITH GRAVE
+ return rune(0xe8), true
+ case "elinters":
+ // ELECTRICAL INTERSECTION
+ return rune(0x23e7), true
+ case "ell":
+ // SCRIPT SMALL L
+ return rune(0x2113), true
+ case "els":
+ // SLANTED EQUAL TO OR LESS-THAN
+ return rune(0x2a95), true
+ case "elsdot":
+ // SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE
+ return rune(0x2a97), true
+ case "el":
+ // DOUBLE-LINE EQUAL TO OR LESS-THAN
+ return rune(0x2a99), true
+ case "emacr":
+ // LATIN SMALL LETTER E WITH MACRON
+ return rune(0x0113), true
+ case "empty":
+ // EMPTY SET
+ return rune(0x2205), true
+ case "emptyset":
+ // EMPTY SET
+ return rune(0x2205), true
+ case "emptyv":
+ // EMPTY SET
+ return rune(0x2205), true
+ case "emsp13":
+ // THREE-PER-EM SPACE
+ return rune(0x2004), true
+ case "emsp14":
+ // FOUR-PER-EM SPACE
+ return rune(0x2005), true
+ case "emsp":
+ // EM SPACE
+ return rune(0x2003), true
+ case "eng":
+ // LATIN SMALL LETTER ENG
+ return rune(0x014b), true
+ case "ensp":
+ // EN SPACE
+ return rune(0x2002), true
+ case "eogon":
+ // LATIN SMALL LETTER E WITH OGONEK
+ return rune(0x0119), true
+ case "eopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL E
+ return rune(0x01d556), true
+ case "epar":
+ // EQUAL AND PARALLEL TO
+ return rune(0x22d5), true
+ case "eparsl":
+ // EQUALS SIGN AND SLANTED PARALLEL
+ return rune(0x29e3), true
+ case "eplus":
+ // EQUALS SIGN ABOVE PLUS SIGN
+ return rune(0x2a71), true
+ case "epsilon":
+ // GREEK SMALL LETTER EPSILON
+ return rune(0x03b5), true
+ case "epsis":
+ // GREEK LUNATE EPSILON SYMBOL
+ return rune(0x03f5), true
+ case "epsiv":
+ // GREEK LUNATE EPSILON SYMBOL
+ return rune(0x03f5), true
+ case "epsi":
+ // GREEK SMALL LETTER EPSILON
+ return rune(0x03b5), true
+ case "eqcirc":
+ // RING IN EQUAL TO
+ return rune(0x2256), true
+ case "eqcolon":
+ // EQUALS COLON
+ return rune(0x2255), true
+ case "eqeq":
+ // TWO CONSECUTIVE EQUALS SIGNS
+ return rune(0x2a75), true
+ case "eqsim":
+ // MINUS TILDE
+ return rune(0x2242), true
+ case "eqslantgtr":
+ // SLANTED EQUAL TO OR GREATER-THAN
+ return rune(0x2a96), true
+ case "eqslantless":
+ // SLANTED EQUAL TO OR LESS-THAN
+ return rune(0x2a95), true
+ case "equals":
+ // EQUALS SIGN
+ return rune(0x3d), true
+ case "equest":
+ // QUESTIONED EQUAL TO
+ return rune(0x225f), true
+ case "equiv":
+ // IDENTICAL TO
+ return rune(0x2261), true
+ case "equivDD":
+ // EQUIVALENT WITH FOUR DOTS ABOVE
+ return rune(0x2a78), true
+ case "eqvparsl":
+ // IDENTICAL TO AND SLANTED PARALLEL
+ return rune(0x29e5), true
+ case "erDot":
+ // IMAGE OF OR APPROXIMATELY EQUAL TO
+ return rune(0x2253), true
+ case "erarr":
+ // EQUALS SIGN ABOVE RIGHTWARDS ARROW
+ return rune(0x2971), true
+ case "escr":
+ // SCRIPT SMALL E
+ return rune(0x212f), true
+ case "esdot":
+ // APPROACHES THE LIMIT
+ return rune(0x2250), true
+ case "esim":
+ // MINUS TILDE
+ return rune(0x2242), true
+ case "eta":
+ // GREEK SMALL LETTER ETA
+ return rune(0x03b7), true
+ case "eth":
+ // LATIN SMALL LETTER ETH
+ return rune(0xf0), true
+ case "euml":
+ // LATIN SMALL LETTER E WITH DIAERESIS
+ return rune(0xeb), true
+ case "euro":
+ // EURO SIGN
+ return rune(0x20ac), true
+ case "excl":
+ // EXCLAMATION MARK
+ return rune(0x21), true
+ case "exist":
+ // THERE EXISTS
+ return rune(0x2203), true
+ case "expectation":
+ // SCRIPT CAPITAL E
+ return rune(0x2130), true
+ case "exponentiale":
+ // DOUBLE-STRUCK ITALIC SMALL E
+ return rune(0x2147), true
+ }
+
+ case 'f':
+ switch name {
+ case "fallingdotseq":
+ // APPROXIMATELY EQUAL TO OR THE IMAGE OF
+ return rune(0x2252), true
+ case "fbowtie":
+ // BLACK BOWTIE
+ return rune(0x29d3), true
+ case "fcy":
+ // CYRILLIC SMALL LETTER EF
+ return rune(0x0444), true
+ case "fdiag":
+ // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+ return rune(0x2572), true
+ case "fdiordi":
+ // FALLING DIAGONAL CROSSING RISING DIAGONAL
+ return rune(0x292c), true
+ case "fdonearr":
+ // FALLING DIAGONAL CROSSING NORTH EAST ARROW
+ return rune(0x292f), true
+ case "female":
+ // FEMALE SIGN
+ return rune(0x2640), true
+ case "ffilig":
+ // LATIN SMALL LIGATURE FFI
+ return rune(0xfb03), true
+ case "fflig":
+ // LATIN SMALL LIGATURE FF
+ return rune(0xfb00), true
+ case "ffllig":
+ // LATIN SMALL LIGATURE FFL
+ return rune(0xfb04), true
+ case "ffr":
+ // MATHEMATICAL FRAKTUR SMALL F
+ return rune(0x01d523), true
+ case "fhrglass":
+ // BLACK HOURGLASS
+ return rune(0x29d7), true
+ case "filig":
+ // LATIN SMALL LIGATURE FI
+ return rune(0xfb01), true
+ case "fjlig":
+ // fj ligature
+ return rune(0x66), true
+ case "flat":
+ // MUSIC FLAT SIGN
+ return rune(0x266d), true
+ case "fllig":
+ // LATIN SMALL LIGATURE FL
+ return rune(0xfb02), true
+ case "fltns":
+ // WHITE PARALLELOGRAM
+ return rune(0x25b1), true
+ case "fnof":
+ // LATIN SMALL LETTER F WITH HOOK
+ return rune(0x0192), true
+ case "fopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL F
+ return rune(0x01d557), true
+ case "forall":
+ // FOR ALL
+ return rune(0x2200), true
+ case "fork":
+ // PITCHFORK
+ return rune(0x22d4), true
+ case "forkv":
+ // ELEMENT OF OPENING DOWNWARDS
+ return rune(0x2ad9), true
+ case "fpartint":
+ // FINITE PART INTEGRAL
+ return rune(0x2a0d), true
+ case "frac12":
+ // VULGAR FRACTION ONE HALF
+ return rune(0xbd), true
+ case "frac13":
+ // VULGAR FRACTION ONE THIRD
+ return rune(0x2153), true
+ case "frac14":
+ // VULGAR FRACTION ONE QUARTER
+ return rune(0xbc), true
+ case "frac15":
+ // VULGAR FRACTION ONE FIFTH
+ return rune(0x2155), true
+ case "frac16":
+ // VULGAR FRACTION ONE SIXTH
+ return rune(0x2159), true
+ case "frac18":
+ // VULGAR FRACTION ONE EIGHTH
+ return rune(0x215b), true
+ case "frac23":
+ // VULGAR FRACTION TWO THIRDS
+ return rune(0x2154), true
+ case "frac25":
+ // VULGAR FRACTION TWO FIFTHS
+ return rune(0x2156), true
+ case "frac34":
+ // VULGAR FRACTION THREE QUARTERS
+ return rune(0xbe), true
+ case "frac35":
+ // VULGAR FRACTION THREE FIFTHS
+ return rune(0x2157), true
+ case "frac38":
+ // VULGAR FRACTION THREE EIGHTHS
+ return rune(0x215c), true
+ case "frac45":
+ // VULGAR FRACTION FOUR FIFTHS
+ return rune(0x2158), true
+ case "frac56":
+ // VULGAR FRACTION FIVE SIXTHS
+ return rune(0x215a), true
+ case "frac58":
+ // VULGAR FRACTION FIVE EIGHTHS
+ return rune(0x215d), true
+ case "frac78":
+ // VULGAR FRACTION SEVEN EIGHTHS
+ return rune(0x215e), true
+ case "frasl":
+ // FRACTION SLASH
+ return rune(0x2044), true
+ case "frown":
+ // FROWN
+ return rune(0x2322), true
+ case "fscr":
+ // MATHEMATICAL SCRIPT SMALL F
+ return rune(0x01d4bb), true
+ }
+
+ case 'g':
+ switch name {
+ case "gE":
+ // GREATER-THAN OVER EQUAL TO
+ return rune(0x2267), true
+ case "gEl":
+ // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+ return rune(0x2a8c), true
+ case "gacute":
+ // LATIN SMALL LETTER G WITH ACUTE
+ return rune(0x01f5), true
+ case "gammad":
+ // GREEK SMALL LETTER DIGAMMA
+ return rune(0x03dd), true
+ case "gamma":
+ // GREEK SMALL LETTER GAMMA
+ return rune(0x03b3), true
+ case "gap":
+ // GREATER-THAN OR APPROXIMATE
+ return rune(0x2a86), true
+ case "gbreve":
+ // LATIN SMALL LETTER G WITH BREVE
+ return rune(0x011f), true
+ case "gcedil":
+ // LATIN SMALL LETTER G WITH CEDILLA
+ return rune(0x0123), true
+ case "gcirc":
+ // LATIN SMALL LETTER G WITH CIRCUMFLEX
+ return rune(0x011d), true
+ case "gcy":
+ // CYRILLIC SMALL LETTER GHE
+ return rune(0x0433), true
+ case "gdot":
+ // LATIN SMALL LETTER G WITH DOT ABOVE
+ return rune(0x0121), true
+ case "ge":
+ // GREATER-THAN OR EQUAL TO
+ return rune(0x2265), true
+ case "gel":
+ // GREATER-THAN EQUAL TO OR LESS-THAN
+ return rune(0x22db), true
+ case "geq":
+ // GREATER-THAN OR EQUAL TO
+ return rune(0x2265), true
+ case "geqq":
+ // GREATER-THAN OVER EQUAL TO
+ return rune(0x2267), true
+ case "geqslant":
+ // GREATER-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7e), true
+ case "gesl":
+ // GREATER-THAN slanted EQUAL TO OR LESS-THAN
+ return rune(0x22db), true
+ case "ges":
+ // GREATER-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7e), true
+ case "gescc":
+ // GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ return rune(0x2aa9), true
+ case "gesdot":
+ // GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ return rune(0x2a80), true
+ case "gesdoto":
+ // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ return rune(0x2a82), true
+ case "gesdotol":
+ // GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT
+ return rune(0x2a84), true
+ case "gesles":
+ // GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL
+ return rune(0x2a94), true
+ case "gfr":
+ // MATHEMATICAL FRAKTUR SMALL G
+ return rune(0x01d524), true
+ case "gg":
+ // MUCH GREATER-THAN
+ return rune(0x226b), true
+ case "ggg":
+ // VERY MUCH GREATER-THAN
+ return rune(0x22d9), true
+ case "ggr":
+ // GREEK SMALL LETTER GAMMA
+ return rune(0x03b3), true
+ case "gimel":
+ // GIMEL SYMBOL
+ return rune(0x2137), true
+ case "gjcy":
+ // CYRILLIC SMALL LETTER GJE
+ return rune(0x0453), true
+ case "gl":
+ // GREATER-THAN OR LESS-THAN
+ return rune(0x2277), true
+ case "glE":
+ // GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL
+ return rune(0x2a92), true
+ case "gla":
+ // GREATER-THAN BESIDE LESS-THAN
+ return rune(0x2aa5), true
+ case "glj":
+ // GREATER-THAN OVERLAPPING LESS-THAN
+ return rune(0x2aa4), true
+ case "gnE":
+ // GREATER-THAN BUT NOT EQUAL TO
+ return rune(0x2269), true
+ case "gnap":
+ // GREATER-THAN AND NOT APPROXIMATE
+ return rune(0x2a8a), true
+ case "gnapprox":
+ // GREATER-THAN AND NOT APPROXIMATE
+ return rune(0x2a8a), true
+ case "gneqq":
+ // GREATER-THAN BUT NOT EQUAL TO
+ return rune(0x2269), true
+ case "gne":
+ // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
+ return rune(0x2a88), true
+ case "gneq":
+ // GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
+ return rune(0x2a88), true
+ case "gnsim":
+ // GREATER-THAN BUT NOT EQUIVALENT TO
+ return rune(0x22e7), true
+ case "gopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL G
+ return rune(0x01d558), true
+ case "grave":
+ // GRAVE ACCENT
+ return rune(0x60), true
+ case "gscr":
+ // SCRIPT SMALL G
+ return rune(0x210a), true
+ case "gsdot":
+ // GREATER-THAN WITH DOT
+ return rune(0x22d7), true
+ case "gsim":
+ // GREATER-THAN OR EQUIVALENT TO
+ return rune(0x2273), true
+ case "gsime":
+ // GREATER-THAN ABOVE SIMILAR OR EQUAL
+ return rune(0x2a8e), true
+ case "gsiml":
+ // GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN
+ return rune(0x2a90), true
+ case "gtcc":
+ // GREATER-THAN CLOSED BY CURVE
+ return rune(0x2aa7), true
+ case "gtcir":
+ // GREATER-THAN WITH CIRCLE INSIDE
+ return rune(0x2a7a), true
+ case "gtdot":
+ // GREATER-THAN WITH DOT
+ return rune(0x22d7), true
+ case "gtlPar":
+ // DOUBLE LEFT ARC GREATER-THAN BRACKET
+ return rune(0x2995), true
+ case "gtquest":
+ // GREATER-THAN WITH QUESTION MARK ABOVE
+ return rune(0x2a7c), true
+ case "gtrapprox":
+ // GREATER-THAN OR APPROXIMATE
+ return rune(0x2a86), true
+ case "gtrarr":
+ // GREATER-THAN ABOVE RIGHTWARDS ARROW
+ return rune(0x2978), true
+ case "gtrdot":
+ // GREATER-THAN WITH DOT
+ return rune(0x22d7), true
+ case "gtreqless":
+ // GREATER-THAN EQUAL TO OR LESS-THAN
+ return rune(0x22db), true
+ case "gtreqqless":
+ // GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN
+ return rune(0x2a8c), true
+ case "gtrless":
+ // GREATER-THAN OR LESS-THAN
+ return rune(0x2277), true
+ case "gtrpar":
+ // SPHERICAL ANGLE OPENING LEFT
+ return rune(0x29a0), true
+ case "gtrsim":
+ // GREATER-THAN OR EQUIVALENT TO
+ return rune(0x2273), true
+ case "gt":
+ // GREATER-THAN SIGN
+ return rune(0x3e), true
+ case "gvertneqq":
+ // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
+ return rune(0x2269), true
+ case "gvnE":
+ // GREATER-THAN BUT NOT EQUAL TO - with vertical stroke
+ return rune(0x2269), true
+ }
+
+ case 'h':
+ switch name {
+ case "hArr":
+ // LEFT RIGHT DOUBLE ARROW
+ return rune(0x21d4), true
+ case "hairsp":
+ // HAIR SPACE
+ return rune(0x200a), true
+ case "half":
+ // VULGAR FRACTION ONE HALF
+ return rune(0xbd), true
+ case "hamilt":
+ // SCRIPT CAPITAL H
+ return rune(0x210b), true
+ case "hardcy":
+ // CYRILLIC SMALL LETTER HARD SIGN
+ return rune(0x044a), true
+ case "harrw":
+ // LEFT RIGHT WAVE ARROW
+ return rune(0x21ad), true
+ case "harr":
+ // LEFT RIGHT ARROW
+ return rune(0x2194), true
+ case "harrcir":
+ // LEFT RIGHT ARROW THROUGH SMALL CIRCLE
+ return rune(0x2948), true
+ case "hbar":
+ // PLANCK CONSTANT OVER TWO PI
+ return rune(0x210f), true
+ case "hcirc":
+ // LATIN SMALL LETTER H WITH CIRCUMFLEX
+ return rune(0x0125), true
+ case "hearts":
+ // BLACK HEART SUIT
+ return rune(0x2665), true
+ case "heartsuit":
+ // BLACK HEART SUIT
+ return rune(0x2665), true
+ case "hellip":
+ // HORIZONTAL ELLIPSIS
+ return rune(0x2026), true
+ case "hercon":
+ // HERMITIAN CONJUGATE MATRIX
+ return rune(0x22b9), true
+ case "hfr":
+ // MATHEMATICAL FRAKTUR SMALL H
+ return rune(0x01d525), true
+ case "hksearow":
+ // SOUTH EAST ARROW WITH HOOK
+ return rune(0x2925), true
+ case "hkswarow":
+ // SOUTH WEST ARROW WITH HOOK
+ return rune(0x2926), true
+ case "hoarr":
+ // LEFT RIGHT OPEN-HEADED ARROW
+ return rune(0x21ff), true
+ case "homtht":
+ // HOMOTHETIC
+ return rune(0x223b), true
+ case "hookleftarrow":
+ // LEFTWARDS ARROW WITH HOOK
+ return rune(0x21a9), true
+ case "hookrightarrow":
+ // RIGHTWARDS ARROW WITH HOOK
+ return rune(0x21aa), true
+ case "hopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL H
+ return rune(0x01d559), true
+ case "horbar":
+ // HORIZONTAL BAR
+ return rune(0x2015), true
+ case "hrglass":
+ // WHITE HOURGLASS
+ return rune(0x29d6), true
+ case "hscr":
+ // MATHEMATICAL SCRIPT SMALL H
+ return rune(0x01d4bd), true
+ case "hslash":
+ // PLANCK CONSTANT OVER TWO PI
+ return rune(0x210f), true
+ case "hstrok":
+ // LATIN SMALL LETTER H WITH STROKE
+ return rune(0x0127), true
+ case "htimes":
+ // VECTOR OR CROSS PRODUCT
+ return rune(0x2a2f), true
+ case "hybull":
+ // HYPHEN BULLET
+ return rune(0x2043), true
+ case "hyphen":
+ // HYPHEN
+ return rune(0x2010), true
+ }
+
+ case 'i':
+ switch name {
+ case "iacgr":
+ // GREEK SMALL LETTER IOTA WITH TONOS
+ return rune(0x03af), true
+ case "iacute":
+ // LATIN SMALL LETTER I WITH ACUTE
+ return rune(0xed), true
+ case "ic":
+ // INVISIBLE SEPARATOR
+ return rune(0x2063), true
+ case "icirc":
+ // LATIN SMALL LETTER I WITH CIRCUMFLEX
+ return rune(0xee), true
+ case "icy":
+ // CYRILLIC SMALL LETTER I
+ return rune(0x0438), true
+ case "idiagr":
+ // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+ return rune(0x0390), true
+ case "idigr":
+ // GREEK SMALL LETTER IOTA WITH DIALYTIKA
+ return rune(0x03ca), true
+ case "iecy":
+ // CYRILLIC SMALL LETTER IE
+ return rune(0x0435), true
+ case "iexcl":
+ // INVERTED EXCLAMATION MARK
+ return rune(0xa1), true
+ case "iff":
+ // LEFT RIGHT DOUBLE ARROW
+ return rune(0x21d4), true
+ case "ifr":
+ // MATHEMATICAL FRAKTUR SMALL I
+ return rune(0x01d526), true
+ case "igr":
+ // GREEK SMALL LETTER IOTA
+ return rune(0x03b9), true
+ case "igrave":
+ // LATIN SMALL LETTER I WITH GRAVE
+ return rune(0xec), true
+ case "iiint":
+ // TRIPLE INTEGRAL
+ return rune(0x222d), true
+ case "ii":
+ // DOUBLE-STRUCK ITALIC SMALL I
+ return rune(0x2148), true
+ case "iiiint":
+ // QUADRUPLE INTEGRAL OPERATOR
+ return rune(0x2a0c), true
+ case "iinfin":
+ // INCOMPLETE INFINITY
+ return rune(0x29dc), true
+ case "iiota":
+ // TURNED GREEK SMALL LETTER IOTA
+ return rune(0x2129), true
+ case "ijlig":
+ // LATIN SMALL LIGATURE IJ
+ return rune(0x0133), true
+ case "imacr":
+ // LATIN SMALL LETTER I WITH MACRON
+ return rune(0x012b), true
+ case "image":
+ // BLACK-LETTER CAPITAL I
+ return rune(0x2111), true
+ case "imagline":
+ // SCRIPT CAPITAL I
+ return rune(0x2110), true
+ case "imagpart":
+ // BLACK-LETTER CAPITAL I
+ return rune(0x2111), true
+ case "imath":
+ // LATIN SMALL LETTER DOTLESS I
+ return rune(0x0131), true
+ case "imof":
+ // IMAGE OF
+ return rune(0x22b7), true
+ case "imped":
+ // LATIN CAPITAL LETTER Z WITH STROKE
+ return rune(0x01b5), true
+ case "in":
+ // ELEMENT OF
+ return rune(0x2208), true
+ case "incare":
+ // CARE OF
+ return rune(0x2105), true
+ case "infin":
+ // INFINITY
+ return rune(0x221e), true
+ case "infintie":
+ // TIE OVER INFINITY
+ return rune(0x29dd), true
+ case "inodot":
+ // LATIN SMALL LETTER DOTLESS I
+ return rune(0x0131), true
+ case "int":
+ // INTEGRAL
+ return rune(0x222b), true
+ case "intcal":
+ // INTERCALATE
+ return rune(0x22ba), true
+ case "integers":
+ // DOUBLE-STRUCK CAPITAL Z
+ return rune(0x2124), true
+ case "intercal":
+ // INTERCALATE
+ return rune(0x22ba), true
+ case "intlarhk":
+ // INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
+ return rune(0x2a17), true
+ case "intprod":
+ // INTERIOR PRODUCT
+ return rune(0x2a3c), true
+ case "iocy":
+ // CYRILLIC SMALL LETTER IO
+ return rune(0x0451), true
+ case "iogon":
+ // LATIN SMALL LETTER I WITH OGONEK
+ return rune(0x012f), true
+ case "iopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL I
+ return rune(0x01d55a), true
+ case "iota":
+ // GREEK SMALL LETTER IOTA
+ return rune(0x03b9), true
+ case "iprod":
+ // INTERIOR PRODUCT
+ return rune(0x2a3c), true
+ case "iprodr":
+ // RIGHTHAND INTERIOR PRODUCT
+ return rune(0x2a3d), true
+ case "iquest":
+ // INVERTED QUESTION MARK
+ return rune(0xbf), true
+ case "iscr":
+ // MATHEMATICAL SCRIPT SMALL I
+ return rune(0x01d4be), true
+ case "isin":
+ // ELEMENT OF
+ return rune(0x2208), true
+ case "isinE":
+ // ELEMENT OF WITH TWO HORIZONTAL STROKES
+ return rune(0x22f9), true
+ case "isindot":
+ // ELEMENT OF WITH DOT ABOVE
+ return rune(0x22f5), true
+ case "isinsv":
+ // ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ return rune(0x22f3), true
+ case "isins":
+ // SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ return rune(0x22f4), true
+ case "isinv":
+ // ELEMENT OF
+ return rune(0x2208), true
+ case "isinvb":
+ // ELEMENT OF WITH UNDERBAR
+ return rune(0x22f8), true
+ case "it":
+ // INVISIBLE TIMES
+ return rune(0x2062), true
+ case "itilde":
+ // LATIN SMALL LETTER I WITH TILDE
+ return rune(0x0129), true
+ case "iukcy":
+ // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+ return rune(0x0456), true
+ case "iuml":
+ // LATIN SMALL LETTER I WITH DIAERESIS
+ return rune(0xef), true
+ }
+
+ case 'j':
+ switch name {
+ case "jcirc":
+ // LATIN SMALL LETTER J WITH CIRCUMFLEX
+ return rune(0x0135), true
+ case "jcy":
+ // CYRILLIC SMALL LETTER SHORT I
+ return rune(0x0439), true
+ case "jfr":
+ // MATHEMATICAL FRAKTUR SMALL J
+ return rune(0x01d527), true
+ case "jmath":
+ // LATIN SMALL LETTER DOTLESS J
+ return rune(0x0237), true
+ case "jnodot":
+ // LATIN SMALL LETTER DOTLESS J
+ return rune(0x0237), true
+ case "jopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL J
+ return rune(0x01d55b), true
+ case "jscr":
+ // MATHEMATICAL SCRIPT SMALL J
+ return rune(0x01d4bf), true
+ case "jsercy":
+ // CYRILLIC SMALL LETTER JE
+ return rune(0x0458), true
+ case "jukcy":
+ // CYRILLIC SMALL LETTER UKRAINIAN IE
+ return rune(0x0454), true
+ }
+
+ case 'k':
+ switch name {
+ case "kappav":
+ // GREEK KAPPA SYMBOL
+ return rune(0x03f0), true
+ case "kappa":
+ // GREEK SMALL LETTER KAPPA
+ return rune(0x03ba), true
+ case "kcedil":
+ // LATIN SMALL LETTER K WITH CEDILLA
+ return rune(0x0137), true
+ case "kcy":
+ // CYRILLIC SMALL LETTER KA
+ return rune(0x043a), true
+ case "kfr":
+ // MATHEMATICAL FRAKTUR SMALL K
+ return rune(0x01d528), true
+ case "kgr":
+ // GREEK SMALL LETTER KAPPA
+ return rune(0x03ba), true
+ case "kgreen":
+ // LATIN SMALL LETTER KRA
+ return rune(0x0138), true
+ case "khcy":
+ // CYRILLIC SMALL LETTER HA
+ return rune(0x0445), true
+ case "khgr":
+ // GREEK SMALL LETTER CHI
+ return rune(0x03c7), true
+ case "kjcy":
+ // CYRILLIC SMALL LETTER KJE
+ return rune(0x045c), true
+ case "kopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL K
+ return rune(0x01d55c), true
+ case "koppa":
+ // GREEK LETTER KOPPA
+ return rune(0x03de), true
+ case "kscr":
+ // MATHEMATICAL SCRIPT SMALL K
+ return rune(0x01d4c0), true
+ }
+
+ case 'l':
+ switch name {
+ case "lAarr":
+ // LEFTWARDS TRIPLE ARROW
+ return rune(0x21da), true
+ case "lArr":
+ // LEFTWARDS DOUBLE ARROW
+ return rune(0x21d0), true
+ case "lAtail":
+ // LEFTWARDS DOUBLE ARROW-TAIL
+ return rune(0x291b), true
+ case "lBarr":
+ // LEFTWARDS TRIPLE DASH ARROW
+ return rune(0x290e), true
+ case "lE":
+ // LESS-THAN OVER EQUAL TO
+ return rune(0x2266), true
+ case "lEg":
+ // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+ return rune(0x2a8b), true
+ case "lHar":
+ // LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN
+ return rune(0x2962), true
+ case "lacute":
+ // LATIN SMALL LETTER L WITH ACUTE
+ return rune(0x013a), true
+ case "laemptyv":
+ // EMPTY SET WITH LEFT ARROW ABOVE
+ return rune(0x29b4), true
+ case "lagran":
+ // SCRIPT CAPITAL L
+ return rune(0x2112), true
+ case "lambda":
+ // GREEK SMALL LETTER LAMDA
+ return rune(0x03bb), true
+ case "lang":
+ // MATHEMATICAL LEFT ANGLE BRACKET
+ return rune(0x27e8), true
+ case "langd":
+ // LEFT ANGLE BRACKET WITH DOT
+ return rune(0x2991), true
+ case "langle":
+ // MATHEMATICAL LEFT ANGLE BRACKET
+ return rune(0x27e8), true
+ case "lap":
+ // LESS-THAN OR APPROXIMATE
+ return rune(0x2a85), true
+ case "laquo":
+ // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+ return rune(0xab), true
+ case "larr2":
+ // LEFTWARDS PAIRED ARROWS
+ return rune(0x21c7), true
+ case "larrb":
+ // LEFTWARDS ARROW TO BAR
+ return rune(0x21e4), true
+ case "larrhk":
+ // LEFTWARDS ARROW WITH HOOK
+ return rune(0x21a9), true
+ case "larrlp":
+ // LEFTWARDS ARROW WITH LOOP
+ return rune(0x21ab), true
+ case "larrtl":
+ // LEFTWARDS ARROW WITH TAIL
+ return rune(0x21a2), true
+ case "larr":
+ // LEFTWARDS ARROW
+ return rune(0x2190), true
+ case "larrbfs":
+ // LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND
+ return rune(0x291f), true
+ case "larrfs":
+ // LEFTWARDS ARROW TO BLACK DIAMOND
+ return rune(0x291d), true
+ case "larrpl":
+ // LEFT-SIDE ARC ANTICLOCKWISE ARROW
+ return rune(0x2939), true
+ case "larrsim":
+ // LEFTWARDS ARROW ABOVE TILDE OPERATOR
+ return rune(0x2973), true
+ case "latail":
+ // LEFTWARDS ARROW-TAIL
+ return rune(0x2919), true
+ case "lat":
+ // LARGER THAN
+ return rune(0x2aab), true
+ case "late":
+ // LARGER THAN OR EQUAL TO
+ return rune(0x2aad), true
+ case "lates":
+ // LARGER THAN OR slanted EQUAL
+ return rune(0x2aad), true
+ case "lbarr":
+ // LEFTWARDS DOUBLE DASH ARROW
+ return rune(0x290c), true
+ case "lbbrk":
+ // LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT
+ return rune(0x2772), true
+ case "lbrace":
+ // LEFT CURLY BRACKET
+ return rune(0x7b), true
+ case "lbrack":
+ // LEFT SQUARE BRACKET
+ return rune(0x5b), true
+ case "lbrke":
+ // LEFT SQUARE BRACKET WITH UNDERBAR
+ return rune(0x298b), true
+ case "lbrksld":
+ // LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ return rune(0x298f), true
+ case "lbrkslu":
+ // LEFT SQUARE BRACKET WITH TICK IN TOP CORNER
+ return rune(0x298d), true
+ case "lcaron":
+ // LATIN SMALL LETTER L WITH CARON
+ return rune(0x013e), true
+ case "lcedil":
+ // LATIN SMALL LETTER L WITH CEDILLA
+ return rune(0x013c), true
+ case "lceil":
+ // LEFT CEILING
+ return rune(0x2308), true
+ case "lcub":
+ // LEFT CURLY BRACKET
+ return rune(0x7b), true
+ case "lcy":
+ // CYRILLIC SMALL LETTER EL
+ return rune(0x043b), true
+ case "ldca":
+ // ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
+ return rune(0x2936), true
+ case "ldharb":
+ // LEFTWARDS HARPOON WITH BARB DOWN TO BAR
+ return rune(0x2956), true
+ case "ldot":
+ // LESS-THAN WITH DOT
+ return rune(0x22d6), true
+ case "ldquor":
+ // DOUBLE LOW-9 QUOTATION MARK
+ return rune(0x201e), true
+ case "ldquo":
+ // LEFT DOUBLE QUOTATION MARK
+ return rune(0x201c), true
+ case "ldrdhar":
+ // LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
+ return rune(0x2967), true
+ case "ldrdshar":
+ // LEFT BARB DOWN RIGHT BARB DOWN HARPOON
+ return rune(0x2950), true
+ case "ldrushar":
+ // LEFT BARB DOWN RIGHT BARB UP HARPOON
+ return rune(0x294b), true
+ case "ldsh":
+ // DOWNWARDS ARROW WITH TIP LEFTWARDS
+ return rune(0x21b2), true
+ case "leftarrowtail":
+ // LEFTWARDS ARROW WITH TAIL
+ return rune(0x21a2), true
+ case "leftarrow":
+ // LEFTWARDS ARROW
+ return rune(0x2190), true
+ case "leftharpoondown":
+ // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21bd), true
+ case "leftharpoonup":
+ // LEFTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21bc), true
+ case "leftleftarrows":
+ // LEFTWARDS PAIRED ARROWS
+ return rune(0x21c7), true
+ case "leftrightarrows":
+ // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ return rune(0x21c6), true
+ case "leftrightarrow":
+ // LEFT RIGHT ARROW
+ return rune(0x2194), true
+ case "leftrightharpoons":
+ // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ return rune(0x21cb), true
+ case "leftrightsquigarrow":
+ // LEFT RIGHT WAVE ARROW
+ return rune(0x21ad), true
+ case "le":
+ // LESS-THAN OR EQUAL TO
+ return rune(0x2264), true
+ case "leftthreetimes":
+ // LEFT SEMIDIRECT PRODUCT
+ return rune(0x22cb), true
+ case "leg":
+ // LESS-THAN EQUAL TO OR GREATER-THAN
+ return rune(0x22da), true
+ case "leq":
+ // LESS-THAN OR EQUAL TO
+ return rune(0x2264), true
+ case "leqq":
+ // LESS-THAN OVER EQUAL TO
+ return rune(0x2266), true
+ case "leqslant":
+ // LESS-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7d), true
+ case "lesg":
+ // LESS-THAN slanted EQUAL TO OR GREATER-THAN
+ return rune(0x22da), true
+ case "lessdot":
+ // LESS-THAN WITH DOT
+ return rune(0x22d6), true
+ case "lesseqgtr":
+ // LESS-THAN EQUAL TO OR GREATER-THAN
+ return rune(0x22da), true
+ case "lessgtr":
+ // LESS-THAN OR GREATER-THAN
+ return rune(0x2276), true
+ case "lesssim":
+ // LESS-THAN OR EQUIVALENT TO
+ return rune(0x2272), true
+ case "les":
+ // LESS-THAN OR SLANTED EQUAL TO
+ return rune(0x2a7d), true
+ case "lescc":
+ // LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL
+ return rune(0x2aa8), true
+ case "lesdot":
+ // LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE
+ return rune(0x2a7f), true
+ case "lesdoto":
+ // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE
+ return rune(0x2a81), true
+ case "lesdotor":
+ // LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT
+ return rune(0x2a83), true
+ case "lesges":
+ // LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL
+ return rune(0x2a93), true
+ case "lessapprox":
+ // LESS-THAN OR APPROXIMATE
+ return rune(0x2a85), true
+ case "lesseqqgtr":
+ // LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN
+ return rune(0x2a8b), true
+ case "lfbowtie":
+ // BOWTIE WITH LEFT HALF BLACK
+ return rune(0x29d1), true
+ case "lfisht":
+ // LEFT FISH TAIL
+ return rune(0x297c), true
+ case "lfloor":
+ // LEFT FLOOR
+ return rune(0x230a), true
+ case "lfr":
+ // MATHEMATICAL FRAKTUR SMALL L
+ return rune(0x01d529), true
+ case "lftimes":
+ // TIMES WITH LEFT HALF BLACK
+ return rune(0x29d4), true
+ case "lg":
+ // LESS-THAN OR GREATER-THAN
+ return rune(0x2276), true
+ case "lgE":
+ // LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL
+ return rune(0x2a91), true
+ case "lgr":
+ // GREEK SMALL LETTER LAMDA
+ return rune(0x03bb), true
+ case "lhard":
+ // LEFTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21bd), true
+ case "lharu":
+ // LEFTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21bc), true
+ case "lharul":
+ // LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
+ return rune(0x296a), true
+ case "lhblk":
+ // LOWER HALF BLOCK
+ return rune(0x2584), true
+ case "ljcy":
+ // CYRILLIC SMALL LETTER LJE
+ return rune(0x0459), true
+ case "llarr":
+ // LEFTWARDS PAIRED ARROWS
+ return rune(0x21c7), true
+ case "ll":
+ // MUCH LESS-THAN
+ return rune(0x226a), true
+ case "llcorner":
+ // BOTTOM LEFT CORNER
+ return rune(0x231e), true
+ case "llhard":
+ // LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
+ return rune(0x296b), true
+ case "lltrif":
+ // BLACK LOWER LEFT TRIANGLE
+ return rune(0x25e3), true
+ case "lltri":
+ // LOWER LEFT TRIANGLE
+ return rune(0x25fa), true
+ case "lmidot":
+ // LATIN SMALL LETTER L WITH MIDDLE DOT
+ return rune(0x0140), true
+ case "lmoust":
+ // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
+ return rune(0x23b0), true
+ case "lmoustache":
+ // UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
+ return rune(0x23b0), true
+ case "lnE":
+ // LESS-THAN BUT NOT EQUAL TO
+ return rune(0x2268), true
+ case "lnap":
+ // LESS-THAN AND NOT APPROXIMATE
+ return rune(0x2a89), true
+ case "lnapprox":
+ // LESS-THAN AND NOT APPROXIMATE
+ return rune(0x2a89), true
+ case "lneqq":
+ // LESS-THAN BUT NOT EQUAL TO
+ return rune(0x2268), true
+ case "lne":
+ // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
+ return rune(0x2a87), true
+ case "lneq":
+ // LESS-THAN AND SINGLE-LINE NOT EQUAL TO
+ return rune(0x2a87), true
+ case "lnsim":
+ // LESS-THAN BUT NOT EQUIVALENT TO
+ return rune(0x22e6), true
+ case "loang":
+ // MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET
+ return rune(0x27ec), true
+ case "loarr":
+ // LEFTWARDS OPEN-HEADED ARROW
+ return rune(0x21fd), true
+ case "lobrk":
+ // MATHEMATICAL LEFT WHITE SQUARE BRACKET
+ return rune(0x27e6), true
+ case "locub":
+ // LEFT WHITE CURLY BRACKET
+ return rune(0x2983), true
+ case "longleftarrow":
+ // LONG LEFTWARDS ARROW
+ return rune(0x27f5), true
+ case "longleftrightarrow":
+ // LONG LEFT RIGHT ARROW
+ return rune(0x27f7), true
+ case "longmapsto":
+ // LONG RIGHTWARDS ARROW FROM BAR
+ return rune(0x27fc), true
+ case "longrightarrow":
+ // LONG RIGHTWARDS ARROW
+ return rune(0x27f6), true
+ case "looparrowleft":
+ // LEFTWARDS ARROW WITH LOOP
+ return rune(0x21ab), true
+ case "looparrowright":
+ // RIGHTWARDS ARROW WITH LOOP
+ return rune(0x21ac), true
+ case "lopar":
+ // LEFT WHITE PARENTHESIS
+ return rune(0x2985), true
+ case "lopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL L
+ return rune(0x01d55d), true
+ case "loplus":
+ // PLUS SIGN IN LEFT HALF CIRCLE
+ return rune(0x2a2d), true
+ case "lotimes":
+ // MULTIPLICATION SIGN IN LEFT HALF CIRCLE
+ return rune(0x2a34), true
+ case "lowast":
+ // LOW ASTERISK
+ return rune(0x204e), true
+ case "lowbar":
+ // LOW LINE
+ return rune(0x5f), true
+ case "lowint":
+ // INTEGRAL WITH UNDERBAR
+ return rune(0x2a1c), true
+ case "loz":
+ // LOZENGE
+ return rune(0x25ca), true
+ case "lozenge":
+ // LOZENGE
+ return rune(0x25ca), true
+ case "lozf":
+ // BLACK LOZENGE
+ return rune(0x29eb), true
+ case "lpargt":
+ // SPHERICAL ANGLE OPENING LEFT
+ return rune(0x29a0), true
+ case "lparlt":
+ // LEFT ARC LESS-THAN BRACKET
+ return rune(0x2993), true
+ case "lpar":
+ // LEFT PARENTHESIS
+ return rune(0x28), true
+ case "lrarr2":
+ // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ return rune(0x21c6), true
+ case "lrarr":
+ // LEFTWARDS ARROW OVER RIGHTWARDS ARROW
+ return rune(0x21c6), true
+ case "lrcorner":
+ // BOTTOM RIGHT CORNER
+ return rune(0x231f), true
+ case "lrhar":
+ // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ return rune(0x21cb), true
+ case "lrhar2":
+ // LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON
+ return rune(0x21cb), true
+ case "lrhard":
+ // RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH
+ return rune(0x296d), true
+ case "lrm":
+ // LEFT-TO-RIGHT MARK
+ return rune(0x200e), true
+ case "lrtri":
+ // RIGHT TRIANGLE
+ return rune(0x22bf), true
+ case "lsaquo":
+ // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+ return rune(0x2039), true
+ case "lscr":
+ // MATHEMATICAL SCRIPT SMALL L
+ return rune(0x01d4c1), true
+ case "lsh":
+ // UPWARDS ARROW WITH TIP LEFTWARDS
+ return rune(0x21b0), true
+ case "lsim":
+ // LESS-THAN OR EQUIVALENT TO
+ return rune(0x2272), true
+ case "lsime":
+ // LESS-THAN ABOVE SIMILAR OR EQUAL
+ return rune(0x2a8d), true
+ case "lsimg":
+ // LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN
+ return rune(0x2a8f), true
+ case "lsqb":
+ // LEFT SQUARE BRACKET
+ return rune(0x5b), true
+ case "lsquor":
+ // SINGLE LOW-9 QUOTATION MARK
+ return rune(0x201a), true
+ case "lsquo":
+ // LEFT SINGLE QUOTATION MARK
+ return rune(0x2018), true
+ case "lstrok":
+ // LATIN SMALL LETTER L WITH STROKE
+ return rune(0x0142), true
+ case "ltcc":
+ // LESS-THAN CLOSED BY CURVE
+ return rune(0x2aa6), true
+ case "ltcir":
+ // LESS-THAN WITH CIRCLE INSIDE
+ return rune(0x2a79), true
+ case "ltdot":
+ // LESS-THAN WITH DOT
+ return rune(0x22d6), true
+ case "lthree":
+ // LEFT SEMIDIRECT PRODUCT
+ return rune(0x22cb), true
+ case "ltimes":
+ // LEFT NORMAL FACTOR SEMIDIRECT PRODUCT
+ return rune(0x22c9), true
+ case "ltlarr":
+ // LESS-THAN ABOVE LEFTWARDS ARROW
+ return rune(0x2976), true
+ case "ltquest":
+ // LESS-THAN WITH QUESTION MARK ABOVE
+ return rune(0x2a7b), true
+ case "ltrPar":
+ // DOUBLE RIGHT ARC LESS-THAN BRACKET
+ return rune(0x2996), true
+ case "ltrie":
+ // NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22b4), true
+ case "ltrif":
+ // BLACK LEFT-POINTING SMALL TRIANGLE
+ return rune(0x25c2), true
+ case "ltri":
+ // WHITE LEFT-POINTING SMALL TRIANGLE
+ return rune(0x25c3), true
+ case "ltrivb":
+ // LEFT TRIANGLE BESIDE VERTICAL BAR
+ return rune(0x29cf), true
+ case "lt":
+ // LESS-THAN SIGN
+ return rune(0x3c), true
+ case "luharb":
+ // LEFTWARDS HARPOON WITH BARB UP TO BAR
+ return rune(0x2952), true
+ case "lurdshar":
+ // LEFT BARB UP RIGHT BARB DOWN HARPOON
+ return rune(0x294a), true
+ case "luruhar":
+ // LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP
+ return rune(0x2966), true
+ case "lurushar":
+ // LEFT BARB UP RIGHT BARB UP HARPOON
+ return rune(0x294e), true
+ case "lvertneqq":
+ // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
+ return rune(0x2268), true
+ case "lvnE":
+ // LESS-THAN BUT NOT EQUAL TO - with vertical stroke
+ return rune(0x2268), true
+ }
+
+ case 'm':
+ switch name {
+ case "mDDot":
+ // GEOMETRIC PROPORTION
+ return rune(0x223a), true
+ case "macr":
+ // MACRON
+ return rune(0xaf), true
+ case "male":
+ // MALE SIGN
+ return rune(0x2642), true
+ case "malt":
+ // MALTESE CROSS
+ return rune(0x2720), true
+ case "maltese":
+ // MALTESE CROSS
+ return rune(0x2720), true
+ case "mapstodown":
+ // DOWNWARDS ARROW FROM BAR
+ return rune(0x21a7), true
+ case "mapsto":
+ // RIGHTWARDS ARROW FROM BAR
+ return rune(0x21a6), true
+ case "map":
+ // RIGHTWARDS ARROW FROM BAR
+ return rune(0x21a6), true
+ case "mapstoleft":
+ // LEFTWARDS ARROW FROM BAR
+ return rune(0x21a4), true
+ case "mapstoup":
+ // UPWARDS ARROW FROM BAR
+ return rune(0x21a5), true
+ case "marker":
+ // BLACK VERTICAL RECTANGLE
+ return rune(0x25ae), true
+ case "mcomma":
+ // MINUS SIGN WITH COMMA ABOVE
+ return rune(0x2a29), true
+ case "mcy":
+ // CYRILLIC SMALL LETTER EM
+ return rune(0x043c), true
+ case "mdash":
+ // EM DASH
+ return rune(0x2014), true
+ case "measuredangle":
+ // MEASURED ANGLE
+ return rune(0x2221), true
+ case "mfr":
+ // MATHEMATICAL FRAKTUR SMALL M
+ return rune(0x01d52a), true
+ case "mgr":
+ // GREEK SMALL LETTER MU
+ return rune(0x03bc), true
+ case "mho":
+ // INVERTED OHM SIGN
+ return rune(0x2127), true
+ case "micro":
+ // MICRO SIGN
+ return rune(0xb5), true
+ case "mid":
+ // DIVIDES
+ return rune(0x2223), true
+ case "midast":
+ // ASTERISK
+ return rune(0x2a), true
+ case "midcir":
+ // VERTICAL LINE WITH CIRCLE BELOW
+ return rune(0x2af0), true
+ case "middot":
+ // MIDDLE DOT
+ return rune(0xb7), true
+ case "minus":
+ // MINUS SIGN
+ return rune(0x2212), true
+ case "minusb":
+ // SQUARED MINUS
+ return rune(0x229f), true
+ case "minusd":
+ // DOT MINUS
+ return rune(0x2238), true
+ case "minusdu":
+ // MINUS SIGN WITH DOT BELOW
+ return rune(0x2a2a), true
+ case "mlcp":
+ // TRANSVERSAL INTERSECTION
+ return rune(0x2adb), true
+ case "mldr":
+ // HORIZONTAL ELLIPSIS
+ return rune(0x2026), true
+ case "mnplus":
+ // MINUS-OR-PLUS SIGN
+ return rune(0x2213), true
+ case "models":
+ // MODELS
+ return rune(0x22a7), true
+ case "mopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL M
+ return rune(0x01d55e), true
+ case "mp":
+ // MINUS-OR-PLUS SIGN
+ return rune(0x2213), true
+ case "mscr":
+ // MATHEMATICAL SCRIPT SMALL M
+ return rune(0x01d4c2), true
+ case "mstpos":
+ // INVERTED LAZY S
+ return rune(0x223e), true
+ case "multimap":
+ // MULTIMAP
+ return rune(0x22b8), true
+ case "mumap":
+ // MULTIMAP
+ return rune(0x22b8), true
+ case "mu":
+ // GREEK SMALL LETTER MU
+ return rune(0x03bc), true
+ }
+
+ case 'n':
+ switch name {
+ case "nGg":
+ // VERY MUCH GREATER-THAN with slash
+ return rune(0x22d9), true
+ case "nGtv":
+ // MUCH GREATER THAN with slash
+ return rune(0x226b), true
+ case "nGt":
+ // MUCH GREATER THAN with vertical line
+ return rune(0x226b), true
+ case "nLeftarrow":
+ // LEFTWARDS DOUBLE ARROW WITH STROKE
+ return rune(0x21cd), true
+ case "nLeftrightarrow":
+ // LEFT RIGHT DOUBLE ARROW WITH STROKE
+ return rune(0x21ce), true
+ case "nLl":
+ // VERY MUCH LESS-THAN with slash
+ return rune(0x22d8), true
+ case "nLtv":
+ // MUCH LESS THAN with slash
+ return rune(0x226a), true
+ case "nLt":
+ // MUCH LESS THAN with vertical line
+ return rune(0x226a), true
+ case "nRightarrow":
+ // RIGHTWARDS DOUBLE ARROW WITH STROKE
+ return rune(0x21cf), true
+ case "nVDash":
+ // NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+ return rune(0x22af), true
+ case "nVdash":
+ // DOES NOT FORCE
+ return rune(0x22ae), true
+ case "nabla":
+ // NABLA
+ return rune(0x2207), true
+ case "nacute":
+ // LATIN SMALL LETTER N WITH ACUTE
+ return rune(0x0144), true
+ case "nang":
+ // ANGLE with vertical line
+ return rune(0x2220), true
+ case "nap":
+ // NOT ALMOST EQUAL TO
+ return rune(0x2249), true
+ case "napE":
+ // APPROXIMATELY EQUAL OR EQUAL TO with slash
+ return rune(0x2a70), true
+ case "napid":
+ // TRIPLE TILDE with slash
+ return rune(0x224b), true
+ case "napos":
+ // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ return rune(0x0149), true
+ case "napprox":
+ // NOT ALMOST EQUAL TO
+ return rune(0x2249), true
+ case "naturals":
+ // DOUBLE-STRUCK CAPITAL N
+ return rune(0x2115), true
+ case "natur":
+ // MUSIC NATURAL SIGN
+ return rune(0x266e), true
+ case "natural":
+ // MUSIC NATURAL SIGN
+ return rune(0x266e), true
+ case "nbsp":
+ // NO-BREAK SPACE
+ return rune(0xa0), true
+ case "nbump":
+ // GEOMETRICALLY EQUIVALENT TO with slash
+ return rune(0x224e), true
+ case "nbumpe":
+ // DIFFERENCE BETWEEN with slash
+ return rune(0x224f), true
+ case "ncap":
+ // INTERSECTION WITH OVERBAR
+ return rune(0x2a43), true
+ case "ncaron":
+ // LATIN SMALL LETTER N WITH CARON
+ return rune(0x0148), true
+ case "ncedil":
+ // LATIN SMALL LETTER N WITH CEDILLA
+ return rune(0x0146), true
+ case "ncong":
+ // NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+ return rune(0x2247), true
+ case "ncongdot":
+ // CONGRUENT WITH DOT ABOVE with slash
+ return rune(0x2a6d), true
+ case "ncup":
+ // UNION WITH OVERBAR
+ return rune(0x2a42), true
+ case "ncy":
+ // CYRILLIC SMALL LETTER EN
+ return rune(0x043d), true
+ case "ndash":
+ // EN DASH
+ return rune(0x2013), true
+ case "neArr":
+ // NORTH EAST DOUBLE ARROW
+ return rune(0x21d7), true
+ case "nearrow":
+ // NORTH EAST ARROW
+ return rune(0x2197), true
+ case "nearr":
+ // NORTH EAST ARROW
+ return rune(0x2197), true
+ case "nedot":
+ // APPROACHES THE LIMIT with slash
+ return rune(0x2250), true
+ case "nesim":
+ // MINUS TILDE with slash
+ return rune(0x2242), true
+ case "nexist":
+ // THERE DOES NOT EXIST
+ return rune(0x2204), true
+ case "nexists":
+ // THERE DOES NOT EXIST
+ return rune(0x2204), true
+ case "ne":
+ // NOT EQUAL TO
+ return rune(0x2260), true
+ case "nearhk":
+ // NORTH EAST ARROW WITH HOOK
+ return rune(0x2924), true
+ case "neonwarr":
+ // NORTH EAST ARROW CROSSING NORTH WEST ARROW
+ return rune(0x2931), true
+ case "neosearr":
+ // NORTH EAST ARROW CROSSING SOUTH EAST ARROW
+ return rune(0x292e), true
+ case "nequiv":
+ // NOT IDENTICAL TO
+ return rune(0x2262), true
+ case "nesear":
+ // NORTH EAST ARROW AND SOUTH EAST ARROW
+ return rune(0x2928), true
+ case "neswsarr":
+ // NORTH EAST AND SOUTH WEST ARROW
+ return rune(0x2922), true
+ case "nfr":
+ // MATHEMATICAL FRAKTUR SMALL N
+ return rune(0x01d52b), true
+ case "ngE":
+ // GREATER-THAN OVER EQUAL TO with slash
+ return rune(0x2267), true
+ case "ngeqq":
+ // GREATER-THAN OVER EQUAL TO with slash
+ return rune(0x2267), true
+ case "nge":
+ // NEITHER GREATER-THAN NOR EQUAL TO
+ return rune(0x2271), true
+ case "ngeq":
+ // NEITHER GREATER-THAN NOR EQUAL TO
+ return rune(0x2271), true
+ case "ngeqslant":
+ // GREATER-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7e), true
+ case "nges":
+ // GREATER-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7e), true
+ case "ngr":
+ // GREEK SMALL LETTER NU
+ return rune(0x03bd), true
+ case "ngsim":
+ // NEITHER GREATER-THAN NOR EQUIVALENT TO
+ return rune(0x2275), true
+ case "ngt":
+ // NOT GREATER-THAN
+ return rune(0x226f), true
+ case "ngtr":
+ // NOT GREATER-THAN
+ return rune(0x226f), true
+ case "nhArr":
+ // LEFT RIGHT DOUBLE ARROW WITH STROKE
+ return rune(0x21ce), true
+ case "nharr":
+ // LEFT RIGHT ARROW WITH STROKE
+ return rune(0x21ae), true
+ case "nhpar":
+ // PARALLEL WITH HORIZONTAL STROKE
+ return rune(0x2af2), true
+ case "niv":
+ // CONTAINS AS MEMBER
+ return rune(0x220b), true
+ case "ni":
+ // CONTAINS AS MEMBER
+ return rune(0x220b), true
+ case "nisd":
+ // CONTAINS WITH LONG HORIZONTAL STROKE
+ return rune(0x22fa), true
+ case "nis":
+ // SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ return rune(0x22fc), true
+ case "njcy":
+ // CYRILLIC SMALL LETTER NJE
+ return rune(0x045a), true
+ case "nlArr":
+ // LEFTWARDS DOUBLE ARROW WITH STROKE
+ return rune(0x21cd), true
+ case "nlE":
+ // LESS-THAN OVER EQUAL TO with slash
+ return rune(0x2266), true
+ case "nlarr":
+ // LEFTWARDS ARROW WITH STROKE
+ return rune(0x219a), true
+ case "nldr":
+ // TWO DOT LEADER
+ return rune(0x2025), true
+ case "nleftarrow":
+ // LEFTWARDS ARROW WITH STROKE
+ return rune(0x219a), true
+ case "nleftrightarrow":
+ // LEFT RIGHT ARROW WITH STROKE
+ return rune(0x21ae), true
+ case "nleqq":
+ // LESS-THAN OVER EQUAL TO with slash
+ return rune(0x2266), true
+ case "nless":
+ // NOT LESS-THAN
+ return rune(0x226e), true
+ case "nle":
+ // NEITHER LESS-THAN NOR EQUAL TO
+ return rune(0x2270), true
+ case "nleq":
+ // NEITHER LESS-THAN NOR EQUAL TO
+ return rune(0x2270), true
+ case "nleqslant":
+ // LESS-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7d), true
+ case "nles":
+ // LESS-THAN OR SLANTED EQUAL TO with slash
+ return rune(0x2a7d), true
+ case "nlsim":
+ // NEITHER LESS-THAN NOR EQUIVALENT TO
+ return rune(0x2274), true
+ case "nlt":
+ // NOT LESS-THAN
+ return rune(0x226e), true
+ case "nltri":
+ // NOT NORMAL SUBGROUP OF
+ return rune(0x22ea), true
+ case "nltrie":
+ // NOT NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22ec), true
+ case "nltrivb":
+ // LEFT TRIANGLE BESIDE VERTICAL BAR with slash
+ return rune(0x29cf), true
+ case "nmid":
+ // DOES NOT DIVIDE
+ return rune(0x2224), true
+ case "nopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL N
+ return rune(0x01d55f), true
+ case "notin":
+ // NOT AN ELEMENT OF
+ return rune(0x2209), true
+ case "notinE":
+ // ELEMENT OF WITH TWO HORIZONTAL STROKES with slash
+ return rune(0x22f9), true
+ case "notindot":
+ // ELEMENT OF WITH DOT ABOVE with slash
+ return rune(0x22f5), true
+ case "notinva":
+ // NOT AN ELEMENT OF
+ return rune(0x2209), true
+ case "notinvb":
+ // SMALL ELEMENT OF WITH OVERBAR
+ return rune(0x22f7), true
+ case "notinvc":
+ // ELEMENT OF WITH OVERBAR
+ return rune(0x22f6), true
+ case "notni":
+ // DOES NOT CONTAIN AS MEMBER
+ return rune(0x220c), true
+ case "notniva":
+ // DOES NOT CONTAIN AS MEMBER
+ return rune(0x220c), true
+ case "notnivb":
+ // SMALL CONTAINS WITH OVERBAR
+ return rune(0x22fe), true
+ case "notnivc":
+ // CONTAINS WITH OVERBAR
+ return rune(0x22fd), true
+ case "not":
+ // NOT SIGN
+ return rune(0xac), true
+ case "npart":
+ // PARTIAL DIFFERENTIAL with slash
+ return rune(0x2202), true
+ case "npar":
+ // NOT PARALLEL TO
+ return rune(0x2226), true
+ case "nparallel":
+ // NOT PARALLEL TO
+ return rune(0x2226), true
+ case "nparsl":
+ // DOUBLE SOLIDUS OPERATOR with reverse slash
+ return rune(0x2afd), true
+ case "npolint":
+ // LINE INTEGRATION NOT INCLUDING THE POLE
+ return rune(0x2a14), true
+ case "nprsim":
+ // PRECEDES OR EQUIVALENT TO with slash
+ return rune(0x227e), true
+ case "npr":
+ // DOES NOT PRECEDE
+ return rune(0x2280), true
+ case "nprcue":
+ // DOES NOT PRECEDE OR EQUAL
+ return rune(0x22e0), true
+ case "nprec":
+ // DOES NOT PRECEDE
+ return rune(0x2280), true
+ case "npre":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2aaf), true
+ case "npreceq":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2aaf), true
+ case "nrArr":
+ // RIGHTWARDS DOUBLE ARROW WITH STROKE
+ return rune(0x21cf), true
+ case "nrarrw":
+ // RIGHTWARDS WAVE ARROW with slash
+ return rune(0x219d), true
+ case "nrarr":
+ // RIGHTWARDS ARROW WITH STROKE
+ return rune(0x219b), true
+ case "nrarrc":
+ // WAVE ARROW POINTING DIRECTLY RIGHT with slash
+ return rune(0x2933), true
+ case "nrightarrow":
+ // RIGHTWARDS ARROW WITH STROKE
+ return rune(0x219b), true
+ case "nrtri":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP
+ return rune(0x22eb), true
+ case "nrtrie":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ return rune(0x22ed), true
+ case "nsGt":
+ // DOUBLE NESTED GREATER-THAN with slash
+ return rune(0x2aa2), true
+ case "nsLt":
+ // DOUBLE NESTED LESS-THAN with slash
+ return rune(0x2aa1), true
+ case "nscsim":
+ // SUCCEEDS OR EQUIVALENT TO with slash
+ return rune(0x227f), true
+ case "nsc":
+ // DOES NOT SUCCEED
+ return rune(0x2281), true
+ case "nsccue":
+ // DOES NOT SUCCEED OR EQUAL
+ return rune(0x22e1), true
+ case "nsce":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2ab0), true
+ case "nscr":
+ // MATHEMATICAL SCRIPT SMALL N
+ return rune(0x01d4c3), true
+ case "nshortmid":
+ // DOES NOT DIVIDE
+ return rune(0x2224), true
+ case "nshortparallel":
+ // NOT PARALLEL TO
+ return rune(0x2226), true
+ case "nsim":
+ // NOT TILDE
+ return rune(0x2241), true
+ case "nsime":
+ // NOT ASYMPTOTICALLY EQUAL TO
+ return rune(0x2244), true
+ case "nsimeq":
+ // NOT ASYMPTOTICALLY EQUAL TO
+ return rune(0x2244), true
+ case "nsmid":
+ // DOES NOT DIVIDE
+ return rune(0x2224), true
+ case "nspar":
+ // NOT PARALLEL TO
+ return rune(0x2226), true
+ case "nsqsub":
+ // SQUARE IMAGE OF with slash
+ return rune(0x228f), true
+ case "nsqsube":
+ // NOT SQUARE IMAGE OF OR EQUAL TO
+ return rune(0x22e2), true
+ case "nsqsup":
+ // SQUARE ORIGINAL OF with slash
+ return rune(0x2290), true
+ case "nsqsupe":
+ // NOT SQUARE ORIGINAL OF OR EQUAL TO
+ return rune(0x22e3), true
+ case "nsubset":
+ // SUBSET OF with vertical line
+ return rune(0x2282), true
+ case "nsub":
+ // NOT A SUBSET OF
+ return rune(0x2284), true
+ case "nsubE":
+ // SUBSET OF ABOVE EQUALS SIGN with slash
+ return rune(0x2ac5), true
+ case "nsube":
+ // NEITHER A SUBSET OF NOR EQUAL TO
+ return rune(0x2288), true
+ case "nsubseteq":
+ // NEITHER A SUBSET OF NOR EQUAL TO
+ return rune(0x2288), true
+ case "nsubseteqq":
+ // SUBSET OF ABOVE EQUALS SIGN with slash
+ return rune(0x2ac5), true
+ case "nsucc":
+ // DOES NOT SUCCEED
+ return rune(0x2281), true
+ case "nsucceq":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN with slash
+ return rune(0x2ab0), true
+ case "nsupset":
+ // SUPERSET OF with vertical line
+ return rune(0x2283), true
+ case "nsup":
+ // NOT A SUPERSET OF
+ return rune(0x2285), true
+ case "nsupE":
+ // SUPERSET OF ABOVE EQUALS SIGN with slash
+ return rune(0x2ac6), true
+ case "nsupe":
+ // NEITHER A SUPERSET OF NOR EQUAL TO
+ return rune(0x2289), true
+ case "nsupseteq":
+ // NEITHER A SUPERSET OF NOR EQUAL TO
+ return rune(0x2289), true
+ case "nsupseteqq":
+ // SUPERSET OF ABOVE EQUALS SIGN with slash
+ return rune(0x2ac6), true
+ case "ntgl":
+ // NEITHER GREATER-THAN NOR LESS-THAN
+ return rune(0x2279), true
+ case "ntilde":
+ // LATIN SMALL LETTER N WITH TILDE
+ return rune(0xf1), true
+ case "ntlg":
+ // NEITHER LESS-THAN NOR GREATER-THAN
+ return rune(0x2278), true
+ case "ntriangleleft":
+ // NOT NORMAL SUBGROUP OF
+ return rune(0x22ea), true
+ case "ntrianglelefteq":
+ // NOT NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22ec), true
+ case "ntriangleright":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP
+ return rune(0x22eb), true
+ case "ntrianglerighteq":
+ // DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+ return rune(0x22ed), true
+ case "numero":
+ // NUMERO SIGN
+ return rune(0x2116), true
+ case "numsp":
+ // FIGURE SPACE
+ return rune(0x2007), true
+ case "nu":
+ // GREEK SMALL LETTER NU
+ return rune(0x03bd), true
+ case "num":
+ // NUMBER SIGN
+ return rune(0x23), true
+ case "nvDash":
+ // NOT TRUE
+ return rune(0x22ad), true
+ case "nvHarr":
+ // LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE
+ return rune(0x2904), true
+ case "nvap":
+ // EQUIVALENT TO with vertical line
+ return rune(0x224d), true
+ case "nvbrtri":
+ // VERTICAL BAR BESIDE RIGHT TRIANGLE with slash
+ return rune(0x29d0), true
+ case "nvdash":
+ // DOES NOT PROVE
+ return rune(0x22ac), true
+ case "nvge":
+ // GREATER-THAN OR EQUAL TO with vertical line
+ return rune(0x2265), true
+ case "nvgt":
+ // GREATER-THAN SIGN with vertical line
+ return rune(0x3e), true
+ case "nvinfin":
+ // INFINITY NEGATED WITH VERTICAL BAR
+ return rune(0x29de), true
+ case "nvlArr":
+ // LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE
+ return rune(0x2902), true
+ case "nvle":
+ // LESS-THAN OR EQUAL TO with vertical line
+ return rune(0x2264), true
+ case "nvltrie":
+ // NORMAL SUBGROUP OF OR EQUAL TO with vertical line
+ return rune(0x22b4), true
+ case "nvlt":
+ // LESS-THAN SIGN with vertical line
+ return rune(0x3c), true
+ case "nvrArr":
+ // RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE
+ return rune(0x2903), true
+ case "nvrtrie":
+ // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO with vertical line
+ return rune(0x22b5), true
+ case "nvsim":
+ // TILDE OPERATOR with vertical line
+ return rune(0x223c), true
+ case "nwArr":
+ // NORTH WEST DOUBLE ARROW
+ return rune(0x21d6), true
+ case "nwarhk":
+ // NORTH WEST ARROW WITH HOOK
+ return rune(0x2923), true
+ case "nwarrow":
+ // NORTH WEST ARROW
+ return rune(0x2196), true
+ case "nwarr":
+ // NORTH WEST ARROW
+ return rune(0x2196), true
+ case "nwnear":
+ // NORTH WEST ARROW AND NORTH EAST ARROW
+ return rune(0x2927), true
+ case "nwonearr":
+ // NORTH WEST ARROW CROSSING NORTH EAST ARROW
+ return rune(0x2932), true
+ case "nwsesarr":
+ // NORTH WEST AND SOUTH EAST ARROW
+ return rune(0x2921), true
+ }
+
+ case 'o':
+ switch name {
+ case "oS":
+ // CIRCLED LATIN CAPITAL LETTER S
+ return rune(0x24c8), true
+ case "oacgr":
+ // GREEK SMALL LETTER OMICRON WITH TONOS
+ return rune(0x03cc), true
+ case "oacute":
+ // LATIN SMALL LETTER O WITH ACUTE
+ return rune(0xf3), true
+ case "oast":
+ // CIRCLED ASTERISK OPERATOR
+ return rune(0x229b), true
+ case "obsol":
+ // CIRCLED REVERSE SOLIDUS
+ return rune(0x29b8), true
+ case "ocir":
+ // CIRCLED RING OPERATOR
+ return rune(0x229a), true
+ case "ocirc":
+ // LATIN SMALL LETTER O WITH CIRCUMFLEX
+ return rune(0xf4), true
+ case "ocy":
+ // CYRILLIC SMALL LETTER O
+ return rune(0x043e), true
+ case "odash":
+ // CIRCLED DASH
+ return rune(0x229d), true
+ case "odblac":
+ // LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ return rune(0x0151), true
+ case "odiv":
+ // CIRCLED DIVISION SIGN
+ return rune(0x2a38), true
+ case "odot":
+ // CIRCLED DOT OPERATOR
+ return rune(0x2299), true
+ case "odsold":
+ // CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN
+ return rune(0x29bc), true
+ case "oelig":
+ // LATIN SMALL LIGATURE OE
+ return rune(0x0153), true
+ case "ofcir":
+ // CIRCLED BULLET
+ return rune(0x29bf), true
+ case "ofr":
+ // MATHEMATICAL FRAKTUR SMALL O
+ return rune(0x01d52c), true
+ case "ogon":
+ // OGONEK
+ return rune(0x02db), true
+ case "ogr":
+ // GREEK SMALL LETTER OMICRON
+ return rune(0x03bf), true
+ case "ograve":
+ // LATIN SMALL LETTER O WITH GRAVE
+ return rune(0xf2), true
+ case "ogt":
+ // CIRCLED GREATER-THAN
+ return rune(0x29c1), true
+ case "ohacgr":
+ // GREEK SMALL LETTER OMEGA WITH TONOS
+ return rune(0x03ce), true
+ case "ohbar":
+ // CIRCLE WITH HORIZONTAL BAR
+ return rune(0x29b5), true
+ case "ohgr":
+ // GREEK SMALL LETTER OMEGA
+ return rune(0x03c9), true
+ case "ohm":
+ // GREEK CAPITAL LETTER OMEGA
+ return rune(0x03a9), true
+ case "oint":
+ // CONTOUR INTEGRAL
+ return rune(0x222e), true
+ case "olarr":
+ // ANTICLOCKWISE OPEN CIRCLE ARROW
+ return rune(0x21ba), true
+ case "olcir":
+ // CIRCLED WHITE BULLET
+ return rune(0x29be), true
+ case "olcross":
+ // CIRCLE WITH SUPERIMPOSED X
+ return rune(0x29bb), true
+ case "oline":
+ // OVERLINE
+ return rune(0x203e), true
+ case "olt":
+ // CIRCLED LESS-THAN
+ return rune(0x29c0), true
+ case "omacr":
+ // LATIN SMALL LETTER O WITH MACRON
+ return rune(0x014d), true
+ case "omega":
+ // GREEK SMALL LETTER OMEGA
+ return rune(0x03c9), true
+ case "omicron":
+ // GREEK SMALL LETTER OMICRON
+ return rune(0x03bf), true
+ case "omid":
+ // CIRCLED VERTICAL BAR
+ return rune(0x29b6), true
+ case "ominus":
+ // CIRCLED MINUS
+ return rune(0x2296), true
+ case "oopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL O
+ return rune(0x01d560), true
+ case "opar":
+ // CIRCLED PARALLEL
+ return rune(0x29b7), true
+ case "operp":
+ // CIRCLED PERPENDICULAR
+ return rune(0x29b9), true
+ case "opfgamma":
+ // DOUBLE-STRUCK SMALL GAMMA
+ return rune(0x213d), true
+ case "opfpi":
+ // DOUBLE-STRUCK CAPITAL PI
+ return rune(0x213f), true
+ case "opfsum":
+ // DOUBLE-STRUCK N-ARY SUMMATION
+ return rune(0x2140), true
+ case "oplus":
+ // CIRCLED PLUS
+ return rune(0x2295), true
+ case "orarr":
+ // CLOCKWISE OPEN CIRCLE ARROW
+ return rune(0x21bb), true
+ case "or":
+ // LOGICAL OR
+ return rune(0x2228), true
+ case "orderof":
+ // SCRIPT SMALL O
+ return rune(0x2134), true
+ case "order":
+ // SCRIPT SMALL O
+ return rune(0x2134), true
+ case "ord":
+ // LOGICAL OR WITH HORIZONTAL DASH
+ return rune(0x2a5d), true
+ case "ordf":
+ // FEMININE ORDINAL INDICATOR
+ return rune(0xaa), true
+ case "ordm":
+ // MASCULINE ORDINAL INDICATOR
+ return rune(0xba), true
+ case "origof":
+ // ORIGINAL OF
+ return rune(0x22b6), true
+ case "oror":
+ // TWO INTERSECTING LOGICAL OR
+ return rune(0x2a56), true
+ case "orslope":
+ // SLOPING LARGE OR
+ return rune(0x2a57), true
+ case "orv":
+ // LOGICAL OR WITH MIDDLE STEM
+ return rune(0x2a5b), true
+ case "oscr":
+ // SCRIPT SMALL O
+ return rune(0x2134), true
+ case "oslash":
+ // LATIN SMALL LETTER O WITH STROKE
+ return rune(0xf8), true
+ case "osol":
+ // CIRCLED DIVISION SLASH
+ return rune(0x2298), true
+ case "otilde":
+ // LATIN SMALL LETTER O WITH TILDE
+ return rune(0xf5), true
+ case "otimes":
+ // CIRCLED TIMES
+ return rune(0x2297), true
+ case "otimesas":
+ // CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT
+ return rune(0x2a36), true
+ case "ouml":
+ // LATIN SMALL LETTER O WITH DIAERESIS
+ return rune(0xf6), true
+ case "ovbar":
+ // APL FUNCTIONAL SYMBOL CIRCLE STILE
+ return rune(0x233d), true
+ case "ovrbrk":
+ // TOP SQUARE BRACKET
+ return rune(0x23b4), true
+ case "ovrcub":
+ // TOP CURLY BRACKET
+ return rune(0x23de), true
+ case "ovrpar":
+ // TOP PARENTHESIS
+ return rune(0x23dc), true
+ case "oxuarr":
+ // UP ARROW THROUGH CIRCLE
+ return rune(0x29bd), true
+ }
+
+ case 'p':
+ switch name {
+ case "part":
+ // PARTIAL DIFFERENTIAL
+ return rune(0x2202), true
+ case "par":
+ // PARALLEL TO
+ return rune(0x2225), true
+ case "parallel":
+ // PARALLEL TO
+ return rune(0x2225), true
+ case "para":
+ // PILCROW SIGN
+ return rune(0xb6), true
+ case "parsim":
+ // PARALLEL WITH TILDE OPERATOR
+ return rune(0x2af3), true
+ case "parsl":
+ // DOUBLE SOLIDUS OPERATOR
+ return rune(0x2afd), true
+ case "pcy":
+ // CYRILLIC SMALL LETTER PE
+ return rune(0x043f), true
+ case "percnt":
+ // PERCENT SIGN
+ return rune(0x25), true
+ case "period":
+ // FULL STOP
+ return rune(0x2e), true
+ case "permil":
+ // PER MILLE SIGN
+ return rune(0x2030), true
+ case "perp":
+ // UP TACK
+ return rune(0x22a5), true
+ case "pertenk":
+ // PER TEN THOUSAND SIGN
+ return rune(0x2031), true
+ case "pfr":
+ // MATHEMATICAL FRAKTUR SMALL P
+ return rune(0x01d52d), true
+ case "pgr":
+ // GREEK SMALL LETTER PI
+ return rune(0x03c0), true
+ case "phgr":
+ // GREEK SMALL LETTER PHI
+ return rune(0x03c6), true
+ case "phis":
+ // GREEK PHI SYMBOL
+ return rune(0x03d5), true
+ case "phiv":
+ // GREEK PHI SYMBOL
+ return rune(0x03d5), true
+ case "phi":
+ // GREEK SMALL LETTER PHI
+ return rune(0x03c6), true
+ case "phmmat":
+ // SCRIPT CAPITAL M
+ return rune(0x2133), true
+ case "phone":
+ // BLACK TELEPHONE
+ return rune(0x260e), true
+ case "pitchfork":
+ // PITCHFORK
+ return rune(0x22d4), true
+ case "piv":
+ // GREEK PI SYMBOL
+ return rune(0x03d6), true
+ case "pi":
+ // GREEK SMALL LETTER PI
+ return rune(0x03c0), true
+ case "planck":
+ // PLANCK CONSTANT OVER TWO PI
+ return rune(0x210f), true
+ case "planckh":
+ // PLANCK CONSTANT
+ return rune(0x210e), true
+ case "plankv":
+ // PLANCK CONSTANT OVER TWO PI
+ return rune(0x210f), true
+ case "plusacir":
+ // PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE
+ return rune(0x2a23), true
+ case "plusb":
+ // SQUARED PLUS
+ return rune(0x229e), true
+ case "pluscir":
+ // PLUS SIGN WITH SMALL CIRCLE ABOVE
+ return rune(0x2a22), true
+ case "plusdo":
+ // DOT PLUS
+ return rune(0x2214), true
+ case "plusdu":
+ // PLUS SIGN WITH DOT BELOW
+ return rune(0x2a25), true
+ case "pluse":
+ // PLUS SIGN ABOVE EQUALS SIGN
+ return rune(0x2a72), true
+ case "plusmn":
+ // PLUS-MINUS SIGN
+ return rune(0xb1), true
+ case "plussim":
+ // PLUS SIGN WITH TILDE BELOW
+ return rune(0x2a26), true
+ case "plustrif":
+ // PLUS SIGN WITH BLACK TRIANGLE
+ return rune(0x2a28), true
+ case "plustwo":
+ // PLUS SIGN WITH SUBSCRIPT TWO
+ return rune(0x2a27), true
+ case "plus":
+ // PLUS SIGN
+ return rune(0x2b), true
+ case "pm":
+ // PLUS-MINUS SIGN
+ return rune(0xb1), true
+ case "pointint":
+ // INTEGRAL AROUND A POINT OPERATOR
+ return rune(0x2a15), true
+ case "popf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL P
+ return rune(0x01d561), true
+ case "pound":
+ // POUND SIGN
+ return rune(0xa3), true
+ case "prod":
+ // N-ARY PRODUCT
+ return rune(0x220f), true
+ case "prop":
+ // PROPORTIONAL TO
+ return rune(0x221d), true
+ case "propto":
+ // PROPORTIONAL TO
+ return rune(0x221d), true
+ case "pr":
+ // PRECEDES
+ return rune(0x227a), true
+ case "prE":
+ // PRECEDES ABOVE EQUALS SIGN
+ return rune(0x2ab3), true
+ case "prap":
+ // PRECEDES ABOVE ALMOST EQUAL TO
+ return rune(0x2ab7), true
+ case "prcue":
+ // PRECEDES OR EQUAL TO
+ return rune(0x227c), true
+ case "prec":
+ // PRECEDES
+ return rune(0x227a), true
+ case "preccurlyeq":
+ // PRECEDES OR EQUAL TO
+ return rune(0x227c), true
+ case "precnsim":
+ // PRECEDES BUT NOT EQUIVALENT TO
+ return rune(0x22e8), true
+ case "precsim":
+ // PRECEDES OR EQUIVALENT TO
+ return rune(0x227e), true
+ case "pre":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2aaf), true
+ case "precapprox":
+ // PRECEDES ABOVE ALMOST EQUAL TO
+ return rune(0x2ab7), true
+ case "preceq":
+ // PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2aaf), true
+ case "precnapprox":
+ // PRECEDES ABOVE NOT ALMOST EQUAL TO
+ return rune(0x2ab9), true
+ case "precneqq":
+ // PRECEDES ABOVE NOT EQUAL TO
+ return rune(0x2ab5), true
+ case "primes":
+ // DOUBLE-STRUCK CAPITAL P
+ return rune(0x2119), true
+ case "prime":
+ // PRIME
+ return rune(0x2032), true
+ case "prnE":
+ // PRECEDES ABOVE NOT EQUAL TO
+ return rune(0x2ab5), true
+ case "prnap":
+ // PRECEDES ABOVE NOT ALMOST EQUAL TO
+ return rune(0x2ab9), true
+ case "prnsim":
+ // PRECEDES BUT NOT EQUIVALENT TO
+ return rune(0x22e8), true
+ case "profalar":
+ // ALL AROUND-PROFILE
+ return rune(0x232e), true
+ case "profline":
+ // ARC
+ return rune(0x2312), true
+ case "profsurf":
+ // SEGMENT
+ return rune(0x2313), true
+ case "prsim":
+ // PRECEDES OR EQUIVALENT TO
+ return rune(0x227e), true
+ case "prurel":
+ // PRECEDES UNDER RELATION
+ return rune(0x22b0), true
+ case "pscr":
+ // MATHEMATICAL SCRIPT SMALL P
+ return rune(0x01d4c5), true
+ case "psgr":
+ // GREEK SMALL LETTER PSI
+ return rune(0x03c8), true
+ case "psi":
+ // GREEK SMALL LETTER PSI
+ return rune(0x03c8), true
+ case "puncsp":
+ // PUNCTUATION SPACE
+ return rune(0x2008), true
+ }
+
+ case 'q':
+ switch name {
+ case "qfr":
+ // MATHEMATICAL FRAKTUR SMALL Q
+ return rune(0x01d52e), true
+ case "qint":
+ // QUADRUPLE INTEGRAL OPERATOR
+ return rune(0x2a0c), true
+ case "qopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL Q
+ return rune(0x01d562), true
+ case "qprime":
+ // QUADRUPLE PRIME
+ return rune(0x2057), true
+ case "qscr":
+ // MATHEMATICAL SCRIPT SMALL Q
+ return rune(0x01d4c6), true
+ case "quaternions":
+ // DOUBLE-STRUCK CAPITAL H
+ return rune(0x210d), true
+ case "quatint":
+ // QUATERNION INTEGRAL OPERATOR
+ return rune(0x2a16), true
+ case "questeq":
+ // QUESTIONED EQUAL TO
+ return rune(0x225f), true
+ case "quest":
+ // QUESTION MARK
+ return rune(0x3f), true
+ case "quot":
+ // QUOTATION MARK
+ return rune(0x22), true
+ }
+
+ case 'r':
+ switch name {
+ case "rAarr":
+ // RIGHTWARDS TRIPLE ARROW
+ return rune(0x21db), true
+ case "rArr":
+ // RIGHTWARDS DOUBLE ARROW
+ return rune(0x21d2), true
+ case "rAtail":
+ // RIGHTWARDS DOUBLE ARROW-TAIL
+ return rune(0x291c), true
+ case "rBarr":
+ // RIGHTWARDS TRIPLE DASH ARROW
+ return rune(0x290f), true
+ case "rHar":
+ // RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN
+ return rune(0x2964), true
+ case "race":
+ // REVERSED TILDE with underline
+ return rune(0x223d), true
+ case "racute":
+ // LATIN SMALL LETTER R WITH ACUTE
+ return rune(0x0155), true
+ case "radic":
+ // SQUARE ROOT
+ return rune(0x221a), true
+ case "raemptyv":
+ // EMPTY SET WITH RIGHT ARROW ABOVE
+ return rune(0x29b3), true
+ case "rang":
+ // MATHEMATICAL RIGHT ANGLE BRACKET
+ return rune(0x27e9), true
+ case "rangd":
+ // RIGHT ANGLE BRACKET WITH DOT
+ return rune(0x2992), true
+ case "range":
+ // REVERSED ANGLE WITH UNDERBAR
+ return rune(0x29a5), true
+ case "rangle":
+ // MATHEMATICAL RIGHT ANGLE BRACKET
+ return rune(0x27e9), true
+ case "raquo":
+ // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+ return rune(0xbb), true
+ case "rarr2":
+ // RIGHTWARDS PAIRED ARROWS
+ return rune(0x21c9), true
+ case "rarr3":
+ // THREE RIGHTWARDS ARROWS
+ return rune(0x21f6), true
+ case "rarrb":
+ // RIGHTWARDS ARROW TO BAR
+ return rune(0x21e5), true
+ case "rarrhk":
+ // RIGHTWARDS ARROW WITH HOOK
+ return rune(0x21aa), true
+ case "rarrlp":
+ // RIGHTWARDS ARROW WITH LOOP
+ return rune(0x21ac), true
+ case "rarrtl":
+ // RIGHTWARDS ARROW WITH TAIL
+ return rune(0x21a3), true
+ case "rarrw":
+ // RIGHTWARDS WAVE ARROW
+ return rune(0x219d), true
+ case "rarr":
+ // RIGHTWARDS ARROW
+ return rune(0x2192), true
+ case "rarrap":
+ // RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO
+ return rune(0x2975), true
+ case "rarrbfs":
+ // RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND
+ return rune(0x2920), true
+ case "rarrc":
+ // WAVE ARROW POINTING DIRECTLY RIGHT
+ return rune(0x2933), true
+ case "rarrfs":
+ // RIGHTWARDS ARROW TO BLACK DIAMOND
+ return rune(0x291e), true
+ case "rarrpl":
+ // RIGHTWARDS ARROW WITH PLUS BELOW
+ return rune(0x2945), true
+ case "rarrsim":
+ // RIGHTWARDS ARROW ABOVE TILDE OPERATOR
+ return rune(0x2974), true
+ case "rarrx":
+ // RIGHTWARDS ARROW THROUGH X
+ return rune(0x2947), true
+ case "ratail":
+ // RIGHTWARDS ARROW-TAIL
+ return rune(0x291a), true
+ case "ratio":
+ // RATIO
+ return rune(0x2236), true
+ case "rationals":
+ // DOUBLE-STRUCK CAPITAL Q
+ return rune(0x211a), true
+ case "rbarr":
+ // RIGHTWARDS DOUBLE DASH ARROW
+ return rune(0x290d), true
+ case "rbbrk":
+ // LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT
+ return rune(0x2773), true
+ case "rbrace":
+ // RIGHT CURLY BRACKET
+ return rune(0x7d), true
+ case "rbrack":
+ // RIGHT SQUARE BRACKET
+ return rune(0x5d), true
+ case "rbrke":
+ // RIGHT SQUARE BRACKET WITH UNDERBAR
+ return rune(0x298c), true
+ case "rbrksld":
+ // RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER
+ return rune(0x298e), true
+ case "rbrkslu":
+ // RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER
+ return rune(0x2990), true
+ case "rcaron":
+ // LATIN SMALL LETTER R WITH CARON
+ return rune(0x0159), true
+ case "rcedil":
+ // LATIN SMALL LETTER R WITH CEDILLA
+ return rune(0x0157), true
+ case "rceil":
+ // RIGHT CEILING
+ return rune(0x2309), true
+ case "rcub":
+ // RIGHT CURLY BRACKET
+ return rune(0x7d), true
+ case "rcy":
+ // CYRILLIC SMALL LETTER ER
+ return rune(0x0440), true
+ case "rdca":
+ // ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS
+ return rune(0x2937), true
+ case "rdharb":
+ // RIGHTWARDS HARPOON WITH BARB DOWN TO BAR
+ return rune(0x2957), true
+ case "rdiag":
+ // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+ return rune(0x2571), true
+ case "rdiofdi":
+ // RISING DIAGONAL CROSSING FALLING DIAGONAL
+ return rune(0x292b), true
+ case "rdldhar":
+ // RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN
+ return rune(0x2969), true
+ case "rdosearr":
+ // RISING DIAGONAL CROSSING SOUTH EAST ARROW
+ return rune(0x2930), true
+ case "rdquor":
+ // RIGHT DOUBLE QUOTATION MARK
+ return rune(0x201d), true
+ case "rdquo":
+ // RIGHT DOUBLE QUOTATION MARK
+ return rune(0x201d), true
+ case "rdsh":
+ // DOWNWARDS ARROW WITH TIP RIGHTWARDS
+ return rune(0x21b3), true
+ case "realpart":
+ // BLACK-LETTER CAPITAL R
+ return rune(0x211c), true
+ case "reals":
+ // DOUBLE-STRUCK CAPITAL R
+ return rune(0x211d), true
+ case "real":
+ // BLACK-LETTER CAPITAL R
+ return rune(0x211c), true
+ case "realine":
+ // SCRIPT CAPITAL R
+ return rune(0x211b), true
+ case "rect":
+ // WHITE RECTANGLE
+ return rune(0x25ad), true
+ case "reg":
+ // REGISTERED SIGN
+ return rune(0xae), true
+ case "rfbowtie":
+ // BOWTIE WITH RIGHT HALF BLACK
+ return rune(0x29d2), true
+ case "rfisht":
+ // RIGHT FISH TAIL
+ return rune(0x297d), true
+ case "rfloor":
+ // RIGHT FLOOR
+ return rune(0x230b), true
+ case "rfr":
+ // MATHEMATICAL FRAKTUR SMALL R
+ return rune(0x01d52f), true
+ case "rftimes":
+ // TIMES WITH RIGHT HALF BLACK
+ return rune(0x29d5), true
+ case "rgr":
+ // GREEK SMALL LETTER RHO
+ return rune(0x03c1), true
+ case "rhard":
+ // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21c1), true
+ case "rharu":
+ // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21c0), true
+ case "rharul":
+ // RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH
+ return rune(0x296c), true
+ case "rhov":
+ // GREEK RHO SYMBOL
+ return rune(0x03f1), true
+ case "rho":
+ // GREEK SMALL LETTER RHO
+ return rune(0x03c1), true
+ case "rightarrowtail":
+ // RIGHTWARDS ARROW WITH TAIL
+ return rune(0x21a3), true
+ case "rightarrow":
+ // RIGHTWARDS ARROW
+ return rune(0x2192), true
+ case "rightharpoondown":
+ // RIGHTWARDS HARPOON WITH BARB DOWNWARDS
+ return rune(0x21c1), true
+ case "rightharpoonup":
+ // RIGHTWARDS HARPOON WITH BARB UPWARDS
+ return rune(0x21c0), true
+ case "rightleftarrows":
+ // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ return rune(0x21c4), true
+ case "rightleftharpoons":
+ // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ return rune(0x21cc), true
+ case "rightrightarrows":
+ // RIGHTWARDS PAIRED ARROWS
+ return rune(0x21c9), true
+ case "rightsquigarrow":
+ // RIGHTWARDS WAVE ARROW
+ return rune(0x219d), true
+ case "rightthreetimes":
+ // RIGHT SEMIDIRECT PRODUCT
+ return rune(0x22cc), true
+ case "rimply":
+ // RIGHT DOUBLE ARROW WITH ROUNDED HEAD
+ return rune(0x2970), true
+ case "ring":
+ // RING ABOVE
+ return rune(0x02da), true
+ case "risingdotseq":
+ // IMAGE OF OR APPROXIMATELY EQUAL TO
+ return rune(0x2253), true
+ case "rlarr2":
+ // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ return rune(0x21c4), true
+ case "rlarr":
+ // RIGHTWARDS ARROW OVER LEFTWARDS ARROW
+ return rune(0x21c4), true
+ case "rlhar":
+ // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ return rune(0x21cc), true
+ case "rlhar2":
+ // RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON
+ return rune(0x21cc), true
+ case "rlm":
+ // RIGHT-TO-LEFT MARK
+ return rune(0x200f), true
+ case "rmoust":
+ // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
+ return rune(0x23b1), true
+ case "rmoustache":
+ // UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
+ return rune(0x23b1), true
+ case "rnmid":
+ // DOES NOT DIVIDE WITH REVERSED NEGATION SLASH
+ return rune(0x2aee), true
+ case "roang":
+ // MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET
+ return rune(0x27ed), true
+ case "roarr":
+ // RIGHTWARDS OPEN-HEADED ARROW
+ return rune(0x21fe), true
+ case "robrk":
+ // MATHEMATICAL RIGHT WHITE SQUARE BRACKET
+ return rune(0x27e7), true
+ case "rocub":
+ // RIGHT WHITE CURLY BRACKET
+ return rune(0x2984), true
+ case "ropar":
+ // RIGHT WHITE PARENTHESIS
+ return rune(0x2986), true
+ case "ropf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL R
+ return rune(0x01d563), true
+ case "roplus":
+ // PLUS SIGN IN RIGHT HALF CIRCLE
+ return rune(0x2a2e), true
+ case "rotimes":
+ // MULTIPLICATION SIGN IN RIGHT HALF CIRCLE
+ return rune(0x2a35), true
+ case "rpargt":
+ // RIGHT ARC GREATER-THAN BRACKET
+ return rune(0x2994), true
+ case "rpar":
+ // RIGHT PARENTHESIS
+ return rune(0x29), true
+ case "rppolint":
+ // LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
+ return rune(0x2a12), true
+ case "rrarr":
+ // RIGHTWARDS PAIRED ARROWS
+ return rune(0x21c9), true
+ case "rsaquo":
+ // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+ return rune(0x203a), true
+ case "rscr":
+ // MATHEMATICAL SCRIPT SMALL R
+ return rune(0x01d4c7), true
+ case "rsh":
+ // UPWARDS ARROW WITH TIP RIGHTWARDS
+ return rune(0x21b1), true
+ case "rsolbar":
+ // REVERSE SOLIDUS WITH HORIZONTAL STROKE
+ return rune(0x29f7), true
+ case "rsqb":
+ // RIGHT SQUARE BRACKET
+ return rune(0x5d), true
+ case "rsquor":
+ // RIGHT SINGLE QUOTATION MARK
+ return rune(0x2019), true
+ case "rsquo":
+ // RIGHT SINGLE QUOTATION MARK
+ return rune(0x2019), true
+ case "rthree":
+ // RIGHT SEMIDIRECT PRODUCT
+ return rune(0x22cc), true
+ case "rtimes":
+ // RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT
+ return rune(0x22ca), true
+ case "rtrie":
+ // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ return rune(0x22b5), true
+ case "rtrif":
+ // BLACK RIGHT-POINTING SMALL TRIANGLE
+ return rune(0x25b8), true
+ case "rtri":
+ // WHITE RIGHT-POINTING SMALL TRIANGLE
+ return rune(0x25b9), true
+ case "rtriltri":
+ // RIGHT TRIANGLE ABOVE LEFT TRIANGLE
+ return rune(0x29ce), true
+ case "ruharb":
+ // RIGHTWARDS HARPOON WITH BARB UP TO BAR
+ return rune(0x2953), true
+ case "ruluhar":
+ // RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP
+ return rune(0x2968), true
+ case "rx":
+ // PRESCRIPTION TAKE
+ return rune(0x211e), true
+ }
+
+ case 's':
+ switch name {
+ case "sacute":
+ // LATIN SMALL LETTER S WITH ACUTE
+ return rune(0x015b), true
+ case "samalg":
+ // N-ARY COPRODUCT
+ return rune(0x2210), true
+ case "sampi":
+ // GREEK LETTER SAMPI
+ return rune(0x03e0), true
+ case "sbquo":
+ // SINGLE LOW-9 QUOTATION MARK
+ return rune(0x201a), true
+ case "sbsol":
+ // SMALL REVERSE SOLIDUS
+ return rune(0xfe68), true
+ case "sc":
+ // SUCCEEDS
+ return rune(0x227b), true
+ case "scE":
+ // SUCCEEDS ABOVE EQUALS SIGN
+ return rune(0x2ab4), true
+ case "scap":
+ // SUCCEEDS ABOVE ALMOST EQUAL TO
+ return rune(0x2ab8), true
+ case "scaron":
+ // LATIN SMALL LETTER S WITH CARON
+ return rune(0x0161), true
+ case "sccue":
+ // SUCCEEDS OR EQUAL TO
+ return rune(0x227d), true
+ case "scedil":
+ // LATIN SMALL LETTER S WITH CEDILLA
+ return rune(0x015f), true
+ case "sce":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2ab0), true
+ case "scirc":
+ // LATIN SMALL LETTER S WITH CIRCUMFLEX
+ return rune(0x015d), true
+ case "scnE":
+ // SUCCEEDS ABOVE NOT EQUAL TO
+ return rune(0x2ab6), true
+ case "scnap":
+ // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+ return rune(0x2aba), true
+ case "scnsim":
+ // SUCCEEDS BUT NOT EQUIVALENT TO
+ return rune(0x22e9), true
+ case "scpolint":
+ // LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
+ return rune(0x2a13), true
+ case "scsim":
+ // SUCCEEDS OR EQUIVALENT TO
+ return rune(0x227f), true
+ case "scy":
+ // CYRILLIC SMALL LETTER ES
+ return rune(0x0441), true
+ case "sdotb":
+ // SQUARED DOT OPERATOR
+ return rune(0x22a1), true
+ case "sdot":
+ // DOT OPERATOR
+ return rune(0x22c5), true
+ case "sdote":
+ // EQUALS SIGN WITH DOT BELOW
+ return rune(0x2a66), true
+ case "seArr":
+ // SOUTH EAST DOUBLE ARROW
+ return rune(0x21d8), true
+ case "searhk":
+ // SOUTH EAST ARROW WITH HOOK
+ return rune(0x2925), true
+ case "searrow":
+ // SOUTH EAST ARROW
+ return rune(0x2198), true
+ case "searr":
+ // SOUTH EAST ARROW
+ return rune(0x2198), true
+ case "sect":
+ // SECTION SIGN
+ return rune(0xa7), true
+ case "semi":
+ // SEMICOLON
+ return rune(0x3b), true
+ case "seonearr":
+ // SOUTH EAST ARROW CROSSING NORTH EAST ARROW
+ return rune(0x292d), true
+ case "seswar":
+ // SOUTH EAST ARROW AND SOUTH WEST ARROW
+ return rune(0x2929), true
+ case "setminus":
+ // SET MINUS
+ return rune(0x2216), true
+ case "setmn":
+ // SET MINUS
+ return rune(0x2216), true
+ case "sext":
+ // SIX POINTED BLACK STAR
+ return rune(0x2736), true
+ case "sfgr":
+ // GREEK SMALL LETTER FINAL SIGMA
+ return rune(0x03c2), true
+ case "sfrown":
+ // FROWN
+ return rune(0x2322), true
+ case "sfr":
+ // MATHEMATICAL FRAKTUR SMALL S
+ return rune(0x01d530), true
+ case "sgr":
+ // GREEK SMALL LETTER SIGMA
+ return rune(0x03c3), true
+ case "sharp":
+ // MUSIC SHARP SIGN
+ return rune(0x266f), true
+ case "shchcy":
+ // CYRILLIC SMALL LETTER SHCHA
+ return rune(0x0449), true
+ case "shcy":
+ // CYRILLIC SMALL LETTER SHA
+ return rune(0x0448), true
+ case "shortmid":
+ // DIVIDES
+ return rune(0x2223), true
+ case "shortparallel":
+ // PARALLEL TO
+ return rune(0x2225), true
+ case "shuffle":
+ // SHUFFLE PRODUCT
+ return rune(0x29e2), true
+ case "shy":
+ // SOFT HYPHEN
+ return rune(0xad), true
+ case "sigma":
+ // GREEK SMALL LETTER SIGMA
+ return rune(0x03c3), true
+ case "sigmaf":
+ // GREEK SMALL LETTER FINAL SIGMA
+ return rune(0x03c2), true
+ case "sigmav":
+ // GREEK SMALL LETTER FINAL SIGMA
+ return rune(0x03c2), true
+ case "sim":
+ // TILDE OPERATOR
+ return rune(0x223c), true
+ case "simdot":
+ // TILDE OPERATOR WITH DOT ABOVE
+ return rune(0x2a6a), true
+ case "sime":
+ // ASYMPTOTICALLY EQUAL TO
+ return rune(0x2243), true
+ case "simeq":
+ // ASYMPTOTICALLY EQUAL TO
+ return rune(0x2243), true
+ case "simg":
+ // SIMILAR OR GREATER-THAN
+ return rune(0x2a9e), true
+ case "simgE":
+ // SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN
+ return rune(0x2aa0), true
+ case "siml":
+ // SIMILAR OR LESS-THAN
+ return rune(0x2a9d), true
+ case "simlE":
+ // SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN
+ return rune(0x2a9f), true
+ case "simne":
+ // APPROXIMATELY BUT NOT ACTUALLY EQUAL TO
+ return rune(0x2246), true
+ case "simplus":
+ // PLUS SIGN WITH TILDE ABOVE
+ return rune(0x2a24), true
+ case "simrarr":
+ // TILDE OPERATOR ABOVE RIGHTWARDS ARROW
+ return rune(0x2972), true
+ case "slarr":
+ // LEFTWARDS ARROW
+ return rune(0x2190), true
+ case "slint":
+ // INTEGRAL AVERAGE WITH SLASH
+ return rune(0x2a0f), true
+ case "smallsetminus":
+ // SET MINUS
+ return rune(0x2216), true
+ case "smashp":
+ // SMASH PRODUCT
+ return rune(0x2a33), true
+ case "smeparsl":
+ // EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE
+ return rune(0x29e4), true
+ case "smid":
+ // DIVIDES
+ return rune(0x2223), true
+ case "smile":
+ // SMILE
+ return rune(0x2323), true
+ case "smt":
+ // SMALLER THAN
+ return rune(0x2aaa), true
+ case "smte":
+ // SMALLER THAN OR EQUAL TO
+ return rune(0x2aac), true
+ case "smtes":
+ // SMALLER THAN OR slanted EQUAL
+ return rune(0x2aac), true
+ case "softcy":
+ // CYRILLIC SMALL LETTER SOFT SIGN
+ return rune(0x044c), true
+ case "solbar":
+ // APL FUNCTIONAL SYMBOL SLASH BAR
+ return rune(0x233f), true
+ case "solb":
+ // SQUARED RISING DIAGONAL SLASH
+ return rune(0x29c4), true
+ case "sol":
+ // SOLIDUS
+ return rune(0x2f), true
+ case "sopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL S
+ return rune(0x01d564), true
+ case "spades":
+ // BLACK SPADE SUIT
+ return rune(0x2660), true
+ case "spadesuit":
+ // BLACK SPADE SUIT
+ return rune(0x2660), true
+ case "spar":
+ // PARALLEL TO
+ return rune(0x2225), true
+ case "sqcap":
+ // SQUARE CAP
+ return rune(0x2293), true
+ case "sqcaps":
+ // SQUARE CAP with serifs
+ return rune(0x2293), true
+ case "sqcup":
+ // SQUARE CUP
+ return rune(0x2294), true
+ case "sqcups":
+ // SQUARE CUP with serifs
+ return rune(0x2294), true
+ case "sqsub":
+ // SQUARE IMAGE OF
+ return rune(0x228f), true
+ case "sqsube":
+ // SQUARE IMAGE OF OR EQUAL TO
+ return rune(0x2291), true
+ case "sqsubset":
+ // SQUARE IMAGE OF
+ return rune(0x228f), true
+ case "sqsubseteq":
+ // SQUARE IMAGE OF OR EQUAL TO
+ return rune(0x2291), true
+ case "sqsup":
+ // SQUARE ORIGINAL OF
+ return rune(0x2290), true
+ case "sqsupe":
+ // SQUARE ORIGINAL OF OR EQUAL TO
+ return rune(0x2292), true
+ case "sqsupset":
+ // SQUARE ORIGINAL OF
+ return rune(0x2290), true
+ case "sqsupseteq":
+ // SQUARE ORIGINAL OF OR EQUAL TO
+ return rune(0x2292), true
+ case "squ":
+ // WHITE SQUARE
+ return rune(0x25a1), true
+ case "square":
+ // WHITE SQUARE
+ return rune(0x25a1), true
+ case "squarf":
+ // BLACK SMALL SQUARE
+ return rune(0x25aa), true
+ case "squb":
+ // SQUARED SQUARE
+ return rune(0x29c8), true
+ case "squerr":
+ // ERROR-BARRED WHITE SQUARE
+ return rune(0x29ee), true
+ case "squf":
+ // BLACK SMALL SQUARE
+ return rune(0x25aa), true
+ case "squferr":
+ // ERROR-BARRED BLACK SQUARE
+ return rune(0x29ef), true
+ case "srarr":
+ // RIGHTWARDS ARROW
+ return rune(0x2192), true
+ case "sscr":
+ // MATHEMATICAL SCRIPT SMALL S
+ return rune(0x01d4c8), true
+ case "ssetmn":
+ // SET MINUS
+ return rune(0x2216), true
+ case "ssmile":
+ // SMILE
+ return rune(0x2323), true
+ case "sstarf":
+ // STAR OPERATOR
+ return rune(0x22c6), true
+ case "starf":
+ // BLACK STAR
+ return rune(0x2605), true
+ case "star":
+ // WHITE STAR
+ return rune(0x2606), true
+ case "stigma":
+ // GREEK LETTER STIGMA
+ return rune(0x03da), true
+ case "straightepsilon":
+ // GREEK LUNATE EPSILON SYMBOL
+ return rune(0x03f5), true
+ case "straightphi":
+ // GREEK PHI SYMBOL
+ return rune(0x03d5), true
+ case "strns":
+ // MACRON
+ return rune(0xaf), true
+ case "sub":
+ // SUBSET OF
+ return rune(0x2282), true
+ case "subE":
+ // SUBSET OF ABOVE EQUALS SIGN
+ return rune(0x2ac5), true
+ case "subdot":
+ // SUBSET WITH DOT
+ return rune(0x2abd), true
+ case "sube":
+ // SUBSET OF OR EQUAL TO
+ return rune(0x2286), true
+ case "subedot":
+ // SUBSET OF OR EQUAL TO WITH DOT ABOVE
+ return rune(0x2ac3), true
+ case "submult":
+ // SUBSET WITH MULTIPLICATION SIGN BELOW
+ return rune(0x2ac1), true
+ case "subnE":
+ // SUBSET OF ABOVE NOT EQUAL TO
+ return rune(0x2acb), true
+ case "subne":
+ // SUBSET OF WITH NOT EQUAL TO
+ return rune(0x228a), true
+ case "subplus":
+ // SUBSET WITH PLUS SIGN BELOW
+ return rune(0x2abf), true
+ case "subrarr":
+ // SUBSET ABOVE RIGHTWARDS ARROW
+ return rune(0x2979), true
+ case "subset":
+ // SUBSET OF
+ return rune(0x2282), true
+ case "subseteq":
+ // SUBSET OF OR EQUAL TO
+ return rune(0x2286), true
+ case "subseteqq":
+ // SUBSET OF ABOVE EQUALS SIGN
+ return rune(0x2ac5), true
+ case "subsetneq":
+ // SUBSET OF WITH NOT EQUAL TO
+ return rune(0x228a), true
+ case "subsetneqq":
+ // SUBSET OF ABOVE NOT EQUAL TO
+ return rune(0x2acb), true
+ case "subsim":
+ // SUBSET OF ABOVE TILDE OPERATOR
+ return rune(0x2ac7), true
+ case "subsub":
+ // SUBSET ABOVE SUBSET
+ return rune(0x2ad5), true
+ case "subsup":
+ // SUBSET ABOVE SUPERSET
+ return rune(0x2ad3), true
+ case "succ":
+ // SUCCEEDS
+ return rune(0x227b), true
+ case "succapprox":
+ // SUCCEEDS ABOVE ALMOST EQUAL TO
+ return rune(0x2ab8), true
+ case "succcurlyeq":
+ // SUCCEEDS OR EQUAL TO
+ return rune(0x227d), true
+ case "succeq":
+ // SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
+ return rune(0x2ab0), true
+ case "succnapprox":
+ // SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+ return rune(0x2aba), true
+ case "succneqq":
+ // SUCCEEDS ABOVE NOT EQUAL TO
+ return rune(0x2ab6), true
+ case "succnsim":
+ // SUCCEEDS BUT NOT EQUIVALENT TO
+ return rune(0x22e9), true
+ case "succsim":
+ // SUCCEEDS OR EQUIVALENT TO
+ return rune(0x227f), true
+ case "sum":
+ // N-ARY SUMMATION
+ return rune(0x2211), true
+ case "sumint":
+ // SUMMATION WITH INTEGRAL
+ return rune(0x2a0b), true
+ case "sung":
+ // EIGHTH NOTE
+ return rune(0x266a), true
+ case "sup":
+ // SUPERSET OF
+ return rune(0x2283), true
+ case "sup1":
+ // SUPERSCRIPT ONE
+ return rune(0xb9), true
+ case "sup2":
+ // SUPERSCRIPT TWO
+ return rune(0xb2), true
+ case "sup3":
+ // SUPERSCRIPT THREE
+ return rune(0xb3), true
+ case "supE":
+ // SUPERSET OF ABOVE EQUALS SIGN
+ return rune(0x2ac6), true
+ case "supdot":
+ // SUPERSET WITH DOT
+ return rune(0x2abe), true
+ case "supdsub":
+ // SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET
+ return rune(0x2ad8), true
+ case "supe":
+ // SUPERSET OF OR EQUAL TO
+ return rune(0x2287), true
+ case "supedot":
+ // SUPERSET OF OR EQUAL TO WITH DOT ABOVE
+ return rune(0x2ac4), true
+ case "suphsol":
+ // SUPERSET PRECEDING SOLIDUS
+ return rune(0x27c9), true
+ case "suphsub":
+ // SUPERSET BESIDE SUBSET
+ return rune(0x2ad7), true
+ case "suplarr":
+ // SUPERSET ABOVE LEFTWARDS ARROW
+ return rune(0x297b), true
+ case "supmult":
+ // SUPERSET WITH MULTIPLICATION SIGN BELOW
+ return rune(0x2ac2), true
+ case "supnE":
+ // SUPERSET OF ABOVE NOT EQUAL TO
+ return rune(0x2acc), true
+ case "supne":
+ // SUPERSET OF WITH NOT EQUAL TO
+ return rune(0x228b), true
+ case "supplus":
+ // SUPERSET WITH PLUS SIGN BELOW
+ return rune(0x2ac0), true
+ case "supset":
+ // SUPERSET OF
+ return rune(0x2283), true
+ case "supseteq":
+ // SUPERSET OF OR EQUAL TO
+ return rune(0x2287), true
+ case "supseteqq":
+ // SUPERSET OF ABOVE EQUALS SIGN
+ return rune(0x2ac6), true
+ case "supsetneq":
+ // SUPERSET OF WITH NOT EQUAL TO
+ return rune(0x228b), true
+ case "supsetneqq":
+ // SUPERSET OF ABOVE NOT EQUAL TO
+ return rune(0x2acc), true
+ case "supsim":
+ // SUPERSET OF ABOVE TILDE OPERATOR
+ return rune(0x2ac8), true
+ case "supsub":
+ // SUPERSET ABOVE SUBSET
+ return rune(0x2ad4), true
+ case "supsup":
+ // SUPERSET ABOVE SUPERSET
+ return rune(0x2ad6), true
+ case "swArr":
+ // SOUTH WEST DOUBLE ARROW
+ return rune(0x21d9), true
+ case "swarhk":
+ // SOUTH WEST ARROW WITH HOOK
+ return rune(0x2926), true
+ case "swarrow":
+ // SOUTH WEST ARROW
+ return rune(0x2199), true
+ case "swarr":
+ // SOUTH WEST ARROW
+ return rune(0x2199), true
+ case "swnwar":
+ // SOUTH WEST ARROW AND NORTH WEST ARROW
+ return rune(0x292a), true
+ case "szlig":
+ // LATIN SMALL LETTER SHARP S
+ return rune(0xdf), true
+ }
+
+ case 't':
+ switch name {
+ case "target":
+ // POSITION INDICATOR
+ return rune(0x2316), true
+ case "tau":
+ // GREEK SMALL LETTER TAU
+ return rune(0x03c4), true
+ case "tbrk":
+ // TOP SQUARE BRACKET
+ return rune(0x23b4), true
+ case "tcaron":
+ // LATIN SMALL LETTER T WITH CARON
+ return rune(0x0165), true
+ case "tcedil":
+ // LATIN SMALL LETTER T WITH CEDILLA
+ return rune(0x0163), true
+ case "tcy":
+ // CYRILLIC SMALL LETTER TE
+ return rune(0x0442), true
+ case "tdot":
+ // COMBINING THREE DOTS ABOVE
+ return rune(0x20db), true
+ case "telrec":
+ // TELEPHONE RECORDER
+ return rune(0x2315), true
+ case "tfr":
+ // MATHEMATICAL FRAKTUR SMALL T
+ return rune(0x01d531), true
+ case "tgr":
+ // GREEK SMALL LETTER TAU
+ return rune(0x03c4), true
+ case "there4":
+ // THEREFORE
+ return rune(0x2234), true
+ case "therefore":
+ // THEREFORE
+ return rune(0x2234), true
+ case "thermod":
+ // THERMODYNAMIC
+ return rune(0x29e7), true
+ case "thetasym":
+ // GREEK THETA SYMBOL
+ return rune(0x03d1), true
+ case "thetas":
+ // GREEK SMALL LETTER THETA
+ return rune(0x03b8), true
+ case "thetav":
+ // GREEK THETA SYMBOL
+ return rune(0x03d1), true
+ case "theta":
+ // GREEK SMALL LETTER THETA
+ return rune(0x03b8), true
+ case "thgr":
+ // GREEK SMALL LETTER THETA
+ return rune(0x03b8), true
+ case "thickapprox":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "thicksim":
+ // TILDE OPERATOR
+ return rune(0x223c), true
+ case "thinsp":
+ // THIN SPACE
+ return rune(0x2009), true
+ case "thkap":
+ // ALMOST EQUAL TO
+ return rune(0x2248), true
+ case "thksim":
+ // TILDE OPERATOR
+ return rune(0x223c), true
+ case "thorn":
+ // LATIN SMALL LETTER THORN
+ return rune(0xfe), true
+ case "tilde":
+ // SMALL TILDE
+ return rune(0x02dc), true
+ case "timeint":
+ // INTEGRAL WITH TIMES SIGN
+ return rune(0x2a18), true
+ case "timesb":
+ // SQUARED TIMES
+ return rune(0x22a0), true
+ case "timesbar":
+ // MULTIPLICATION SIGN WITH UNDERBAR
+ return rune(0x2a31), true
+ case "timesd":
+ // MULTIPLICATION SIGN WITH DOT ABOVE
+ return rune(0x2a30), true
+ case "times":
+ // MULTIPLICATION SIGN
+ return rune(0xd7), true
+ case "tint":
+ // TRIPLE INTEGRAL
+ return rune(0x222d), true
+ case "toea":
+ // NORTH EAST ARROW AND SOUTH EAST ARROW
+ return rune(0x2928), true
+ case "top":
+ // DOWN TACK
+ return rune(0x22a4), true
+ case "topbot":
+ // APL FUNCTIONAL SYMBOL I-BEAM
+ return rune(0x2336), true
+ case "topcir":
+ // DOWN TACK WITH CIRCLE BELOW
+ return rune(0x2af1), true
+ case "topfork":
+ // PITCHFORK WITH TEE TOP
+ return rune(0x2ada), true
+ case "topf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL T
+ return rune(0x01d565), true
+ case "tosa":
+ // SOUTH EAST ARROW AND SOUTH WEST ARROW
+ return rune(0x2929), true
+ case "tprime":
+ // TRIPLE PRIME
+ return rune(0x2034), true
+ case "trade":
+ // TRADE MARK SIGN
+ return rune(0x2122), true
+ case "triS":
+ // S IN TRIANGLE
+ return rune(0x29cc), true
+ case "trianglelefteq":
+ // NORMAL SUBGROUP OF OR EQUAL TO
+ return rune(0x22b4), true
+ case "triangleq":
+ // DELTA EQUAL TO
+ return rune(0x225c), true
+ case "trianglerighteq":
+ // CONTAINS AS NORMAL SUBGROUP OR EQUAL TO
+ return rune(0x22b5), true
+ case "triangle":
+ // WHITE UP-POINTING SMALL TRIANGLE
+ return rune(0x25b5), true
+ case "triangledown":
+ // WHITE DOWN-POINTING SMALL TRIANGLE
+ return rune(0x25bf), true
+ case "triangleleft":
+ // WHITE LEFT-POINTING SMALL TRIANGLE
+ return rune(0x25c3), true
+ case "triangleright":
+ // WHITE RIGHT-POINTING SMALL TRIANGLE
+ return rune(0x25b9), true
+ case "tribar":
+ // TRIANGLE WITH UNDERBAR
+ return rune(0x29cb), true
+ case "tridot":
+ // WHITE UP-POINTING TRIANGLE WITH DOT
+ return rune(0x25ec), true
+ case "tridoto":
+ // TRIANGLE WITH DOT ABOVE
+ return rune(0x29ca), true
+ case "trie":
+ // DELTA EQUAL TO
+ return rune(0x225c), true
+ case "triminus":
+ // MINUS SIGN IN TRIANGLE
+ return rune(0x2a3a), true
+ case "triplus":
+ // PLUS SIGN IN TRIANGLE
+ return rune(0x2a39), true
+ case "trisb":
+ // TRIANGLE WITH SERIFS AT BOTTOM
+ return rune(0x29cd), true
+ case "tritime":
+ // MULTIPLICATION SIGN IN TRIANGLE
+ return rune(0x2a3b), true
+ case "trpezium":
+ // WHITE TRAPEZIUM
+ return rune(0x23e2), true
+ case "tscr":
+ // MATHEMATICAL SCRIPT SMALL T
+ return rune(0x01d4c9), true
+ case "tscy":
+ // CYRILLIC SMALL LETTER TSE
+ return rune(0x0446), true
+ case "tshcy":
+ // CYRILLIC SMALL LETTER TSHE
+ return rune(0x045b), true
+ case "tstrok":
+ // LATIN SMALL LETTER T WITH STROKE
+ return rune(0x0167), true
+ case "tverbar":
+ // TRIPLE VERTICAL BAR DELIMITER
+ return rune(0x2980), true
+ case "twixt":
+ // BETWEEN
+ return rune(0x226c), true
+ case "twoheadleftarrow":
+ // LEFTWARDS TWO HEADED ARROW
+ return rune(0x219e), true
+ case "twoheadrightarrow":
+ // RIGHTWARDS TWO HEADED ARROW
+ return rune(0x21a0), true
+ }
+
+ case 'u':
+ switch name {
+ case "uAarr":
+ // UPWARDS TRIPLE ARROW
+ return rune(0x290a), true
+ case "uArr":
+ // UPWARDS DOUBLE ARROW
+ return rune(0x21d1), true
+ case "uHar":
+ // UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT
+ return rune(0x2963), true
+ case "uacgr":
+ // GREEK SMALL LETTER UPSILON WITH TONOS
+ return rune(0x03cd), true
+ case "uacute":
+ // LATIN SMALL LETTER U WITH ACUTE
+ return rune(0xfa), true
+ case "uarr2":
+ // UPWARDS PAIRED ARROWS
+ return rune(0x21c8), true
+ case "uarr":
+ // UPWARDS ARROW
+ return rune(0x2191), true
+ case "uarrb":
+ // UPWARDS ARROW TO BAR
+ return rune(0x2912), true
+ case "uarrln":
+ // UPWARDS ARROW WITH HORIZONTAL STROKE
+ return rune(0x2909), true
+ case "ubrcy":
+ // CYRILLIC SMALL LETTER SHORT U
+ return rune(0x045e), true
+ case "ubreve":
+ // LATIN SMALL LETTER U WITH BREVE
+ return rune(0x016d), true
+ case "ucirc":
+ // LATIN SMALL LETTER U WITH CIRCUMFLEX
+ return rune(0xfb), true
+ case "ucy":
+ // CYRILLIC SMALL LETTER U
+ return rune(0x0443), true
+ case "udarr":
+ // UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW
+ return rune(0x21c5), true
+ case "udblac":
+ // LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ return rune(0x0171), true
+ case "udhar":
+ // UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT
+ return rune(0x296e), true
+ case "udiagr":
+ // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+ return rune(0x03b0), true
+ case "udigr":
+ // GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+ return rune(0x03cb), true
+ case "udrbrk":
+ // BOTTOM SQUARE BRACKET
+ return rune(0x23b5), true
+ case "udrcub":
+ // BOTTOM CURLY BRACKET
+ return rune(0x23df), true
+ case "udrpar":
+ // BOTTOM PARENTHESIS
+ return rune(0x23dd), true
+ case "ufisht":
+ // UP FISH TAIL
+ return rune(0x297e), true
+ case "ufr":
+ // MATHEMATICAL FRAKTUR SMALL U
+ return rune(0x01d532), true
+ case "ugr":
+ // GREEK SMALL LETTER UPSILON
+ return rune(0x03c5), true
+ case "ugrave":
+ // LATIN SMALL LETTER U WITH GRAVE
+ return rune(0xf9), true
+ case "uharl":
+ // UPWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21bf), true
+ case "uharr":
+ // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21be), true
+ case "uhblk":
+ // UPPER HALF BLOCK
+ return rune(0x2580), true
+ case "ulcorn":
+ // TOP LEFT CORNER
+ return rune(0x231c), true
+ case "ulcorner":
+ // TOP LEFT CORNER
+ return rune(0x231c), true
+ case "ulcrop":
+ // TOP LEFT CROP
+ return rune(0x230f), true
+ case "uldlshar":
+ // UP BARB LEFT DOWN BARB LEFT HARPOON
+ return rune(0x2951), true
+ case "ulharb":
+ // UPWARDS HARPOON WITH BARB LEFT TO BAR
+ return rune(0x2958), true
+ case "ultri":
+ // UPPER LEFT TRIANGLE
+ return rune(0x25f8), true
+ case "umacr":
+ // LATIN SMALL LETTER U WITH MACRON
+ return rune(0x016b), true
+ case "uml":
+ // DIAERESIS
+ return rune(0xa8), true
+ case "uogon":
+ // LATIN SMALL LETTER U WITH OGONEK
+ return rune(0x0173), true
+ case "uopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL U
+ return rune(0x01d566), true
+ case "uparrow":
+ // UPWARDS ARROW
+ return rune(0x2191), true
+ case "updownarrow":
+ // UP DOWN ARROW
+ return rune(0x2195), true
+ case "upharpoonleft":
+ // UPWARDS HARPOON WITH BARB LEFTWARDS
+ return rune(0x21bf), true
+ case "upharpoonright":
+ // UPWARDS HARPOON WITH BARB RIGHTWARDS
+ return rune(0x21be), true
+ case "upint":
+ // INTEGRAL WITH OVERBAR
+ return rune(0x2a1b), true
+ case "uplus":
+ // MULTISET UNION
+ return rune(0x228e), true
+ case "upsih":
+ // GREEK UPSILON WITH HOOK SYMBOL
+ return rune(0x03d2), true
+ case "upsilon":
+ // GREEK SMALL LETTER UPSILON
+ return rune(0x03c5), true
+ case "upsi":
+ // GREEK SMALL LETTER UPSILON
+ return rune(0x03c5), true
+ case "upuparrows":
+ // UPWARDS PAIRED ARROWS
+ return rune(0x21c8), true
+ case "urcorn":
+ // TOP RIGHT CORNER
+ return rune(0x231d), true
+ case "urcorner":
+ // TOP RIGHT CORNER
+ return rune(0x231d), true
+ case "urcrop":
+ // TOP RIGHT CROP
+ return rune(0x230e), true
+ case "urdrshar":
+ // UP BARB RIGHT DOWN BARB RIGHT HARPOON
+ return rune(0x294f), true
+ case "urharb":
+ // UPWARDS HARPOON WITH BARB RIGHT TO BAR
+ return rune(0x2954), true
+ case "uring":
+ // LATIN SMALL LETTER U WITH RING ABOVE
+ return rune(0x016f), true
+ case "urtrif":
+ // BLACK UPPER RIGHT TRIANGLE
+ return rune(0x25e5), true
+ case "urtri":
+ // UPPER RIGHT TRIANGLE
+ return rune(0x25f9), true
+ case "uscr":
+ // MATHEMATICAL SCRIPT SMALL U
+ return rune(0x01d4ca), true
+ case "utdot":
+ // UP RIGHT DIAGONAL ELLIPSIS
+ return rune(0x22f0), true
+ case "utilde":
+ // LATIN SMALL LETTER U WITH TILDE
+ return rune(0x0169), true
+ case "utrif":
+ // BLACK UP-POINTING SMALL TRIANGLE
+ return rune(0x25b4), true
+ case "utri":
+ // WHITE UP-POINTING SMALL TRIANGLE
+ return rune(0x25b5), true
+ case "uuarr":
+ // UPWARDS PAIRED ARROWS
+ return rune(0x21c8), true
+ case "uuml":
+ // LATIN SMALL LETTER U WITH DIAERESIS
+ return rune(0xfc), true
+ case "uwangle":
+ // OBLIQUE ANGLE OPENING DOWN
+ return rune(0x29a7), true
+ }
+
+ case 'v':
+ switch name {
+ case "vArr":
+ // UP DOWN DOUBLE ARROW
+ return rune(0x21d5), true
+ case "vBar":
+ // SHORT UP TACK WITH UNDERBAR
+ return rune(0x2ae8), true
+ case "vBarv":
+ // SHORT UP TACK ABOVE SHORT DOWN TACK
+ return rune(0x2ae9), true
+ case "vDash":
+ // TRUE
+ return rune(0x22a8), true
+ case "vDdash":
+ // VERTICAL BAR TRIPLE RIGHT TURNSTILE
+ return rune(0x2ae2), true
+ case "vangrt":
+ // RIGHT ANGLE VARIANT WITH SQUARE
+ return rune(0x299c), true
+ case "varepsilon":
+ // GREEK LUNATE EPSILON SYMBOL
+ return rune(0x03f5), true
+ case "varkappa":
+ // GREEK KAPPA SYMBOL
+ return rune(0x03f0), true
+ case "varnothing":
+ // EMPTY SET
+ return rune(0x2205), true
+ case "varphi":
+ // GREEK PHI SYMBOL
+ return rune(0x03d5), true
+ case "varpi":
+ // GREEK PI SYMBOL
+ return rune(0x03d6), true
+ case "varpropto":
+ // PROPORTIONAL TO
+ return rune(0x221d), true
+ case "varr":
+ // UP DOWN ARROW
+ return rune(0x2195), true
+ case "varrho":
+ // GREEK RHO SYMBOL
+ return rune(0x03f1), true
+ case "varsigma":
+ // GREEK SMALL LETTER FINAL SIGMA
+ return rune(0x03c2), true
+ case "varsubsetneq":
+ // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x228a), true
+ case "varsubsetneqq":
+ // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x2acb), true
+ case "varsupsetneq":
+ // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x228b), true
+ case "varsupsetneqq":
+ // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x2acc), true
+ case "vartheta":
+ // GREEK THETA SYMBOL
+ return rune(0x03d1), true
+ case "vartriangleleft":
+ // NORMAL SUBGROUP OF
+ return rune(0x22b2), true
+ case "vartriangleright":
+ // CONTAINS AS NORMAL SUBGROUP
+ return rune(0x22b3), true
+ case "vbrtri":
+ // VERTICAL BAR BESIDE RIGHT TRIANGLE
+ return rune(0x29d0), true
+ case "vcy":
+ // CYRILLIC SMALL LETTER VE
+ return rune(0x0432), true
+ case "vdash":
+ // RIGHT TACK
+ return rune(0x22a2), true
+ case "vee":
+ // LOGICAL OR
+ return rune(0x2228), true
+ case "veeBar":
+ // LOGICAL OR WITH DOUBLE UNDERBAR
+ return rune(0x2a63), true
+ case "veebar":
+ // XOR
+ return rune(0x22bb), true
+ case "veeeq":
+ // EQUIANGULAR TO
+ return rune(0x225a), true
+ case "vellip":
+ // VERTICAL ELLIPSIS
+ return rune(0x22ee), true
+ case "vellip4":
+ // DOTTED FENCE
+ return rune(0x2999), true
+ case "vellipv":
+ // TRIPLE COLON OPERATOR
+ return rune(0x2af6), true
+ case "verbar":
+ // VERTICAL LINE
+ return rune(0x7c), true
+ case "vert3":
+ // TRIPLE VERTICAL BAR BINARY RELATION
+ return rune(0x2af4), true
+ case "vert":
+ // VERTICAL LINE
+ return rune(0x7c), true
+ case "vfr":
+ // MATHEMATICAL FRAKTUR SMALL V
+ return rune(0x01d533), true
+ case "vldash":
+ // LEFT SQUARE BRACKET LOWER CORNER
+ return rune(0x23a3), true
+ case "vltri":
+ // NORMAL SUBGROUP OF
+ return rune(0x22b2), true
+ case "vnsub":
+ // SUBSET OF with vertical line
+ return rune(0x2282), true
+ case "vnsup":
+ // SUPERSET OF with vertical line
+ return rune(0x2283), true
+ case "vopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL V
+ return rune(0x01d567), true
+ case "vprime":
+ // PRIME
+ return rune(0x2032), true
+ case "vprop":
+ // PROPORTIONAL TO
+ return rune(0x221d), true
+ case "vrtri":
+ // CONTAINS AS NORMAL SUBGROUP
+ return rune(0x22b3), true
+ case "vscr":
+ // MATHEMATICAL SCRIPT SMALL V
+ return rune(0x01d4cb), true
+ case "vsubnE":
+ // SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x2acb), true
+ case "vsubne":
+ // SUBSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x228a), true
+ case "vsupnE":
+ // SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x2acc), true
+ case "vsupne":
+ // SUPERSET OF WITH NOT EQUAL TO - variant with stroke through bottom members
+ return rune(0x228b), true
+ case "vzigzag":
+ // VERTICAL ZIGZAG LINE
+ return rune(0x299a), true
+ }
+
+ case 'w':
+ switch name {
+ case "wcirc":
+ // LATIN SMALL LETTER W WITH CIRCUMFLEX
+ return rune(0x0175), true
+ case "wedbar":
+ // LOGICAL AND WITH UNDERBAR
+ return rune(0x2a5f), true
+ case "wedge":
+ // LOGICAL AND
+ return rune(0x2227), true
+ case "wedgeq":
+ // ESTIMATES
+ return rune(0x2259), true
+ case "weierp":
+ // SCRIPT CAPITAL P
+ return rune(0x2118), true
+ case "wfr":
+ // MATHEMATICAL FRAKTUR SMALL W
+ return rune(0x01d534), true
+ case "wopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL W
+ return rune(0x01d568), true
+ case "wp":
+ // SCRIPT CAPITAL P
+ return rune(0x2118), true
+ case "wreath":
+ // WREATH PRODUCT
+ return rune(0x2240), true
+ case "wr":
+ // WREATH PRODUCT
+ return rune(0x2240), true
+ case "wscr":
+ // MATHEMATICAL SCRIPT SMALL W
+ return rune(0x01d4cc), true
+ }
+
+ case 'x':
+ switch name {
+ case "xandand":
+ // TWO LOGICAL AND OPERATOR
+ return rune(0x2a07), true
+ case "xbsol":
+ // BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+ return rune(0x2571), true
+ case "xcap":
+ // N-ARY INTERSECTION
+ return rune(0x22c2), true
+ case "xcirc":
+ // LARGE CIRCLE
+ return rune(0x25ef), true
+ case "xcup":
+ // N-ARY UNION
+ return rune(0x22c3), true
+ case "xcupdot":
+ // N-ARY UNION OPERATOR WITH DOT
+ return rune(0x2a03), true
+ case "xdtri":
+ // WHITE DOWN-POINTING TRIANGLE
+ return rune(0x25bd), true
+ case "xfr":
+ // MATHEMATICAL FRAKTUR SMALL X
+ return rune(0x01d535), true
+ case "xgr":
+ // GREEK SMALL LETTER XI
+ return rune(0x03be), true
+ case "xhArr":
+ // LONG LEFT RIGHT DOUBLE ARROW
+ return rune(0x27fa), true
+ case "xharr":
+ // LONG LEFT RIGHT ARROW
+ return rune(0x27f7), true
+ case "xi":
+ // GREEK SMALL LETTER XI
+ return rune(0x03be), true
+ case "xlArr":
+ // LONG LEFTWARDS DOUBLE ARROW
+ return rune(0x27f8), true
+ case "xlarr":
+ // LONG LEFTWARDS ARROW
+ return rune(0x27f5), true
+ case "xmap":
+ // LONG RIGHTWARDS ARROW FROM BAR
+ return rune(0x27fc), true
+ case "xnis":
+ // CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE
+ return rune(0x22fb), true
+ case "xodot":
+ // N-ARY CIRCLED DOT OPERATOR
+ return rune(0x2a00), true
+ case "xopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL X
+ return rune(0x01d569), true
+ case "xoplus":
+ // N-ARY CIRCLED PLUS OPERATOR
+ return rune(0x2a01), true
+ case "xoror":
+ // TWO LOGICAL OR OPERATOR
+ return rune(0x2a08), true
+ case "xotime":
+ // N-ARY CIRCLED TIMES OPERATOR
+ return rune(0x2a02), true
+ case "xrArr":
+ // LONG RIGHTWARDS DOUBLE ARROW
+ return rune(0x27f9), true
+ case "xrarr":
+ // LONG RIGHTWARDS ARROW
+ return rune(0x27f6), true
+ case "xscr":
+ // MATHEMATICAL SCRIPT SMALL X
+ return rune(0x01d4cd), true
+ case "xsol":
+ // BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+ return rune(0x2572), true
+ case "xsqcap":
+ // N-ARY SQUARE INTERSECTION OPERATOR
+ return rune(0x2a05), true
+ case "xsqcup":
+ // N-ARY SQUARE UNION OPERATOR
+ return rune(0x2a06), true
+ case "xsqu":
+ // WHITE MEDIUM SQUARE
+ return rune(0x25fb), true
+ case "xsquf":
+ // BLACK MEDIUM SQUARE
+ return rune(0x25fc), true
+ case "xtimes":
+ // N-ARY TIMES OPERATOR
+ return rune(0x2a09), true
+ case "xuplus":
+ // N-ARY UNION OPERATOR WITH PLUS
+ return rune(0x2a04), true
+ case "xutri":
+ // WHITE UP-POINTING TRIANGLE
+ return rune(0x25b3), true
+ case "xvee":
+ // N-ARY LOGICAL OR
+ return rune(0x22c1), true
+ case "xwedge":
+ // N-ARY LOGICAL AND
+ return rune(0x22c0), true
+ }
+
+ case 'y':
+ switch name {
+ case "yacute":
+ // LATIN SMALL LETTER Y WITH ACUTE
+ return rune(0xfd), true
+ case "yacy":
+ // CYRILLIC SMALL LETTER YA
+ return rune(0x044f), true
+ case "ycirc":
+ // LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ return rune(0x0177), true
+ case "ycy":
+ // CYRILLIC SMALL LETTER YERU
+ return rune(0x044b), true
+ case "yen":
+ // YEN SIGN
+ return rune(0xa5), true
+ case "yfr":
+ // MATHEMATICAL FRAKTUR SMALL Y
+ return rune(0x01d536), true
+ case "yicy":
+ // CYRILLIC SMALL LETTER YI
+ return rune(0x0457), true
+ case "yopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL Y
+ return rune(0x01d56a), true
+ case "yscr":
+ // MATHEMATICAL SCRIPT SMALL Y
+ return rune(0x01d4ce), true
+ case "yucy":
+ // CYRILLIC SMALL LETTER YU
+ return rune(0x044e), true
+ case "yuml":
+ // LATIN SMALL LETTER Y WITH DIAERESIS
+ return rune(0xff), true
+ }
+
+ case 'z':
+ switch name {
+ case "zacute":
+ // LATIN SMALL LETTER Z WITH ACUTE
+ return rune(0x017a), true
+ case "zcaron":
+ // LATIN SMALL LETTER Z WITH CARON
+ return rune(0x017e), true
+ case "zcy":
+ // CYRILLIC SMALL LETTER ZE
+ return rune(0x0437), true
+ case "zdot":
+ // LATIN SMALL LETTER Z WITH DOT ABOVE
+ return rune(0x017c), true
+ case "zeetrf":
+ // BLACK-LETTER CAPITAL Z
+ return rune(0x2128), true
+ case "zeta":
+ // GREEK SMALL LETTER ZETA
+ return rune(0x03b6), true
+ case "zfr":
+ // MATHEMATICAL FRAKTUR SMALL Z
+ return rune(0x01d537), true
+ case "zgr":
+ // GREEK SMALL LETTER ZETA
+ return rune(0x03b6), true
+ case "zhcy":
+ // CYRILLIC SMALL LETTER ZHE
+ return rune(0x0436), true
+ case "zigrarr":
+ // RIGHTWARDS SQUIGGLE ARROW
+ return rune(0x21dd), true
+ case "zopf":
+ // MATHEMATICAL DOUBLE-STRUCK SMALL Z
+ return rune(0x01d56b), true
+ case "zscr":
+ // MATHEMATICAL SCRIPT SMALL Z
+ return rune(0x01d4cf), true
+ case "zwj":
+ // ZERO WIDTH JOINER
+ return rune(0x200d), true
+ case "zwnj":
+ // ZERO WIDTH NON-JOINER
+ return rune(0x200c), true
+ }
+ }
+ return -1, false
+}
+
+/*
+ ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
+*/
diff --git a/core/encoding/hxa/doc.odin b/core/encoding/hxa/doc.odin
index 16b94a243..230d6ea66 100644
--- a/core/encoding/hxa/doc.odin
+++ b/core/encoding/hxa/doc.odin
@@ -27,7 +27,7 @@
// Construction history, or BSP trees would make the format too large to serve its purpose.
// The facilities of the formats to store meta data should make the format flexible enough
// for most uses. Adding HxA support should be something anyone can do in a days work.
-
+//
// Structure:
// ----------
// HxA is designed to be extremely simple to parse, and is therefore based around conventions. It has
@@ -45,17 +45,17 @@
// of a number of named layers. All layers in the stack have the same number of elements. Each layer
// describes one property of the primitive. Each layer can have multiple channels and each layer can
// store data of a different type.
-
+//
// HaX stores 3 kinds of nodes
// - Pixel data.
// - Polygon geometry data.
// - Meta data only.
-
+//
// Pixel Nodes stores pixels in a layer stack. A layer may store things like Albedo, Roughness,
// Reflectance, Light maps, Masks, Normal maps, and Displacement. Layers use the channels of the
// layers to store things like color. The length of the layer stack is determined by the type and
// dimensions stored in the
-
+//
// Geometry data is stored in 3 separate layer stacks for: vertex data, corner data and face data. The
// vertex data stores things like verities, blend shapes, weight maps, and vertex colors. The first
// layer in a vertex stack has to be a 3 channel layer named "position" describing the base position
@@ -63,7 +63,7 @@
// for things like UV, normals, and adjacency. The first layer in a corner stack has to be a 1 channel
// integer layer named "index" describing the vertices used to form polygons. The last value in each
// polygon has a negative - 1 index to indicate the end of the polygon.
-
+//
// Example:
// A quad and a tri with the vertex index:
// [0, 1, 2, 3] [1, 4, 2]
@@ -72,7 +72,7 @@
// The face stack stores values per face. the length of the face stack has to match the number of
// negative values in the index layer in the corner stack. The face stack can be used to store things
// like material index.
-
+//
// Storage
// -------
// All data is stored in little endian byte order with no padding. The layout mirrors the structs
diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin
index ef7edc8b7..abe295530 100644
--- a/core/encoding/hxa/read.odin
+++ b/core/encoding/hxa/read.odin
@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
+ }
err = .Short_Read
return
}
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
+ r.filename, count, r.offset)
+ }
err = .Short_Read
return
}
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
+ fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data
return
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
+ fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data
return
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
if len(data) < size_of(Header) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
+ }
+ err = .Short_Read
return
}
context.allocator = allocator
header := cast(^Header)raw_data(data)
- assert(header.magic_number == MAGIC_NUMBER)
+ if (header.magic_number != MAGIC_NUMBER) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
+ }
+ err = .Invalid_Data
+ return
+ }
r := &Reader{
filename = filename,
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
node_count := 0
+ file.header = header^
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
+ fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data
return
diff --git a/core/encoding/hxa/write.odin b/core/encoding/hxa/write.odin
index e774018b2..5bb950e81 100644
--- a/core/encoding/hxa/write.odin
+++ b/core/encoding/hxa/write.odin
@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
- name_len := max(len(m.name), 255)
+ name_len := min(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)))
for layer in layers {
- name_len := max(len(layer.name), 255)
+ name_len := min(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
return
}
- write_value(w, &Header{
+ write_value(w, Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index adbcb95be..54fab44c6 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -8,18 +8,19 @@ import "core:strings"
import "core:io"
Marshal_Data_Error :: enum {
+ None,
Unsupported_Type,
}
-Marshal_Error :: union {
+Marshal_Error :: union #shared_nil {
Marshal_Data_Error,
io.Error,
}
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
- b := strings.make_builder(allocator)
- defer if err != .None {
- strings.destroy_builder(&b)
+ b := strings.builder_make(allocator)
+ defer if err != nil {
+ strings.builder_destroy(&b)
}
marshal_to_builder(&b, v) or_return
@@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
- return data, .None
+ return data, nil
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
@@ -48,7 +49,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
unreachable()
case runtime.Type_Info_Integer:
- buf: [21]byte
+ buf: [40]byte
u: u128
switch i in a {
case i8: u = u128(i)
@@ -285,8 +286,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
- case .Little: return ODIN_ENDIAN != "little"
- case .Big: return ODIN_ENDIAN != "big"
+ case .Little: return ODIN_ENDIAN != .Little
+ case .Big: return ODIN_ENDIAN != .Big
}
}
return false
diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin
index c682ec9bd..ed36ae33b 100644
--- a/core/encoding/json/parser.odin
+++ b/core/encoding/json/parser.odin
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
return parse_object(&p)
case .JSON5:
return parse_value(&p)
- case .MJSON:
+ case .SJSON:
#partial switch p.curr_token.kind {
case .Ident, .String:
return parse_object_body(&p, .EOF)
@@ -354,6 +354,12 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
w := copy(b, s[0:i])
+
+ if len(b) == 0 && allocator.data == nil {
+ // `unmarshal_count_array` calls us with a nil allocator
+ return string(b[:w]), nil
+ }
+
loop: for i < len(s) {
c := s[i]
switch {
diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin
index 534d20311..468774aa9 100644
--- a/core/encoding/json/types.odin
+++ b/core/encoding/json/types.odin
@@ -33,8 +33,9 @@ package json
Specification :: enum {
JSON,
JSON5, // https://json5.org/
- MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
- Bitsquid = MJSON,
+ SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
+ Bitsquid = SJSON,
+ MJSON = SJSON,
}
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index 23518d8d0..2ff268a21 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -52,11 +52,11 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
if p.spec == .MJSON {
#partial switch p.curr_token.kind {
case .Ident, .String:
- return unmarsal_object(&p, data, .EOF)
+ return unmarshal_object(&p, data, .EOF)
}
}
- return unmarsal_value(&p, data)
+ return unmarshal_value(&p, data)
}
@@ -148,7 +148,7 @@ assign_float :: proc(val: any, f: $T) -> bool {
@(private)
-unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
+unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
val := val
switch dst in &val {
case string:
@@ -198,7 +198,7 @@ unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Inf
@(private)
-unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
token := p.curr_token
@@ -209,7 +209,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
variant := u.variants[0]
v.id = variant.id
ti = reflect.type_info_base(variant)
- if !(u.maybe && reflect.is_pointer(variant)) {
+ if !reflect.is_pointer_internally(variant) {
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
assign_int(tag, 1)
}
@@ -222,6 +222,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
advance_token(p)
return
case .False, .True:
+ advance_token(p)
if assign_bool(v, token.kind == .True) {
return
}
@@ -256,7 +257,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Ident:
advance_token(p)
if p.spec == .MJSON {
- if unmarsal_string(p, any{v.data, ti.id}, token.text, ti) {
+ if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
return nil
}
}
@@ -265,7 +266,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .String:
advance_token(p)
str := unquote_string(token, p.spec, p.allocator) or_return
- if unmarsal_string(p, any{v.data, ti.id}, str, ti) {
+ if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
return nil
}
delete(str, p.allocator)
@@ -273,10 +274,10 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Open_Brace:
- return unmarsal_object(p, v, .Close_Brace)
+ return unmarshal_object(p, v, .Close_Brace)
case .Open_Bracket:
- return unmarsal_array(p, v)
+ return unmarshal_array(p, v)
case:
if p.spec != .JSON {
@@ -311,16 +312,16 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
@(private)
-unmarsal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
+unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
prev := p.curr_token
err := expect_token(p, kind)
- assert(err == nil, "unmarsal_expect_token")
+ assert(err == nil, "unmarshal_expect_token")
return prev
}
@(private)
-unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
+unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace {
@@ -341,7 +342,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
key, _ := parse_object_key(p, p.allocator)
defer delete(key, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
fields := reflect.struct_fields_zipped(ti.id)
@@ -377,7 +378,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
field_ptr := rawptr(uintptr(v.data) + offset)
field := any{field_ptr, type.id}
- unmarsal_value(p, field) or_return
+ unmarshal_value(p, field) or_return
if parse_comma(p) {
break struct_loop
@@ -406,11 +407,11 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
map_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
mem.zero_slice(elem_backing)
- if err := unmarsal_value(p, map_backing_value); err != nil {
+ if err := unmarshal_value(p, map_backing_value); err != nil {
delete(key, p.allocator)
return err
}
@@ -442,7 +443,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
enumerated_array_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
defer delete(key, p.allocator)
index := -1
@@ -459,7 +460,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
index_ptr := rawptr(uintptr(v.data) + uintptr(index*t.elem_size))
index_any := any{index_ptr, t.elem.id}
- unmarsal_value(p, index_any) or_return
+ unmarshal_value(p, index_any) or_return
if parse_comma(p) {
break enumerated_array_loop
@@ -479,10 +480,10 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
@(private)
-unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
+unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
p_backup := p^
p.allocator = mem.nil_allocator()
- unmarsal_expect_token(p, .Open_Bracket)
+ unmarshal_expect_token(p, .Open_Bracket)
array_length_loop: for p.curr_token.kind != .Close_Bracket {
_, _ = parse_value(p)
length += 1
@@ -496,9 +497,9 @@ unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
}
@(private)
-unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr) -> Unmarshal_Error {
- unmarsal_expect_token(p, .Open_Bracket)
+ unmarshal_expect_token(p, .Open_Bracket)
for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
assert(idx < length)
@@ -506,14 +507,14 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
elem := any{elem_ptr, elem.id}
- unmarsal_value(p, elem) or_return
+ unmarshal_value(p, elem) or_return
if parse_comma(p) {
break
}
}
- unmarsal_expect_token(p, .Close_Bracket)
+ unmarshal_expect_token(p, .Close_Bracket)
return nil
@@ -523,7 +524,7 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
ti := reflect.type_info_base(type_info_of(v.id))
- length := unmarsal_count_array(p)
+ length := unmarshal_count_array(p)
#partial switch t in ti.variant {
case reflect.Type_Info_Slice:
@@ -577,4 +578,4 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
}
return UNSUPPORTED_TYPE
-}
\ No newline at end of file
+}
diff --git a/core/encoding/varint/doc.odin b/core/encoding/varint/doc.odin
new file mode 100644
index 000000000..5e4708a59
--- /dev/null
+++ b/core/encoding/varint/doc.odin
@@ -0,0 +1,28 @@
+/*
+ Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
+
+ Author of this Odin package: Jeroen van Rijn
+
+ Example:
+ ```odin
+ import "core:encoding/varint"
+ import "core:fmt"
+
+ main :: proc() {
+ buf: [varint.LEB128_MAX_BYTES]u8
+
+ value := u128(42)
+
+ encode_size, encode_err := varint.encode_uleb128(buf[:], value)
+ assert(encode_size == 1 && encode_err == .None)
+
+ fmt.printf("Encoded as %v\n", buf[:encode_size])
+ decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:])
+
+ assert(decoded_val == value && decode_size == encode_size && decode_err == .None)
+ fmt.printf("Decoded as %v, using %v byte%v\n", decoded_val, decode_size, "" if decode_size == 1 else "s")
+ }
+ ```
+
+*/
+package varint
\ No newline at end of file
diff --git a/core/encoding/varint/leb128.odin b/core/encoding/varint/leb128.odin
new file mode 100644
index 000000000..1cdbb81b0
--- /dev/null
+++ b/core/encoding/varint/leb128.odin
@@ -0,0 +1,163 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+// package varint implements variable length integer encoding and decoding using
+// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
+package varint
+
+// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
+// Instead we'll set limits on the values we'll encode/decode
+// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
+LEB128_MAX_BYTES :: 19
+
+Error :: enum {
+ None = 0,
+ Buffer_Too_Small = 1,
+ Value_Too_Large = 2,
+}
+
+// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_uleb128_buffer :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
+ if len(buf) == 0 {
+ return 0, 0, .Buffer_Too_Small
+ }
+
+ for v in buf {
+ val, size, err = decode_uleb128_byte(v, size, val)
+ if err != .Buffer_Too_Small {
+ return
+ }
+ }
+
+ if err == .Buffer_Too_Small {
+ val, size = 0, 0
+ }
+ return
+}
+
+// Decodes an unsigned LEB128 integer into value a byte at a time.
+// Returns `.None` when decoded properly, `.Value_Too_Large` when they value
+// exceeds the limits of a u128, and `.Buffer_Too_Small` when it's not yet fully decoded.
+decode_uleb128_byte :: proc(input: u8, offset: int, accumulator: u128) -> (val: u128, size: int, err: Error) {
+ size = offset + 1
+
+ // 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
+ if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0b0000_0011 {
+ return 0, 0, .Value_Too_Large
+ }
+
+ val = accumulator | u128(input & 0x7f) << uint(offset * 7)
+
+ if input < 128 {
+ // We're done
+ return
+ }
+
+ // If the buffer runs out before the number ends, return an error.
+ return val, size, .Buffer_Too_Small
+}
+decode_uleb128 :: proc {decode_uleb128_buffer, decode_uleb128_byte}
+
+// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_ileb128_buffer :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
+ if len(buf) == 0 {
+ return 0, 0, .Buffer_Too_Small
+ }
+
+ for v in buf {
+ val, size, err = decode_ileb128_byte(v, size, val)
+ if err != .Buffer_Too_Small {
+ return
+ }
+ }
+
+ if err == .Buffer_Too_Small {
+ val, size = 0, 0
+ }
+ return
+}
+
+// Decode a a signed LEB128 integer into value and number of bytes used, one byte at a time.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_ileb128_byte :: proc(input: u8, offset: int, accumulator: i128) -> (val: i128, size: int, err: Error) {
+ size = offset + 1
+ shift := uint(offset * 7)
+
+ // 18 * 7 bits = 126, which including sign means we can have a 19th byte.
+ if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0x7f {
+ return 0, 0, .Value_Too_Large
+ }
+
+ val = accumulator | i128(input & 0x7f) << shift
+
+ if input < 128 {
+ if input & 0x40 == 0x40 {
+ val |= max(i128) << (shift + 7)
+ }
+ return val, size, .None
+ }
+ return val, size, .Buffer_Too_Small
+}
+decode_ileb128 :: proc{decode_ileb128_buffer, decode_ileb128_byte}
+
+// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
+ val := val
+
+ for {
+ size += 1
+
+ if size > len(buf) {
+ return 0, .Buffer_Too_Small
+ }
+
+ low := val & 0x7f
+ val >>= 7
+
+ if val > 0 {
+ low |= 0x80 // more bytes to follow
+ }
+ buf[size - 1] = u8(low)
+
+ if val == 0 { break }
+ }
+ return
+}
+
+// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
+ SIGN_MASK :: i128(1) << 121 // sign extend mask
+
+ val, more := val, true
+
+ for more {
+ size += 1
+
+ if size > len(buf) {
+ return 0, .Buffer_Too_Small
+ }
+
+ low := val & 0x7f
+ val >>= 7
+
+ low = (low ~ SIGN_MASK) - SIGN_MASK
+
+ if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
+ more = false
+ } else {
+ low |= 0x80
+ }
+
+ buf[size - 1] = u8(low)
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/encoding/xml/debug_print.odin b/core/encoding/xml/debug_print.odin
new file mode 100644
index 000000000..e9a1cb160
--- /dev/null
+++ b/core/encoding/xml/debug_print.odin
@@ -0,0 +1,86 @@
+/*
+ An XML 1.0 / 1.1 parser
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+package xml
+
+import "core:io"
+import "core:fmt"
+
+/*
+ Just for debug purposes.
+*/
+print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
+ if doc == nil { return }
+ using fmt
+
+ written += wprintf(writer, "[XML Prolog]\n")
+
+ for attr in doc.prologue {
+ written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
+ }
+
+ written += wprintf(writer, "[Encoding] %v\n", doc.encoding)
+
+ if len(doc.doctype.ident) > 0 {
+ written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
+
+ if len(doc.doctype.rest) > 0 {
+ wprintf(writer, "\t%v\n", doc.doctype.rest)
+ }
+ }
+
+ for comment in doc.comments {
+ written += wprintf(writer, "[Pre-root comment] %v\n", comment)
+ }
+
+ if len(doc.elements) > 0 {
+ wprintln(writer, " --- ")
+ print_element(writer, doc, 0)
+ wprintln(writer, " --- ")
+ }
+
+ return written, .None
+}
+
+print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
+ using fmt
+
+ tab :: proc(writer: io.Writer, indent: int) {
+ for _ in 0..=indent {
+ wprintf(writer, "\t")
+ }
+ }
+
+ tab(writer, indent)
+
+ element := doc.elements[element_id]
+
+ if element.kind == .Element {
+ wprintf(writer, "<%v>\n", element.ident)
+ if len(element.value) > 0 {
+ tab(writer, indent + 1)
+ wprintf(writer, "[Value] %v\n", element.value)
+ }
+
+ for attr in element.attribs {
+ tab(writer, indent + 1)
+ wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
+ }
+
+ for child in element.children {
+ print_element(writer, doc, child, indent + 1)
+ }
+ } else if element.kind == .Comment {
+ wprintf(writer, "[COMMENT] %v\n", element.value)
+ }
+
+ return written, .None
+}
\ No newline at end of file
diff --git a/core/encoding/xml/example/xml_example.odin b/core/encoding/xml/example/xml_example.odin
new file mode 100644
index 000000000..887b40764
--- /dev/null
+++ b/core/encoding/xml/example/xml_example.odin
@@ -0,0 +1,112 @@
+package xml_example
+
+import "core:encoding/xml"
+import "core:mem"
+import "core:fmt"
+import "core:time"
+import "core:strings"
+import "core:hash"
+
+N :: 1
+
+example :: proc() {
+ using fmt
+
+ docs: [N]^xml.Document
+ errs: [N]xml.Error
+ times: [N]time.Duration
+
+ defer for round in 0..` tag.")
+ return
+ }
+
+ printf("Found `` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count)
+
+ crc32 := doc_hash(docs[0])
+ printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32)
+
+ for round in 0.. (crc32: u32) {
+ buf: strings.Builder
+ defer strings.builder_destroy(&buf)
+ w := strings.to_writer(&buf)
+
+ xml.print(w, doc)
+ tree := strings.to_string(buf)
+ if print { fmt.println(tree) }
+ return hash.crc32(transmute([]u8)tree)
+}
+
+main :: proc() {
+ using fmt
+
+ track: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&track, context.allocator)
+ context.allocator = mem.tracking_allocator(&track)
+
+ example()
+
+ if len(track.allocation_map) > 0 {
+ println()
+ for _, v in track.allocation_map {
+ printf("%v Leaked %v bytes.\n", v.location, v.size)
+ }
+ }
+ println("Done and cleaned up!")
+}
\ No newline at end of file
diff --git a/core/encoding/xml/helpers.odin b/core/encoding/xml/helpers.odin
new file mode 100644
index 000000000..48f058334
--- /dev/null
+++ b/core/encoding/xml/helpers.odin
@@ -0,0 +1,45 @@
+/*
+ An XML 1.0 / 1.1 parser
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ This file contains helper functions.
+*/
+package xml
+
+// Find parent's nth child with a given ident.
+find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
+ tag := doc.elements[parent_id]
+
+ count := 0
+ for child_id in tag.children {
+ child := doc.elements[child_id]
+ /*
+ Skip commments. They have no name.
+ */
+ if child.kind != .Element { continue }
+
+ /*
+ If the ident matches and it's the nth such child, return it.
+ */
+ if child.ident == ident {
+ if count == nth { return child_id, true }
+ count += 1
+ }
+ }
+ return 0, false
+}
+
+// Find an attribute by key.
+find_attribute_val_by_key :: proc(doc: ^Document, parent_id: Element_ID, key: string) -> (val: string, found: bool) {
+ tag := doc.elements[parent_id]
+
+ for attr in tag.attribs {
+ /*
+ If the ident matches, we're done. There can only ever be one attribute with the same name.
+ */
+ if attr.key == key { return attr.val, true }
+ }
+ return "", false
+}
\ No newline at end of file
diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin
new file mode 100644
index 000000000..d225c5d90
--- /dev/null
+++ b/core/encoding/xml/tokenizer.odin
@@ -0,0 +1,436 @@
+/*
+ An XML 1.0 / 1.1 parser
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+package xml
+
+import "core:fmt"
+import "core:unicode"
+import "core:unicode/utf8"
+
+Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
+
+Token :: struct {
+ kind: Token_Kind,
+ text: string,
+ pos: Pos,
+}
+
+Pos :: struct {
+ file: string,
+ offset: int, // starting at 0
+ line: int, // starting at 1
+ column: int, // starting at 1
+}
+
+Token_Kind :: enum {
+ Invalid,
+
+ Ident,
+ Literal,
+ Rune,
+ String,
+
+ Double_Quote, // "
+ Single_Quote, // '
+ Colon, // :
+
+ Eq, // =
+ Lt, // <
+ Gt, // >
+ Exclaim, // !
+ Question, // ?
+ Hash, // #
+ Slash, // /
+ Dash, // -
+
+ Open_Bracket, // [
+ Close_Bracket, // ]
+
+ EOF,
+}
+
+CDATA_START :: ""
+
+COMMENT_START :: ""
+
+Tokenizer :: struct {
+ // Immutable data
+ path: string,
+ src: string,
+ err: Error_Handler,
+
+ // Tokenizing state
+ ch: rune,
+ offset: int,
+ read_offset: int,
+ line_offset: int,
+ line_count: int,
+
+ // Mutable data
+ error_count: int,
+}
+
+init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) {
+ t.src = src
+ t.err = err
+ t.ch = ' '
+ t.offset = 0
+ t.read_offset = 0
+ t.line_offset = 0
+ t.line_count = len(src) > 0 ? 1 : 0
+ t.error_count = 0
+ t.path = path
+
+ advance_rune(t)
+ if t.ch == utf8.RUNE_BOM {
+ advance_rune(t)
+ }
+}
+
+@(private)
+offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
+ line := t.line_count
+ column := offset - t.line_offset + 1
+
+ return Pos {
+ file = t.path,
+ offset = offset,
+ line = line,
+ column = column,
+ }
+}
+
+default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
+ fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
+ fmt.eprintf(msg, ..args)
+ fmt.eprintf("\n")
+}
+
+error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
+ pos := offset_to_pos(t, offset)
+ if t.err != nil {
+ t.err(pos, msg, ..args)
+ }
+ t.error_count += 1
+}
+
+@(optimization_mode="speed")
+advance_rune :: proc(using t: ^Tokenizer) {
+ #no_bounds_check {
+ /*
+ Already bounds-checked here.
+ */
+ if read_offset < len(src) {
+ offset = read_offset
+ if ch == '\n' {
+ line_offset = offset
+ line_count += 1
+ }
+ r, w := rune(src[read_offset]), 1
+ switch {
+ case r == 0:
+ error(t, t.offset, "illegal character NUL")
+ case r >= utf8.RUNE_SELF:
+ r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:])
+ if r == utf8.RUNE_ERROR && w == 1 {
+ error(t, t.offset, "illegal UTF-8 encoding")
+ } else if r == utf8.RUNE_BOM && offset > 0 {
+ error(t, t.offset, "illegal byte order mark")
+ }
+ }
+ read_offset += w
+ ch = r
+ } else {
+ offset = len(src)
+ if ch == '\n' {
+ line_offset = offset
+ line_count += 1
+ }
+ ch = -1
+ }
+ }
+}
+
+peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
+ if t.read_offset+offset < len(t.src) {
+ #no_bounds_check return t.src[t.read_offset+offset]
+ }
+ return 0
+}
+
+@(optimization_mode="speed")
+skip_whitespace :: proc(t: ^Tokenizer) {
+ for {
+ switch t.ch {
+ case ' ', '\t', '\r', '\n':
+ advance_rune(t)
+ case:
+ return
+ }
+ }
+}
+
+@(optimization_mode="speed")
+is_letter :: proc(r: rune) -> bool {
+ if r < utf8.RUNE_SELF {
+ switch r {
+ case '_':
+ return true
+ case 'A'..='Z', 'a'..='z':
+ return true
+ }
+ }
+ return unicode.is_letter(r)
+}
+
+is_valid_identifier_rune :: proc(r: rune) -> bool {
+ if r < utf8.RUNE_SELF {
+ switch r {
+ case '_', '-', ':': return true
+ case 'A'..='Z', 'a'..='z': return true
+ case '0'..='9': return true
+ case -1: return false
+ }
+ }
+
+ if unicode.is_letter(r) || unicode.is_digit(r) {
+ return true
+ }
+ return false
+}
+
+scan_identifier :: proc(t: ^Tokenizer) -> string {
+ offset := t.offset
+ namespaced := false
+
+ for is_valid_identifier_rune(t.ch) {
+ advance_rune(t)
+ if t.ch == ':' {
+ /*
+ A namespaced attr can have at most two parts, `namespace:ident`.
+ */
+ if namespaced {
+ break
+ }
+ namespaced = true
+ }
+ }
+ return string(t.src[offset : t.offset])
+}
+
+/*
+ A comment ends when we see -->, preceded by a character that's not a dash.
+ "For compatibility, the string "--" (double-hyphen) must not occur within comments."
+
+ See: https://www.w3.org/TR/2006/REC-xml11-20060816/#dt-comment
+
+ Thanks to the length (4) of the comment start, we also have enough lookback,
+ and the peek at the next byte asserts that there's at least one more character
+ that's a `>`.
+*/
+scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
+ offset := t.offset
+
+ for {
+ advance_rune(t)
+ ch := t.ch
+
+ if ch < 0 {
+ error(t, offset, "[parse] Comment was not terminated\n")
+ return "", .Unclosed_Comment
+ }
+
+ if string(t.src[t.offset - 1:][:2]) == "--" {
+ if peek_byte(t) == '>' {
+ break
+ } else {
+ error(t, t.offset - 1, "Invalid -- sequence in comment.\n")
+ return "", .Invalid_Sequence_In_Comment
+ }
+ }
+ }
+
+ expect(t, .Dash)
+ expect(t, .Gt)
+
+ return string(t.src[offset : t.offset - 1]), .None
+}
+
+/*
+ Skip CDATA
+*/
+skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
+ if t.read_offset + len(CDATA_START) >= len(t.src) {
+ /*
+ Can't be the start of a CDATA tag.
+ */
+ return .None
+ }
+
+ if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
+ t.read_offset += len(CDATA_START)
+ offset := t.offset
+
+ cdata_scan: for {
+ advance_rune(t)
+ if t.ch < 0 {
+ error(t, offset, "[scan_string] CDATA was not terminated\n")
+ return .Premature_EOF
+ }
+
+ /*
+ Scan until the end of a CDATA tag.
+ */
+ if t.read_offset + len(CDATA_END) < len(t.src) {
+ if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
+ t.read_offset += len(CDATA_END)
+ break cdata_scan
+ }
+ }
+ }
+ }
+ return
+}
+
+@(optimization_mode="speed")
+scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
+ err = .None
+
+ loop: for {
+ ch := t.ch
+
+ switch ch {
+ case -1:
+ error(t, t.offset, "[scan_string] Premature end of file.\n")
+ return "", .Premature_EOF
+
+ case '<':
+ if peek_byte(t) == '!' {
+ if peek_byte(t, 1) == '[' {
+ /*
+ Might be the start of a CDATA tag.
+ */
+ skip_cdata(t) or_return
+ } else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' {
+ /*
+ Comment start. Eat comment.
+ */
+ t.read_offset += 3
+ _ = scan_comment(t) or_return
+ }
+ }
+
+ case '\n':
+ if !multiline {
+ error(t, offset, string(t.src[offset : t.offset]))
+ error(t, offset, "[scan_string] Not terminated\n")
+ err = .Invalid_Tag_Value
+ break loop
+ }
+ }
+
+ if t.ch == close {
+ /*
+ If it's not a CDATA or comment, it's the end of this body.
+ */
+ break loop
+ }
+ advance_rune(t)
+ }
+
+ /*
+ Strip trailing whitespace.
+ */
+ lit := string(t.src[offset : t.offset])
+
+ end := len(lit)
+ eat: for ; end > 0; end -= 1 {
+ ch := lit[end - 1]
+ switch ch {
+ case ' ', '\t', '\r', '\n':
+ case:
+ break eat
+ }
+ }
+ lit = lit[:end]
+
+ if consume_close {
+ advance_rune(t)
+ }
+
+ /*
+ TODO: Handle decoding escape characters and unboxing CDATA.
+ */
+
+ return lit, err
+}
+
+peek :: proc(t: ^Tokenizer) -> (token: Token) {
+ old := t^
+ token = scan(t)
+ t^ = old
+ return token
+}
+
+scan :: proc(t: ^Tokenizer) -> Token {
+ skip_whitespace(t)
+
+ offset := t.offset
+
+ kind: Token_Kind
+ err: Error
+ lit: string
+ pos := offset_to_pos(t, offset)
+
+ switch ch := t.ch; true {
+ case is_letter(ch):
+ lit = scan_identifier(t)
+ kind = .Ident
+
+ case:
+ advance_rune(t)
+ switch ch {
+ case -1:
+ kind = .EOF
+
+ case '<': kind = .Lt
+ case '>': kind = .Gt
+ case '!': kind = .Exclaim
+ case '?': kind = .Question
+ case '=': kind = .Eq
+ case '#': kind = .Hash
+ case '/': kind = .Slash
+ case '-': kind = .Dash
+ case ':': kind = .Colon
+
+ case '"', '\'':
+ kind = .Invalid
+
+ lit, err = scan_string(t, t.offset, ch, true, false)
+ if err == .None {
+ kind = .String
+ }
+
+ case '\n':
+ lit = "\n"
+
+ case:
+ kind = .Invalid
+ }
+ }
+
+ if kind != .String && lit == "" {
+ lit = string(t.src[offset : t.offset])
+ }
+ return Token{kind, lit, pos}
+}
\ No newline at end of file
diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin
new file mode 100644
index 000000000..b77ae97b3
--- /dev/null
+++ b/core/encoding/xml/xml_reader.odin
@@ -0,0 +1,713 @@
+/*
+ An XML 1.0 / 1.1 parser
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ A from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
+
+ Features:
+ - Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
+ - Simple to understand and use. Small.
+
+ Caveats:
+ - We do NOT support HTML in this package, as that may or may not be valid XML.
+ If it works, great. If it doesn't, that's not considered a bug.
+
+ - We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
+ - <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
+
+ MAYBE:
+ - XML writer?
+ - Serialize/deserialize Odin types?
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+package xml
+// An XML 1.0 / 1.1 parser
+
+import "core:bytes"
+import "core:encoding/entity"
+import "core:intrinsics"
+import "core:mem"
+import "core:os"
+import "core:strings"
+
+likely :: intrinsics.expect
+
+DEFAULT_OPTIONS :: Options{
+ flags = {.Ignore_Unsupported},
+ expected_doctype = "",
+}
+
+Option_Flag :: enum {
+ /*
+ If the caller says that input may be modified, we can perform in-situ parsing.
+ If this flag isn't provided, the XML parser first duplicates the input so that it can.
+ */
+ Input_May_Be_Modified,
+
+ /*
+ Document MUST start with ` (doc: ^Document, err: Error) {
+ data := data
+ context.allocator = allocator
+
+ opts := validate_options(options) or_return
+
+ /*
+ If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
+ */
+ if .Input_May_Be_Modified not_in opts.flags {
+ data = bytes.clone(data)
+ }
+
+ t := &Tokenizer{}
+ init(t, string(data), path, error_handler)
+
+ doc = new(Document)
+ doc.allocator = allocator
+ doc.tokenizer = t
+ doc.input = data
+
+ doc.elements = make([dynamic]Element, 1024, 1024, allocator)
+
+ // strings.intern_init(&doc.intern, allocator, allocator)
+
+ err = .Unexpected_Token
+ element, parent: Element_ID
+
+ tag_is_open := false
+ first_element := true
+ open: Token
+
+ /*
+ If a DOCTYPE is present, the root tag has to match.
+ If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
+ */
+ expected_doctype := options.expected_doctype
+
+ loop: for {
+ skip_whitespace(t)
+ // NOTE(Jeroen): This is faster as a switch.
+ switch t.ch {
+ case '<':
+ /*
+ Consume peeked `<`
+ */
+ advance_rune(t)
+
+ open = scan(t)
+ // NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
+ if likely(open.kind, Token_Kind.Ident) == .Ident {
+ /*
+ e.g. 0 && expected_doctype != open.text {
+ error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
+ return doc, .Invalid_DocType
+ }
+ }
+
+ /*
+ One of these should follow:
+ - `>`, which means we've just opened this tag and expect a later element to close it.
+ - `/>`, which means this is an 'empty' or self-closing tag.
+ */
+ end_token := scan(t)
+ #partial switch end_token.kind {
+ case .Gt:
+ /*
+ We're now the new parent.
+ */
+ parent = element
+
+ case .Slash:
+ /*
+ Empty tag. Close it.
+ */
+ expect(t, .Gt) or_return
+ parent = doc.elements[element].parent
+ element = parent
+ tag_is_open = false
+
+ case:
+ error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
+ return
+ }
+
+ } else if open.kind == .Slash {
+ /*
+ Close tag.
+ */
+ ident := expect(t, .Ident) or_return
+ _ = expect(t, .Gt) or_return
+
+ if doc.elements[element].ident != ident.text {
+ error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text)
+ return doc, .Mismatched_Closing_Tag
+ }
+ parent = doc.elements[element].parent
+ element = parent
+ tag_is_open = false
+
+ } else if open.kind == .Exclaim {
+ /*
+ 0 {
+ return doc, .Too_Many_DocTypes
+ }
+ if doc.element_count > 0 {
+ return doc, .DocType_Must_Preceed_Elements
+ }
+ parse_doctype(doc) or_return
+
+ if len(expected_doctype) > 0 && expected_doctype != doc.doctype.ident {
+ error(t, t.offset, "Invalid DOCTYPE. Expected: %v, got: %v\n", expected_doctype, doc.doctype.ident)
+ return doc, .Invalid_DocType
+ }
+ expected_doctype = doc.doctype.ident
+
+ case:
+ if .Error_on_Unsupported in opts.flags {
+ error(t, t.offset, "Unhandled: .
+ The grammar does not allow a comment to end in --->
+ */
+ expect(t, .Dash)
+ comment := scan_comment(t) or_return
+
+ if .Intern_Comments in opts.flags {
+ if len(doc.elements) == 0 {
+ append(&doc.comments, comment)
+ } else {
+ el := new_element(doc)
+ doc.elements[el].parent = element
+ doc.elements[el].kind = .Comment
+ doc.elements[el].value = comment
+ append(&doc.elements[element].children, el)
+ }
+ }
+
+ case:
+ error(t, t.offset, "Invalid Token after 0 {
+ /*
+ We've already seen a prologue.
+ */
+ return doc, .Too_Many_Prologs
+ } else {
+ /*
+ Could be ` (doc: ^Document, err: Error) {
+ _data := transmute([]u8)data
+
+ return parse_bytes(_data, options, path, error_handler, allocator)
+}
+
+parse :: proc { parse_string, parse_bytes }
+
+// Load an XML file
+load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
+ context.allocator = allocator
+ options := options
+
+ data, data_ok := os.read_entire_file(filename)
+ if !data_ok { return {}, .File_Error }
+
+ options.flags += { .Input_May_Be_Modified }
+
+ return parse_bytes(data, options, filename, error_handler, allocator)
+}
+
+destroy :: proc(doc: ^Document) {
+ if doc == nil { return }
+
+ for el in doc.elements {
+ delete(el.attribs)
+ delete(el.children)
+ }
+ delete(doc.elements)
+
+ delete(doc.prologue)
+ delete(doc.comments)
+ delete(doc.input)
+
+ for s in doc.strings_to_free {
+ delete(s)
+ }
+ delete(doc.strings_to_free)
+
+ free(doc)
+}
+
+/*
+ Helpers.
+*/
+
+validate_options :: proc(options: Options) -> (validated: Options, err: Error) {
+ validated = options
+
+ if .Error_on_Unsupported in validated.flags && .Ignore_Unsupported in validated.flags {
+ return options, .Conflicting_Options
+ }
+ return validated, .None
+}
+
+expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
+ tok = scan(t)
+ if tok.kind == kind { return tok, .None }
+
+ error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind)
+ return tok, .Unexpected_Token
+}
+
+parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: Error) {
+ assert(doc != nil)
+ context.allocator = doc.allocator
+ t := doc.tokenizer
+
+ key := expect(t, .Ident) or_return
+ offset = t.offset - len(key.text)
+
+ _ = expect(t, .Eq) or_return
+ value := expect(t, .String) or_return
+
+ attr.key = key.text
+ attr.val = value.text
+
+ err = .None
+ return
+}
+
+check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attribute, offset: int) -> (err: Error) {
+ for a in attribs {
+ if attr.key == a.key {
+ error(t, offset, "Duplicate attribute: %v\n", attr.key)
+ return .Duplicate_Attribute
+ }
+ }
+ return .None
+}
+
+parse_attributes :: proc(doc: ^Document, attribs: ^Attributes) -> (err: Error) {
+ assert(doc != nil)
+ context.allocator = doc.allocator
+ t := doc.tokenizer
+
+ for peek(t).kind == .Ident {
+ attr, offset := parse_attribute(doc) or_return
+ check_duplicate_attributes(t, attribs^, attr, offset) or_return
+ append(attribs, attr)
+ }
+ skip_whitespace(t)
+ return .None
+}
+
+parse_prologue :: proc(doc: ^Document) -> (err: Error) {
+ assert(doc != nil)
+ context.allocator = doc.allocator
+ t := doc.tokenizer
+
+ offset := t.offset
+ parse_attributes(doc, &doc.prologue) or_return
+
+ for attr in doc.prologue {
+ switch attr.key {
+ case "version":
+ switch attr.val {
+ case "1.0", "1.1":
+ case:
+ error(t, offset, "[parse_prologue] Warning: Unhandled XML version: %v\n", attr.val)
+ }
+
+ case "encoding":
+ switch strings.to_lower(attr.val, context.temp_allocator) {
+ case "utf-8", "utf8":
+ doc.encoding = .UTF_8
+
+ case "latin-1", "latin1", "iso-8859-1":
+ doc.encoding = .LATIN_1
+
+ case:
+ /*
+ Unrecognized encoding, assume UTF-8.
+ */
+ error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
+ }
+
+ case:
+ // Ignored.
+ }
+ }
+
+ _ = expect(t, .Question) or_return
+ _ = expect(t, .Gt) or_return
+
+ return .None
+}
+
+skip_element :: proc(t: ^Tokenizer) -> (err: Error) {
+ close := 1
+
+ loop: for {
+ tok := scan(t)
+ #partial switch tok.kind {
+ case .EOF:
+ error(t, t.offset, "[skip_element] Premature EOF\n")
+ return .Premature_EOF
+
+ case .Lt:
+ close += 1
+
+ case .Gt:
+ close -= 1
+ if close == 0 {
+ break loop
+ }
+
+ case:
+
+ }
+ }
+ return .None
+}
+
+parse_doctype :: proc(doc: ^Document) -> (err: Error) {
+ /*
+
+
+
+ ]>
+ */
+ assert(doc != nil)
+ context.allocator = doc.allocator
+ t := doc.tokenizer
+
+ tok := expect(t, .Ident) or_return
+ doc.doctype.ident = tok.text
+
+ skip_whitespace(t)
+ offset := t.offset
+ skip_element(t) or_return
+
+ /*
+ -1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
+ */
+ doc.doctype.rest = string(t.src[offset : t.offset - 1])
+ return .None
+}
+
+Element_ID :: u32
+
+new_element :: proc(doc: ^Document) -> (id: Element_ID) {
+ element_space := len(doc.elements)
+
+ // Need to resize
+ if int(doc.element_count) + 1 > element_space {
+ if element_space < 65536 {
+ element_space *= 2
+ } else {
+ element_space += 65536
+ }
+ resize(&doc.elements, element_space)
+ }
+
+ cur := doc.element_count
+ doc.element_count += 1
+
+ return cur
+}
\ No newline at end of file
diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin
index 5984da950..668fc9bc6 100644
--- a/core/fmt/doc.odin
+++ b/core/fmt/doc.odin
@@ -64,6 +64,7 @@ If not present, the width is whatever is necessary to represent the value.
Precision is specified after the (optional) width followed by a period followed by a decimal number.
If no period is present, a default precision is used.
A period with no following number specifies a precision of 0.
+
Examples:
%f default width, default precision
%8f width 8, default precision
@@ -84,7 +85,6 @@ Other flags:
add leading 0z for dozenal (%#z)
add leading 0x or 0X for hexadecimal (%#x or %#X)
remove leading 0x for %p (%#p)
-
' ' (space) leave a space for elided sign in numbers (% d)
0 pad with leading zeros rather than spaces
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 06cb6703f..4db140afa 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -11,6 +11,7 @@ import "core:time"
import "core:unicode/utf8"
import "core:intrinsics"
+// Internal data structure that stores the required information for formatted printing
Info :: struct {
minus: bool,
plus: bool,
@@ -27,10 +28,17 @@ Info :: struct {
reordered: bool,
good_arg_index: bool,
ignore_user_formatters: bool,
+ in_bad: bool,
writer: io.Writer,
arg: any, // Temporary
+ indirection_level: int,
record_level: int,
+
+ optional_len: Maybe(int),
+ use_nul_termination: bool,
+
+ n: int, // bytes written
}
// Custom formatter signature. It returns true if the formatting was successful and false when it could not be done
@@ -46,9 +54,13 @@ 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
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
_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.
register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error {
if _user_formatters == nil {
return .No_User_Formatter
@@ -61,64 +73,73 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
}
-// aprint* procedures return a string that was allocated with the current context
+// aprint procedure return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
- strings.init_builder(&str)
+ 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
aprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
- strings.init_builder(&str)
+ 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
aprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
- strings.init_builder(&str)
+ strings.builder_init(&str)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
}
-// tprint* procedures return a string that was allocated with the current context's temporary allocator
+// tprint procedure return a string that was allocated with the current context's temporary allocator
tprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
- strings.init_builder(&str, context.temp_allocator)
+ 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
tprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
- strings.init_builder(&str, context.temp_allocator)
+ 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
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
- strings.init_builder(&str, context.temp_allocator)
+ 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
+// bprint procedures return a string using a buffer from an array
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
- sb := strings.builder_from_slice(buf[0:len(buf)])
+ 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
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
- sb := strings.builder_from_slice(buf[0:len(buf)])
+ 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
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
- sb := strings.builder_from_slice(buf[0:len(buf)])
+ sb := strings.builder_from_bytes(buf[0:len(buf)])
return sbprintf(&sb, fmt, ..args)
}
+// formatted assert
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
if !condition {
p := context.assertion_failure_proc
@@ -131,6 +152,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
return condition
}
+// formatted panic
panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
if p == nil {
@@ -142,24 +164,26 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
-
-
+// sbprint formats using the default print settings and writes to buf
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
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
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
wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -179,49 +203,42 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
// so I am going to keep the same behaviour as `*println` for `*print`
- size0 := io.size(auto_cast w)
-
for _, i in args {
if i > 0 {
- io.write_string(fi.writer, sep)
+ io.write_string(fi.writer, sep, &fi.n)
}
fmt_value(&fi, args[i], 'v')
}
io.flush(auto_cast w)
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprintln formats using the default print settings and writes to w
wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
- size0 := io.size(auto_cast w)
-
for _, i in args {
if i > 0 {
- io.write_string(fi.writer, sep)
+ io.write_string(fi.writer, sep, &fi.n)
}
fmt_value(&fi, args[i], 'v')
}
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
io.flush(auto_cast w)
-
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprintf formats according to the specififed format string and writes to w
wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi: Info
arg_index: int = 0
end := len(fmt)
was_prev_index := false
- size0 := io.size(auto_cast w)
-
loop: for i := 0; i < end; /**/ {
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
@@ -230,7 +247,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
}
if i > prev_i {
- io.write_string(fi.writer, fmt[prev_i:i])
+ io.write_string(fi.writer, fmt[prev_i:i], &fi.n)
}
if i >= end {
break loop
@@ -245,13 +262,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
// Skip extra one
i += 1
}
- io.write_byte(fi.writer, char)
+ io.write_byte(fi.writer, char, &fi.n)
continue loop
} else if char == '{' {
if i < end && fmt[i] == char {
// Skip extra one
i += 1
- io.write_byte(fi.writer, char)
+ io.write_byte(fi.writer, char, &fi.n)
continue loop
}
}
@@ -282,7 +299,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
if !fi.width_set {
- io.write_string(w, "%!(BAD WIDTH)")
+ io.write_string(w, "%!(BAD WIDTH)", &fi.n)
}
if fi.width < 0 {
@@ -313,7 +330,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi.prec_set = false
}
if !fi.prec_set {
- io.write_string(fi.writer, "%!(BAD PRECISION)")
+ io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
}
was_prev_index = false
} else {
@@ -326,7 +343,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if i >= end {
- io.write_string(fi.writer, "%!(NO VERB)")
+ io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
break loop
}
@@ -335,11 +352,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
switch {
case verb == '%':
- io.write_byte(fi.writer, '%')
+ io.write_byte(fi.writer, '%', &fi.n)
case !fi.good_arg_index:
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
case arg_index >= len(args):
- io.write_string(fi.writer, "%!(MISSING ARGUMENT)")
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
case:
fmt_arg(&fi, args[arg_index], verb)
arg_index += 1
@@ -355,14 +372,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
arg_index = new_arg_index
i = new_i
} else {
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &fi.n)
// Skip over the bad argument
start_index := i
for i < end && fmt[i] != '}' && fmt[i] != ':' {
i += 1
}
fmt_arg(&fi, fmt[start_index:i], 'v')
- io.write_string(fi.writer, ")")
+ io.write_string(fi.writer, ")", &fi.n)
}
}
@@ -395,7 +412,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
if !fi.width_set {
- io.write_string(fi.writer, "%!(BAD WIDTH)")
+ io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n)
}
if fi.width < 0 {
@@ -426,7 +443,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi.prec_set = false
}
if !fi.prec_set {
- io.write_string(fi.writer, "%!(BAD PRECISION)")
+ io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
}
was_prev_index = false
} else {
@@ -440,7 +457,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
if i >= end {
- io.write_string(fi.writer, "%!(NO VERB)")
+ io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
break loop
}
@@ -450,7 +467,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if i >= end {
- io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)")
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
break loop
}
@@ -459,11 +476,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
switch {
case brace != '}':
- io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)")
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
case !fi.good_arg_index:
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
case arg_index >= len(args):
- io.write_string(fi.writer, "%!(MISSING ARGUMENT)")
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
case:
fmt_arg(&fi, args[arg_index], verb)
arg_index += 1
@@ -472,32 +489,33 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if !fi.reordered && arg_index < len(args) {
- io.write_string(fi.writer, "%!(EXTRA ")
+ io.write_string(fi.writer, "%!(EXTRA ", &fi.n)
for arg, index in args[arg_index:] {
if index > 0 {
- io.write_string(fi.writer, ", ")
+ io.write_string(fi.writer, ", ", &fi.n)
}
if arg == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
} else {
fmt_arg(&fi, args[index], 'v')
}
}
- io.write_string(fi.writer, ")")
+ io.write_string(fi.writer, ")", &fi.n)
}
io.flush(auto_cast w)
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w
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
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)
@@ -576,23 +594,27 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
- io.write_string(writer, "%!")
- io.write_rune(writer, verb)
- io.write_byte(writer, '(')
+ prev_in_bad := fi.in_bad
+ defer fi.in_bad = prev_in_bad
+ fi.in_bad = true
+
+ io.write_string(writer, "%!", &fi.n)
+ io.write_rune(writer, verb, &fi.n)
+ io.write_byte(writer, '(', &fi.n)
if arg.id != nil {
- reflect.write_typeid(writer, arg.id)
- io.write_byte(writer, '=')
+ reflect.write_typeid(writer, arg.id, &fi.n)
+ io.write_byte(writer, '=', &fi.n)
fmt_value(fi, arg, 'v')
} else {
- io.write_string(writer, "")
+ io.write_string(writer, "", &fi.n)
}
- io.write_byte(writer, ')')
+ io.write_byte(writer, ')', &fi.n)
}
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
- io.write_string(writer, b ? "true" : "false")
+ fmt_string(fi, b ? "true" : "false", 's')
case:
fmt_bad_verb(fi, verb)
}
@@ -610,7 +632,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
}
for i := 0; i < width; i += 1 {
- io.write_byte(fi.writer, pad_byte)
+ io.write_byte(fi.writer, pad_byte, &fi.n)
}
}
@@ -669,8 +691,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
case 16: c = 'x'
}
if c != 0 {
- io.write_byte(fi.writer, '0')
- io.write_byte(fi.writer, c)
+ io.write_byte(fi.writer, '0', &fi.n)
+ io.write_byte(fi.writer, c, &fi.n)
}
}
@@ -735,8 +757,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
case 16: c = 'x'
}
if c != 0 {
- io.write_byte(fi.writer, '0')
- io.write_byte(fi.writer, c)
+ io.write_byte(fi.writer, '0', &fi.n)
+ io.write_byte(fi.writer, c, &fi.n)
}
}
@@ -752,9 +774,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX"
fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
switch verb {
case 'c', 'r', 'v':
- io.write_rune(fi.writer, r)
+ io.write_rune(fi.writer, r, &fi.n)
case 'q':
- strings.write_quoted_rune(fi.writer, r)
+ fi.n += io.write_quoted_rune(fi.writer, r)
case:
fmt_int(fi, u64(r), false, 32, verb)
}
@@ -776,7 +798,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb)
} else {
- io.write_string(fi.writer, "U+")
+ io.write_string(fi.writer, "U+", &fi.n)
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER)
}
@@ -801,7 +823,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb)
} else {
- io.write_string(fi.writer, "U+")
+ io.write_string(fi.writer, "U+", &fi.n)
_fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER)
}
@@ -812,24 +834,24 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
_pad :: proc(fi: ^Info, s: string) {
if !fi.width_set {
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
return
}
width := fi.width - utf8.rune_count_in_string(s)
if fi.minus { // right pad
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
fmt_write_padding(fi, width)
} else { // left pad
fmt_write_padding(fi, width)
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
}
}
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
- case 'f', 'F', 'v':
+ case 'f', 'F', 'g', 'G', 'v':
prec: int = 3
if fi.prec_set {
prec = fi.prec
@@ -849,15 +871,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b))
+ io.write_string(fi.writer, string(b), &fi.n)
return
}
if fi.plus || b[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
- io.write_byte(fi.writer, b[0])
+ io.write_byte(fi.writer, b[0], &fi.n)
fmt_write_padding(fi, fi.width - len(b))
- io.write_string(fi.writer, string(b[1:]))
+ io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
@@ -885,15 +907,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b))
+ io.write_string(fi.writer, string(b), &fi.n)
return
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
- io.write_byte(fi.writer, b[0])
+ io.write_byte(fi.writer, b[0], &fi.n)
fmt_write_padding(fi, fi.width - len(b))
- io.write_string(fi.writer, string(b[1:]))
+ io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
@@ -917,7 +939,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
case: panic("Unhandled float size")
}
- io.write_string(fi.writer, "0h")
+ io.write_string(fi.writer, "0h", &fi.n)
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER)
@@ -928,17 +950,41 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
+ s, verb := s, verb
+ if ol, ok := fi.optional_len.?; ok {
+ s = s[:clamp(ol, 0, len(s))]
+ }
+ if !fi.in_bad && fi.record_level > 0 && verb == 'v' {
+ verb = 'q'
+ }
+
switch verb {
case 's', 'v':
- io.write_string(fi.writer, s)
- if fi.width_set && len(s) < fi.width {
- for _ in 0.. len(s) {
+ if fi.minus {
+ io.write_string(fi.writer, s, &fi.n)
+ }
+
+ for _ in 0.. 0 && space {
- io.write_byte(fi.writer, ' ')
+ io.write_byte(fi.writer, ' ', &fi.n)
}
char_set := __DIGITS_UPPER
if verb == 'x' {
@@ -968,8 +1014,8 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
u := u64(uintptr(p))
switch verb {
case 'p', 'v':
- if !fi.hash || verb == 'v' {
- io.write_string(fi.writer, "0x")
+ if !fi.hash && verb == 'v' {
+ io.write_string(fi.writer, "0x", &fi.n)
}
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
@@ -1031,7 +1077,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == nil || v.data == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
@@ -1043,12 +1089,14 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
case: fmt_bad_verb(fi, verb)
case 'i', 'd', 'f':
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb)
- case 's', 'v':
- str, ok := enum_value_to_string(v)
- if !ok {
- str = "%!(BAD ENUM VALUE)"
+ case 's', 'v', 'q':
+ if str, ok := enum_value_to_string(v); ok {
+ fmt_string(fi, str, verb)
+ } else {
+ io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n)
+ fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
+ io.write_string(fi.writer, ")", &fi.n)
}
- io.write_string(fi.writer, str)
}
}
}
@@ -1092,8 +1140,8 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
- case .Little: return ODIN_ENDIAN != "little"
- case .Big: return ODIN_ENDIAN != "big"
+ case .Little: return ODIN_ENDIAN != .Little
+ case .Big: return ODIN_ENDIAN != .Big
}
}
return false
@@ -1141,12 +1189,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
et := runtime.type_info_base(info.elem)
if name != "" {
- io.write_string(fi.writer, name)
+ io.write_string(fi.writer, name, &fi.n)
} else {
- reflect.write_type(fi.writer, type_info)
+ reflect.write_type(fi.writer, type_info, &fi.n)
}
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
+ io.write_byte(fi.writer, '{', &fi.n)
+ defer io.write_byte(fi.writer, '}', &fi.n)
e, is_enum := et.variant.(runtime.Type_Info_Enum)
commas := 0
@@ -1156,21 +1204,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
}
if commas > 0 {
- io.write_string(fi.writer, ", ")
+ io.write_string(fi.writer, ", ", &fi.n)
}
if is_enum {
for ev, evi in e.values {
v := u64(ev)
if v == u64(i) {
- io.write_string(fi.writer, e.names[evi])
+ io.write_string(fi.writer, e.names[evi], &fi.n)
commas += 1
continue loop
}
}
}
v := i64(i) + info.lower
- io.write_i64(fi.writer, v, 10)
+ io.write_i64(fi.writer, v, 10, &fi.n)
commas += 1
}
}
@@ -1178,20 +1226,22 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
fmt_write_indent :: proc(fi: ^Info) {
for in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(array_data) + uintptr(i*elem_size)
fmt_arg(fi, any{rawptr(data), elem_id}, verb)
@@ -1216,21 +1266,529 @@ fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: in
}
}
-fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
+
+@(private)
+handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb: ^rune, optional_len: ^int, use_nul_termination: ^bool) -> (do_continue: bool) {
+ handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) {
+ if optional_len == nil {
+ return
+ }
+ for f, i in info.names {
+ if f != field_name {
+ continue
+ }
+ ptr := rawptr(uintptr(data) + info.offsets[i])
+ field := any{ptr, info.types[i].id}
+ if new_len, iok := reflect.as_int(field); iok {
+ optional_len^ = max(new_len, 0)
+ }
+ break
+ }
+ }
+ tag := info.tags[idx]
+ if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
+ value := strings.trim_space(string(vt))
+ switch value {
+ case "": return false
+ case "-": return true
+ }
+ r, w := utf8.decode_rune_in_string(value)
+ value = value[w:]
+ if value == "" || value[0] == ',' {
+ verb^ = r
+ if len(value) > 0 && value[0] == ',' {
+ field_name := value[1:]
+ if field_name == "0" {
+ if use_nul_termination != nil {
+ use_nul_termination^ = true
+ }
+ } else {
+ switch r {
+ case 's', 'q':
+ handle_optional_len(data, info, field_name, optional_len)
+ case 'v':
+ #partial switch reflect.type_kind(info.types[idx].id) {
+ case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array:
+ handle_optional_len(data, info, field_name, optional_len)
+ }
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+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)
+ return
+ }
+ if info.is_raw_union {
+ if type_name == "" {
+ io.write_string(fi.writer, "(raw union)", &fi.n)
+ } else {
+ io.write_string(fi.writer, type_name, &fi.n)
+ io.write_string(fi.writer, "{}", &fi.n)
+ }
+ return
+ }
+
+ is_soa := info.soa_kind != .None
+
+ io.write_string(fi.writer, type_name, &fi.n)
+ io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n)
+ fi.record_level += 1
+ defer fi.record_level -= 1
+
+ hash := fi.hash; defer fi.hash = hash
+ indent := fi.indent; defer fi.indent -= 1
+ do_trailing_comma := hash
+
+ // fi.hash = false;
+ fi.indent += 1
+
+ if hash {
+ io.write_byte(fi.writer, '\n', &fi.n)
+ }
+ defer {
+ if hash {
+ for in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) }
+
+ field_count := -1
+
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
+
+ io.write_string(fi.writer, base_type_name, &fi.n)
+ io.write_byte(fi.writer, '{', &fi.n)
+ defer io.write_byte(fi.writer, '}', &fi.n)
+ fi.record_level += 1
+ defer fi.record_level -= 1
+
+ for i in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) }
+ if hash {
+ fmt_write_indent(fi)
+ }
+
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
+
+ if info.soa_kind == .Fixed {
+ t := info.types[i].variant.(runtime.Type_Info_Array).elem
+ t_size := uintptr(t.size)
+ if reflect.is_any(t) {
+ io.write_string(fi.writer, "any{}", &fi.n)
+ } else {
+ data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size)
+ fmt_arg(fi, any{data, t.id}, verb)
+ }
+ } else {
+ t := info.types[i].variant.(runtime.Type_Info_Pointer).elem
+ t_size := uintptr(t.size)
+ if reflect.is_any(t) {
+ io.write_string(fi.writer, "any{}", &fi.n)
+ } else {
+ field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^
+ data := rawptr(uintptr(field_ptr) + index*t_size)
+ fmt_arg(fi, any{data, t.id}, verb)
+ }
+ }
+
+ if hash { io.write_string(fi.writer, ",\n", &fi.n) }
+ }
+ }
+ } else {
+ field_count := -1
+ for name, i in info.names {
+ optional_len: int = -1
+ use_nul_termination: bool = false
+ verb := 'v'
+ if handle_tag(v.data, info, i, &verb, &optional_len, &use_nul_termination) {
+ continue
+ }
+ field_count += 1
+
+ if optional_len >= 0 {
+ fi.optional_len = optional_len
+ }
+ defer if optional_len >= 0 {
+ fi.optional_len = nil
+ }
+ fi.use_nul_termination = use_nul_termination
+ defer fi.use_nul_termination = false
+
+ if !do_trailing_comma && field_count > 0 { io.write_string(fi.writer, ", ") }
+ if hash {
+ fmt_write_indent(fi)
+ }
+
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
+
+ if t := info.types[i]; reflect.is_any(t) {
+ io.write_string(fi.writer, "any{}", &fi.n)
+ } else {
+ data := rawptr(uintptr(v.data) + info.offsets[i])
+ fmt_arg(fi, any{data, t.id}, verb)
+ }
+
+ if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
+ }
+ }
+}
+
+@(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) {
+ if mem.check_zero_ptr(rawptr(p), elem_size) {
+ break
+ }
+ n += 1
+ }
+ return n
+}
+
+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)
+ return
+ }
+ n := search_nul_termination(data, elem_size, max_n)
+ fmt_array(fi, data, n, elem_size, elem, verb)
+}
+
+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")
+ return
+ }
+ if verb == 's' || verb == 'q' {
+ print_utf16 :: proc(fi: ^Info, s: []$T) where size_of(T) == 2, intrinsics.type_is_integer(T) {
+ REPLACEMENT_CHAR :: '\ufffd'
+ _surr1 :: 0xd800
+ _surr2 :: 0xdc00
+ _surr3 :: 0xe000
+ _surr_self :: 0x10000
+
+ for i := 0; i < len(s); i += 1 {
+ r := rune(REPLACEMENT_CHAR)
+
+ switch c := s[i]; {
+ case c < _surr1, _surr3 <= c:
+ r = rune(c)
+ case _surr1 <= c && c < _surr2 && i+1 < len(s) &&
+ _surr2 <= s[i+1] && s[i+1] < _surr3:
+ r1, r2 := rune(c), rune(s[i+1])
+ if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
+ r = (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self
+ }
+ i += 1
+ }
+ io.write_rune(fi.writer, r, &fi.n)
+ }
+ }
+
+ print_utf32 :: proc(fi: ^Info, s: []$T) where size_of(T) == 4 {
+ for r in s {
+ io.write_rune(fi.writer, rune(r), &fi.n)
+ }
+ }
+
+ switch reflect.type_info_base(elem).id {
+ case byte: fmt_string(fi, string(([^]byte)(data)[:n]), verb); return
+ case u16: print_utf16(fi, ([^]u16)(data)[:n]); return
+ case u16le: print_utf16(fi, ([^]u16le)(data)[:n]); return
+ case u16be: print_utf16(fi, ([^]u16be)(data)[:n]); return
+ case u32: print_utf32(fi, ([^]u32)(data)[:n]); return
+ case u32le: print_utf32(fi, ([^]u32le)(data)[:n]); return
+ case u32be: print_utf32(fi, ([^]u32be)(data)[:n]); return
+ case rune: print_utf32(fi, ([^]rune)(data)[:n]); return
+ }
+ }
+ if verb == 'p' {
+ fmt_pointer(fi, data, 'p')
+ } else {
+ fmt_write_array(fi, data, n, elem_size, elem.id, verb)
+ }
+}
+
+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
for x := i; x >= 10; x /= 10 {
n -= 1
}
for in 0.. (nw: int, nv: u64) {
+ v := v
+ w := len(buf)
+ print := false
+ for in 0.. int {
+ v := v
+ w := len(buf)
+ if v == 0 {
+ w -= 1
+ buf[w] = '0'
+ } else {
+ for v > 0 {
+ w -= 1
+ buf[w] = byte(v%10) + '0'
+ v /= 10
+ }
+ }
+ return w
+ }
+
+ buf: [32]byte
+ w := len(buf)
+ u := u64(a)
+ neg := a < 0
+ if neg {
+ u = -u
+ }
+
+ if u < u64(time.Second) {
+ prec: int
+ w -= 1
+ buf[w] = 's'
+ w -= 1
+ switch {
+ case u == 0:
+ io.write_string(fi.writer, "0s", &fi.n)
+ return
+ case u < u64(time.Microsecond):
+ prec = 0
+ buf[w] = 'n'
+ case u < u64(time.Millisecond):
+ prec = 3
+ // U+00B5 'µ' micro sign == 0xC2 0xB5
+ w -= 1 // Need room for two bytes
+ copy(buf[w:], "µ")
+ case:
+ prec = 6
+ buf[w] = 'm'
+ }
+ w, u = ffrac(buf[:w], u, prec)
+ w = fint(buf[:w], u)
+ } else {
+ w -= 1
+ buf[w] = 's'
+ w, u = ffrac(buf[:w], u, 9)
+ w = fint(buf[:w], u%60)
+ u /= 60
+ if u > 0 {
+ w -= 1
+ buf[w] = 'm'
+ w = fint(buf[:w], u%60)
+ u /= 60
+ if u > 0 {
+ w -= 1
+ buf[w] = 'h'
+ w = fint(buf[:w], u)
+ }
+ }
+ }
+
+ if neg {
+ w -= 1
+ buf[w] = '-'
+ }
+ io.write_string(fi.writer, string(buf[w:]), &fi.n)
+ return
+
+ case time.Time:
+ t := a
+ y, mon, d := time.date(t)
+ h, min, s := time.clock(t)
+ ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
+ write_padded_number(fi, i64(y), 4)
+ io.write_byte(fi.writer, '-', &fi.n)
+ write_padded_number(fi, i64(mon), 2)
+ io.write_byte(fi.writer, '-', &fi.n)
+ write_padded_number(fi, i64(d), 2)
+ io.write_byte(fi.writer, ' ', &fi.n)
+
+ write_padded_number(fi, i64(h), 2)
+ io.write_byte(fi.writer, ':', &fi.n)
+ write_padded_number(fi, i64(min), 2)
+ io.write_byte(fi.writer, ':', &fi.n)
+ write_padded_number(fi, i64(s), 2)
+ io.write_byte(fi.writer, '.', &fi.n)
+ write_padded_number(fi, (ns), 9)
+ io.write_string(fi.writer, " +0000 UTC", &fi.n)
+ return
+ }
+
+ #partial switch b in info.base.variant {
+ case runtime.Type_Info_Struct:
+ fmt_struct(fi, v, verb, b, info.name)
+ case runtime.Type_Info_Bit_Set:
+ fmt_bit_set(fi, v)
+ case:
+ fmt_value(fi, any{v.data, info.base.id}, verb)
+ }
+}
+
+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)
+ return
+ }
+
+ if reflect.type_info_union_is_pure_maybe(info) {
+ if v.data == nil {
+ io.write_string(fi.writer, "nil", &fi.n)
+ } else {
+ id := info.variants[0].id
+ fmt_arg(fi, any{v.data, id}, verb)
+ }
+ return
+ }
+
+ tag: i64 = -1
+ tag_ptr := uintptr(v.data) + info.tag_offset
+ tag_any := any{rawptr(tag_ptr), info.tag_type.id}
+
+ switch i in tag_any {
+ case u8: tag = i64(i)
+ case i8: tag = i64(i)
+ case u16: tag = i64(i)
+ case i16: tag = i64(i)
+ case u32: tag = i64(i)
+ case i32: tag = i64(i)
+ case u64: tag = i64(i)
+ case i64: tag = i
+ case: panic("Invalid union tag type")
+ }
+ assert(tag >= 0)
+
+ if v.data == nil {
+ io.write_string(fi.writer, "nil", &fi.n)
+ } else if info.no_nil {
+ id := info.variants[tag].id
+ fmt_arg(fi, any{v.data, id}, verb)
+ } else if tag == 0 {
+ io.write_string(fi.writer, "nil", &fi.n)
+ } else {
+ id := info.variants[tag-1].id
+ fmt_arg(fi, any{v.data, id}, verb)
+ }
+}
+
+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)
+
+ fi.indent += 1
+
+ if fi.hash {
+ // Printed as it is written
+ io.write_byte(fi.writer, '\n', &fi.n)
+ for row in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) }
+
+ offset := (row + col*info.elem_stride)*info.elem_size
+
+ data := uintptr(v.data) + uintptr(offset)
+ fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
+ }
+ io.write_string(fi.writer, ",\n", &fi.n)
+ }
+ } else {
+ // Printed in Row-Major layout to match text layout
+ for row in 0.. 0 { io.write_string(fi.writer, "; ", &fi.n) }
+ for col in 0.. 0 { io.write_string(fi.writer, ", ", &fi.n) }
+
+ offset := (row + col*info.elem_stride)*info.elem_size
+
+ data := uintptr(v.data) + uintptr(offset)
+ fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
+ }
+ }
+ }
+
+ fi.indent -= 1
+
+ if fi.hash {
+ fmt_write_indent(fi)
+ }
+}
+
+fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if v.data == nil || v.id == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
@@ -1253,238 +1811,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Tuple: // Ignore
case runtime.Type_Info_Named:
- // Built-in Custom Formatters for core library types
- switch a in v {
- case runtime.Source_Code_Location:
- io.write_string(fi.writer, a.file_path)
- io.write_byte(fi.writer, '(')
- io.write_int(fi.writer, int(a.line))
- io.write_byte(fi.writer, ':')
- io.write_int(fi.writer, int(a.column))
- io.write_byte(fi.writer, ')')
- return
-
- case time.Duration:
- ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
- v := v
- w := len(buf)
- print := false
- for in 0.. int {
- v := v
- w := len(buf)
- if v == 0 {
- w -= 1
- buf[w] = '0'
- } else {
- for v > 0 {
- w -= 1
- buf[w] = byte(v%10) + '0'
- v /= 10
- }
- }
- return w
- }
-
- buf: [32]byte
- w := len(buf)
- u := u64(a)
- neg := a < 0
- if neg {
- u = -u
- }
-
- if u < u64(time.Second) {
- prec: int
- w -= 1
- buf[w] = 's'
- w -= 1
- switch {
- case u == 0:
- io.write_string(fi.writer, "0s")
- return
- case u < u64(time.Microsecond):
- prec = 0
- buf[w] = 'n'
- case u < u64(time.Millisecond):
- prec = 3
- // U+00B5 'µ' micro sign == 0xC2 0xB5
- w -= 1 // Need room for two bytes
- copy(buf[w:], "µ")
- case:
- prec = 6
- buf[w] = 'm'
- }
- w, u = ffrac(buf[:w], u, prec)
- w = fint(buf[:w], u)
- } else {
- w -= 1
- buf[w] = 's'
- w, u = ffrac(buf[:w], u, 9)
- w = fint(buf[:w], u%60)
- u /= 60
- if u > 0 {
- w -= 1
- buf[w] = 'm'
- w = fint(buf[:w], u%60)
- u /= 60
- if u > 0 {
- w -= 1
- buf[w] = 'h'
- w = fint(buf[:w], u)
- }
- }
- }
-
- if neg {
- w -= 1
- buf[w] = '-'
- }
- io.write_string(fi.writer, string(buf[w:]))
- return
-
- case time.Time:
- t := a
- y, mon, d := time.date(t)
- h, min, s := time.clock(t)
- ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
- write_padded_number(fi, i64(y), 4)
- io.write_byte(fi.writer, '-')
- write_padded_number(fi, i64(mon), 2)
- io.write_byte(fi.writer, '-')
- write_padded_number(fi, i64(d), 2)
- io.write_byte(fi.writer, ' ')
-
- write_padded_number(fi, i64(h), 2)
- io.write_byte(fi.writer, ':')
- write_padded_number(fi, i64(min), 2)
- io.write_byte(fi.writer, ':')
- write_padded_number(fi, i64(s), 2)
- io.write_byte(fi.writer, '.')
- write_padded_number(fi, (ns), 9)
- io.write_string(fi.writer, " +0000 UTC")
- return
- }
-
- #partial switch b in info.base.variant {
- case runtime.Type_Info_Struct:
- if verb != 'v' {
- fmt_bad_verb(fi, verb)
- return
- }
- if b.is_raw_union {
- io.write_string(fi.writer, info.name)
- io.write_string(fi.writer, "{}")
- return
- }
-
- is_soa := b.soa_kind != .None
-
- io.write_string(fi.writer, info.name)
- io.write_byte(fi.writer, '[' if is_soa else '{')
-
- hash := fi.hash; defer fi.hash = hash
- indent := fi.indent; defer fi.indent -= 1
-
- // fi.hash = false;
- fi.indent += 1
-
- if hash {
- io.write_byte(fi.writer, '\n')
- }
- defer {
- if hash {
- for in 0.. 0 { io.write_string(fi.writer, ", ") }
-
- field_count := -1
-
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
-
- io.write_string(fi.writer, base_type_name)
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
-
- for name, i in b.names {
- field_count += 1
-
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
- if hash {
- fmt_write_indent(fi)
- }
-
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
-
- t := b.types[i].variant.(runtime.Type_Info_Array).elem
- t_size := uintptr(t.size)
- if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
- } else {
- data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size)
- fmt_arg(fi, any{data, t.id}, 'v')
- }
-
- if hash { io.write_string(fi.writer, ",\n") }
- }
- }
- } else {
- field_count := -1
- for name, i in b.names {
- field_count += 1
-
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
- if hash {
- fmt_write_indent(fi)
- }
-
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
-
- if t := b.types[i]; reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
- } else {
- data := rawptr(uintptr(v.data) + b.offsets[i])
- fmt_arg(fi, any{data, t.id}, 'v')
- }
-
- if hash { io.write_string(fi.writer, ",\n") }
- }
- }
-
- case runtime.Type_Info_Bit_Set:
- fmt_bit_set(fi, v)
- case:
- fmt_value(fi, any{v.data, info.base.id}, verb)
- }
+ fmt_named(fi, v, verb, info)
case runtime.Type_Info_Boolean: fmt_arg(fi, v, verb)
case runtime.Type_Info_Integer: fmt_arg(fi, v, verb)
@@ -1496,7 +1823,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Pointer:
if v.id == typeid_of(^runtime.Type_Info) {
- reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^)
+ reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^, &fi.n)
} else {
ptr := (^rawptr)(v.data)^
if verb != 'p' && info.elem != nil {
@@ -1510,12 +1837,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
- if fi.record_level < 1 {
- fi.record_level += 1
- defer fi.record_level -= 1
+ if fi.indirection_level < 1 {
+ fi.indirection_level += 1
+ defer fi.indirection_level -= 1
io.write_byte(fi.writer, '&')
fmt_value(fi, a, verb)
return
@@ -1524,13 +1851,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
- if fi.record_level < 1 {
- fi.record_level += 1
- defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ if fi.indirection_level < 1 {
+ fi.indirection_level += 1
+ defer fi.indirection_level -= 1
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
@@ -1542,38 +1869,56 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Multi_Pointer:
ptr := (^rawptr)(v.data)^
+ if ptr == nil {
+ io.write_string(fi.writer, "", &fi.n)
+ return
+ }
if verb != 'p' && info.elem != nil {
a := any{ptr, info.elem.id}
elem := runtime.type_info_base(info.elem)
if elem != nil {
+ if n, ok := fi.optional_len.?; ok {
+ fmt_array(fi, ptr, n, elem.size, elem, verb)
+ return
+ } else if fi.use_nul_termination {
+ fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
+ return
+ }
+
#partial switch e in elem.variant {
+ case runtime.Type_Info_Integer:
+ switch verb {
+ case 's', 'q':
+ switch elem.id {
+ case u8:
+ fmt_cstring(fi, cstring(ptr), verb)
+ return
+ case u16, u32, rune:
+ n := search_nul_termination(ptr, elem.size, -1)
+ fmt_array(fi, ptr, n, elem.size, elem, verb)
+ return
+ }
+ }
+
case runtime.Type_Info_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
- if ptr == nil {
- io.write_string(fi.writer, "")
- return
- }
- if fi.record_level < 1 {
- fi.record_level += 1
- defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ if fi.indirection_level < 1 {
+ fi.indirection_level += 1
+ defer fi.indirection_level -= 1
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
- if ptr == nil {
- io.write_string(fi.writer, "")
- return
- }
- if fi.record_level < 1 {
- fi.record_level += 1
- defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ if fi.indirection_level < 1 {
+ fi.indirection_level += 1
+ defer fi.indirection_level -= 1
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
@@ -1582,21 +1927,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
fmt_pointer(fi, ptr, verb)
- case runtime.Type_Info_Array:
- if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
- s := strings.string_from_ptr((^byte)(v.data), info.count)
- fmt_string(fi, s, verb)
- } else {
- fmt_write_array(fi, v.data, info.count, info.elem_size, info.elem.id, verb)
- }
-
case runtime.Type_Info_Enumerated_Array:
+ fi.record_level += 1
+ defer fi.record_level -= 1
+
if fi.hash {
- io.write_string(fi.writer, "[\n")
+ io.write_string(fi.writer, "[\n", &fi.n)
defer {
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
fmt_write_indent(fi)
- io.write_byte(fi.writer, ']')
+ io.write_byte(fi.writer, ']', &fi.n)
}
indent := fi.indent
fi.indent += 1
@@ -1607,78 +1947,94 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
if ok {
- io.write_byte(fi.writer, '.')
- io.write_string(fi.writer, idx)
+ io.write_byte(fi.writer, '.', &fi.n)
+ io.write_string(fi.writer, idx, &fi.n)
} else {
- io.write_i64(fi.writer, i64(info.min_value)+i64(i))
+ io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n)
}
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, " = ", &fi.n)
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
- io.write_string(fi.writer, ",\n")
+ io.write_string(fi.writer, ",\n", &fi.n)
}
} else {
- io.write_byte(fi.writer, '[')
- defer io.write_byte(fi.writer, ']')
+ io.write_byte(fi.writer, '[', &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
if ok {
- io.write_byte(fi.writer, '.')
- io.write_string(fi.writer, idx)
+ io.write_byte(fi.writer, '.', &fi.n)
+ io.write_string(fi.writer, idx, &fi.n)
} else {
- io.write_i64(fi.writer, i64(info.min_value)+i64(i))
+ io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n)
}
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, " = ", &fi.n)
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
}
}
+ case runtime.Type_Info_Array:
+ n := info.count
+ ptr := v.data
+ if ol, ok := fi.optional_len.?; ok {
+ n = min(n, ol)
+ } else if fi.use_nul_termination {
+ fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
+ return
+ }
+ fmt_array(fi, ptr, n, info.elem_size, info.elem, verb)
+
+ case runtime.Type_Info_Slice:
+ slice := cast(^mem.Raw_Slice)v.data
+ n := slice.len
+ ptr := slice.data
+ if ol, ok := fi.optional_len.?; ok {
+ n = min(n, ol)
+ } else if fi.use_nul_termination {
+ fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
+ return
+ }
+ fmt_array(fi, ptr, n, info.elem_size, info.elem, verb)
+
case runtime.Type_Info_Dynamic_Array:
array := cast(^mem.Raw_Dynamic_Array)v.data
- if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
- s := strings.string_from_ptr((^byte)(array.data), array.len)
- fmt_string(fi, s, verb)
- } else if verb == 'p' {
- fmt_pointer(fi, array.data, 'p')
- } else {
- fmt_write_array(fi, array.data, array.len, info.elem_size, info.elem.id, verb)
+ n := array.len
+ ptr := array.data
+ if ol, ok := fi.optional_len.?; ok {
+ n = min(n, ol)
+ } else if fi.use_nul_termination {
+ fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
+ return
}
+ fmt_array(fi, ptr, n, info.elem_size, info.elem, verb)
case runtime.Type_Info_Simd_Vector:
- io.write_byte(fi.writer, '<')
- defer io.write_byte(fi.writer, '>')
+ io.write_byte(fi.writer, '<', &fi.n)
+ defer io.write_byte(fi.writer, '>', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
}
- case runtime.Type_Info_Slice:
- slice := cast(^mem.Raw_Slice)v.data
- if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
- s := strings.string_from_ptr((^byte)(slice.data), slice.len)
- fmt_string(fi, s, verb)
- } else if verb == 'p' {
- fmt_pointer(fi, slice.data, 'p')
- } else {
- fmt_write_array(fi, slice.data, slice.len, info.elem_size, info.elem.id, verb)
- }
case runtime.Type_Info_Map:
if verb != 'v' {
fmt_bad_verb(fi, verb)
return
}
- io.write_string(fi.writer, "map[")
- defer io.write_byte(fi.writer, ']')
+ io.write_string(fi.writer, "map[", &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
+ fi.record_level += 1
+ defer fi.record_level -= 1
m := (^mem.Raw_Map)(v.data)
if m != nil {
@@ -1692,14 +2048,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
entry_size := ed.elem_size
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(entries.data) + uintptr(i*entry_size)
key := data + entry_type.offsets[2]
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
- io.write_string(fi.writer, "=")
+ io.write_string(fi.writer, "=", &fi.n)
value := data + entry_type.offsets[3]
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
@@ -1707,168 +2063,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Struct:
- if info.is_raw_union {
- io.write_string(fi.writer, "(raw_union)")
- return
- }
-
- is_soa := info.soa_kind != .None
-
- io.write_byte(fi.writer, '[' if is_soa else '{')
- defer io.write_byte(fi.writer, ']' if is_soa else '}')
-
- fi.indent += 1; defer fi.indent -= 1
- hash := fi.hash; defer fi.hash = hash
- // fi.hash = false;
-
-
- if hash { io.write_byte(fi.writer, '\n') }
-
- if is_soa {
- fi.indent += 1
- defer fi.indent -= 1
-
- base_type_name: string
- if v, ok := info.soa_base_type.variant.(runtime.Type_Info_Named); ok {
- base_type_name = v.name
- }
-
- actual_field_count := len(info.names)
-
- n := uintptr(info.soa_len)
-
- if info.soa_kind == .Slice {
- actual_field_count = len(info.names)-1 // len
-
- n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
-
- } else if info.soa_kind == .Dynamic {
- actual_field_count = len(info.names)-3 // len, cap, allocator
-
- n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
- }
-
-
-
- for index in 0.. 0 { io.write_string(fi.writer, ", ") }
-
- field_count := -1
-
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
-
- io.write_string(fi.writer, base_type_name)
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
-
- for i in 0.. 0 { io.write_string(fi.writer, ", ") }
- if hash {
- fmt_write_indent(fi)
- }
-
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
-
- if info.soa_kind == .Fixed {
- t := info.types[i].variant.(runtime.Type_Info_Array).elem
- t_size := uintptr(t.size)
- if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
- } else {
- data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size)
- fmt_arg(fi, any{data, t.id}, 'v')
- }
- } else {
- t := info.types[i].variant.(runtime.Type_Info_Pointer).elem
- t_size := uintptr(t.size)
- if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
- } else {
- field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^
- data := rawptr(uintptr(field_ptr) + index*t_size)
- fmt_arg(fi, any{data, t.id}, 'v')
- }
- }
-
- if hash { io.write_string(fi.writer, ",\n") }
- }
- }
- } else {
- field_count := -1
- for name, i in info.names {
- field_count += 1
-
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
- if hash {
- fmt_write_indent(fi)
- }
-
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
-
- if t := info.types[i]; reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
- } else {
- data := rawptr(uintptr(v.data) + info.offsets[i])
- fmt_arg(fi, any{data, t.id}, 'v')
- }
-
- if hash {
- io.write_string(fi.writer, ",\n")
- }
- }
- }
-
+ fmt_struct(fi, v, verb, info, "")
case runtime.Type_Info_Union:
- if type_info.size == 0 {
- io.write_string(fi.writer, "nil")
- return
- }
-
-
- if reflect.type_info_union_is_pure_maybe(info) {
- if v.data == nil {
- io.write_string(fi.writer, "nil")
- } else {
- id := info.variants[0].id
- fmt_arg(fi, any{v.data, id}, verb)
- }
- return
- }
-
- tag: i64 = -1
- tag_ptr := uintptr(v.data) + info.tag_offset
- tag_any := any{rawptr(tag_ptr), info.tag_type.id}
-
- switch i in tag_any {
- case u8: tag = i64(i)
- case i8: tag = i64(i)
- case u16: tag = i64(i)
- case i16: tag = i64(i)
- case u32: tag = i64(i)
- case i32: tag = i64(i)
- case u64: tag = i64(i)
- case i64: tag = i
- case: panic("Invalid union tag type")
- }
- assert(tag >= 0)
-
- if v.data == nil {
- io.write_string(fi.writer, "nil")
- } else if info.no_nil {
- id := info.variants[tag].id
- fmt_arg(fi, any{v.data, id}, verb)
- } else if tag == 0 {
- io.write_string(fi.writer, "nil")
- } else {
- id := info.variants[tag-1].id
- fmt_arg(fi, any{v.data, id}, verb)
- }
+ fmt_union(fi, v, verb, info, type_info.size)
case runtime.Type_Info_Enum:
fmt_enum(fi, v, verb)
@@ -1876,16 +2074,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Procedure:
ptr := (^rawptr)(v.data)^
if ptr == nil {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
} else {
- reflect.write_typeid(fi.writer, v.id)
- io.write_string(fi.writer, " @ ")
+ reflect.write_typeid(fi.writer, v.id, &fi.n)
+ io.write_string(fi.writer, " @ ", &fi.n)
fmt_pointer(fi, ptr, 'p')
}
case runtime.Type_Info_Type_Id:
id := (^typeid)(v.data)^
- reflect.write_typeid(fi.writer, id)
+ reflect.write_typeid(fi.writer, id, &fi.n)
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v)
@@ -1902,18 +2100,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if verb == 'p' {
fmt_pointer(fi, ptr, 'p')
} else if ptr == nil {
- io.write_string(fi.writer, "[]")
+ io.write_string(fi.writer, "[]", &fi.n)
} else {
len_ptr := uintptr(v.data) + uintptr(info.base_integer.size)
len_any := any{rawptr(len_ptr), info.base_integer.id}
len, _ := reflect.as_int(len_any)
slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice)
- io.write_byte(fi.writer, '[')
- defer io.write_byte(fi.writer, ']')
+ fi.record_level += 1
+ defer fi.record_level -= 1
+
+ io.write_byte(fi.writer, '[', &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(ptr) + uintptr(i*slice_type.elem_size)
fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb)
@@ -1921,46 +2122,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Matrix:
- io.write_string(fi.writer, "matrix[")
- defer io.write_byte(fi.writer, ']')
-
- fi.indent += 1
-
- if fi.hash {
- // Printed as it is written
- io.write_byte(fi.writer, '\n')
- for row in 0.. 0 { io.write_string(fi.writer, ", ") }
-
- offset := (row + col*info.elem_stride)*info.elem_size
-
- data := uintptr(v.data) + uintptr(offset)
- fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
- }
- io.write_string(fi.writer, ",\n")
- }
- } else {
- // Printed in Row-Major layout to match text layout
- for row in 0.. 0 { io.write_string(fi.writer, "; ") }
- for col in 0.. 0 { io.write_string(fi.writer, ", ") }
-
- offset := (row + col*info.elem_stride)*info.elem_size
-
- data := uintptr(v.data) + uintptr(offset)
- fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
- }
- }
- }
-
- fi.indent -= 1
-
- if fi.hash {
- fmt_write_indent(fi)
- }
+ fmt_matrix(fi, v, verb, info)
}
}
@@ -1970,10 +2132,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
r, i := real(c), imag(c)
fmt_float(fi, r, bits/2, verb)
if !fi.plus && i >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, i, bits/2, verb)
- io.write_rune(fi.writer, 'i')
+ io.write_rune(fi.writer, 'i', &fi.n)
case:
fmt_bad_verb(fi, verb)
@@ -1989,22 +2151,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
fmt_float(fi, r, bits/4, verb)
if !fi.plus && i >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, i, bits/4, verb)
- io.write_rune(fi.writer, 'i')
+ io.write_rune(fi.writer, 'i', &fi.n)
if !fi.plus && j >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, j, bits/4, verb)
- io.write_rune(fi.writer, 'j')
+ io.write_rune(fi.writer, 'j', &fi.n)
if !fi.plus && k >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, k, bits/4, verb)
- io.write_rune(fi.writer, 'k')
+ io.write_rune(fi.writer, 'k', &fi.n)
case:
fmt_bad_verb(fi, verb)
@@ -2024,7 +2186,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
switch a in arg {
case ^runtime.Type_Info: ti = a
}
- reflect.write_type(fi.writer, ti)
+ reflect.write_type(fi.writer, ti, &fi.n)
return
}
@@ -2042,12 +2204,12 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
custom_types: switch a in arg {
case runtime.Source_Code_Location:
if fi.hash && verb == 'v' {
- io.write_string(fi.writer, a.file_path)
- io.write_byte(fi.writer, '(')
- io.write_i64(fi.writer, i64(a.line), 10)
- io.write_byte(fi.writer, ':')
- io.write_i64(fi.writer, i64(a.column), 10)
- io.write_byte(fi.writer, ')')
+ io.write_string(fi.writer, a.file_path, &fi.n)
+ io.write_byte(fi.writer, '(', &fi.n)
+ io.write_i64(fi.writer, i64(a.line), 10, &fi.n)
+ io.write_byte(fi.writer, ':', &fi.n)
+ io.write_i64(fi.writer, i64(a.column), 10, &fi.n)
+ io.write_byte(fi.writer, ')', &fi.n)
return
}
}
@@ -2076,9 +2238,11 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case f32be: fmt_float(fi, f64(a), 32, verb)
case f64be: fmt_float(fi, f64(a), 64, verb)
+ case complex32: fmt_complex(fi, complex128(a), 32, verb)
case complex64: fmt_complex(fi, complex128(a), 64, verb)
case complex128: fmt_complex(fi, a, 128, verb)
+ case quaternion64: fmt_quaternion(fi, quaternion256(a), 64, verb)
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb)
case quaternion256: fmt_quaternion(fi, a, 256, verb)
@@ -2097,7 +2261,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case string: fmt_string(fi, a, verb)
case cstring: fmt_cstring(fi, a, verb)
- case typeid: reflect.write_typeid(fi.writer, a)
+ case typeid: reflect.write_typeid(fi.writer, a, &fi.n)
case i16le: fmt_int(fi, u64(a), true, 16, verb)
case u16le: fmt_int(fi, u64(a), false, 16, verb)
diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin
index bcd9688a1..7a9876127 100644
--- a/core/fmt/fmt_js.odin
+++ b/core/fmt/fmt_js.odin
@@ -34,11 +34,16 @@ stderr := io.Writer{
},
}
-// print* procedures return the number of bytes written
+// print formats using the default print settings and writes to stdout
print :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
+// println formats using the default print settings and writes to stdout
println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to stdout
printf :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
+// eprint formats using the default print settings and writes to stderr
eprint :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }
diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin
index 7434d939d..f5c8d75bd 100644
--- a/core/fmt/fmt_os.odin
+++ b/core/fmt/fmt_os.odin
@@ -5,15 +5,18 @@ import "core:runtime"
import "core:os"
import "core:io"
+// fprint formats using the default print settings and writes to fd
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprint(w=w, args=args, sep=sep)
}
+// fprintln formats using the default print settings and writes to fd
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintln(w=w, args=args, sep=sep)
}
+// fprintf formats according to the specififed format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args)
@@ -27,11 +30,16 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
return wprint_typeid(w, id)
}
-// print* procedures return the number of bytes written
+// print formats using the default print settings and writes to os.stdout
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
+// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
+// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
diff --git a/core/hash/crc.odin b/core/hash/crc.odin
index b504e92fb..9c0048a0f 100644
--- a/core/hash/crc.odin
+++ b/core/hash/crc.odin
@@ -1,8 +1,8 @@
package hash
@(optimization_mode="speed")
-crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
- result := u64(seed)
+crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
+ result = seed
#no_bounds_check for b in data {
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
}
diff --git a/core/hash/crc32.odin b/core/hash/crc32.odin
index ccb304472..fead4d74f 100644
--- a/core/hash/crc32.odin
+++ b/core/hash/crc32.odin
@@ -9,8 +9,8 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
length := len(data)
for length != 0 && uintptr(buffer) & 7 != 0 {
- crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
- buffer = intrinsics.ptr_offset(buffer, 1)
+ crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
+ buffer = buffer[1:]
length -= 1
}
@@ -28,14 +28,14 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc32_table[1][buf[6]] ~
crc32_table[0][buf[7]]
- buffer = intrinsics.ptr_offset(buffer, 8)
+ buffer = buffer[8:]
length -= 8
}
for length != 0 {
- crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
- buffer = intrinsics.ptr_offset(buffer, 1)
+ crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
+ buffer = buffer[1:]
length -= 1
}
diff --git a/core/hash/hash.odin b/core/hash/hash.odin
index 64c7ad5c4..870d6a638 100644
--- a/core/hash/hash.odin
+++ b/core/hash/hash.odin
@@ -15,7 +15,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
a = (a + u64(buf[0]))
b = (b + a)
- buffer = intrinsics.ptr_offset(buffer, 1)
+ buffer = buffer[1:]
buf = buf[1:]
}
@@ -47,17 +47,34 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
}
@(optimization_mode="speed")
-djb2 :: proc(data: []byte) -> u32 {
- hash: u32 = 5381
+djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
+ hash: u32 = seed
for b in data {
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
}
return hash
}
+djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
+ state := [4]u32{seed, seed, seed, seed}
+
+ s: u32 = 0
+ for p in data {
+ state[s] = (state[s] << 5) + state[s] + u32(p) // hash * 33 + u32(b)
+ s = (s + 1) & 3
+ }
+
+
+ (^u32le)(&result[0])^ = u32le(state[0])
+ (^u32le)(&result[4])^ = u32le(state[1])
+ (^u32le)(&result[8])^ = u32le(state[2])
+ (^u32le)(&result[12])^ = u32le(state[3])
+ return
+}
+
@(optimization_mode="speed")
-fnv32 :: proc(data: []byte) -> u32 {
- h: u32 = 0x811c9dc5
+fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
+ h: u32 = seed
for b in data {
h = (h * 0x01000193) ~ u32(b)
}
@@ -65,8 +82,8 @@ fnv32 :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
-fnv64 :: proc(data: []byte) -> u64 {
- h: u64 = 0xcbf29ce484222325
+fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
+ h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
}
@@ -74,8 +91,8 @@ fnv64 :: proc(data: []byte) -> u64 {
}
@(optimization_mode="speed")
-fnv32a :: proc(data: []byte) -> u32 {
- h: u32 = 0x811c9dc5
+fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
+ h: u32 = seed
for b in data {
h = (h ~ u32(b)) * 0x01000193
}
@@ -83,8 +100,8 @@ fnv32a :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
-fnv64a :: proc(data: []byte) -> u64 {
- h: u64 = 0xcbf29ce484222325
+fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
+ h: u64 = seed
for b in data {
h = (h ~ u64(b)) * 0x100000001b3
}
@@ -92,8 +109,8 @@ fnv64a :: proc(data: []byte) -> u64 {
}
@(optimization_mode="speed")
-jenkins :: proc(data: []byte) -> u32 {
- hash: u32 = 0
+jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
+ hash: u32 = seed
for b in data {
hash += u32(b)
hash += hash << 10
@@ -106,16 +123,16 @@ jenkins :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
-murmur32 :: proc(data: []byte) -> u32 {
+murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593
- h1: u32 = 0
+ h1: u32 = seed
nblocks := len(data)/4
p := raw_data(data)
- p1 := mem.ptr_offset(p, 4*nblocks)
+ p1 := p[4*nblocks:]
- for ; p < p1; p = mem.ptr_offset(p, 4) {
+ for ; p < p1; p = p[4:] {
k1 := (cast(^u32)p)^
k1 *= c1_32
@@ -134,7 +151,7 @@ murmur32 :: proc(data: []byte) -> u32 {
k1 ~= u32(tail[2]) << 16
fallthrough
case 2:
- k1 ~= u32(tail[2]) << 8
+ k1 ~= u32(tail[1]) << 8
fallthrough
case 1:
k1 ~= u32(tail[0])
@@ -155,115 +172,119 @@ murmur32 :: proc(data: []byte) -> u32 {
return h1
}
+// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed")
-murmur64 :: proc(data: []byte) -> u64 {
- SEED :: 0x9747b28c
+murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
+ m :: 0xc6a4a7935bd1e995
+ r :: 47
- when size_of(int) == 8 {
- m :: 0xc6a4a7935bd1e995
- r :: 47
+ h: u64 = seed ~ (u64(len(data)) * m)
+ data64 := mem.slice_data_cast([]u64, data)
- h: u64 = SEED ~ (u64(len(data)) * m)
- data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
+ for _, i in data64 {
+ k := data64[i]
- for _, i in data64 {
- k := data64[i]
+ k *= m
+ k ~= k>>r
+ k *= m
- k *= m
- k ~= k>>r
- k *= m
-
- h ~= k
- h *= m
- }
-
- switch len(data)&7 {
- case 7: h ~= u64(data[6]) << 48; fallthrough
- case 6: h ~= u64(data[5]) << 40; fallthrough
- case 5: h ~= u64(data[4]) << 32; fallthrough
- case 4: h ~= u64(data[3]) << 24; fallthrough
- case 3: h ~= u64(data[2]) << 16; fallthrough
- case 2: h ~= u64(data[1]) << 8; fallthrough
- case 1:
- h ~= u64(data[0])
- h *= m
- }
-
- h ~= h>>r
+ h ~= k
h *= m
- h ~= h>>r
-
- return h
- } else {
- m :: 0x5bd1e995
- r :: 24
-
- h1 := u32(SEED) ~ u32(len(data))
- h2 := u32(SEED) >> 32
- data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
- len := len(data)
- i := 0
-
- for len >= 8 {
- k1, k2: u32
- k1 = data32[i]; i += 1
- k1 *= m
- k1 ~= k1>>r
- k1 *= m
- h1 *= m
- h1 ~= k1
- len -= 4
-
- k2 = data32[i]; i += 1
- k2 *= m
- k2 ~= k2>>r
- k2 *= m
- h2 *= m
- h2 ~= k2
- len -= 4
- }
-
- if len >= 4 {
- k1: u32
- k1 = data32[i]; i += 1
- k1 *= m
- k1 ~= k1>>r
- k1 *= m
- h1 *= m
- h1 ~= k1
- len -= 4
- }
-
- // TODO(bill): Fix this
- #no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
- switch len {
- case 3:
- h2 ~= u32(data8[2]) << 16
- fallthrough
- case 2:
- h2 ~= u32(data8[1]) << 8
- fallthrough
- case 1:
- h2 ~= u32(data8[0])
- h2 *= m
- }
-
- h1 ~= h2>>18
- h1 *= m
- h2 ~= h1>>22
- h2 *= m
- h1 ~= h2>>17
- h1 *= m
- h2 ~= h1>>19
- h2 *= m
-
- return u64(h1)<<32 | u64(h2)
}
+
+ offset := len(data64) * size_of(u64)
+
+ switch len(data)&7 {
+ case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
+ case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
+ case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
+ case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
+ case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
+ case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
+ case 1:
+ h ~= u64(data[offset + 0])
+ h *= m
+ }
+
+ h ~= h>>r
+ h *= m
+ h ~= h>>r
+
+ return h
+}
+
+// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
+@(optimization_mode="speed")
+murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
+ m :: 0x5bd1e995
+ r :: 24
+
+ h1 := u32(seed) ~ u32(len(data))
+ h2 := u32(seed) >> 32
+
+ data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
+ len := len(data)
+ i := 0
+
+ for len >= 8 {
+ k1, k2: u32
+ k1 = data32[i]; i += 1
+ k1 *= m
+ k1 ~= k1>>r
+ k1 *= m
+ h1 *= m
+ h1 ~= k1
+ len -= 4
+
+ k2 = data32[i]; i += 1
+ k2 *= m
+ k2 ~= k2>>r
+ k2 *= m
+ h2 *= m
+ h2 ~= k2
+ len -= 4
+ }
+
+ if len >= 4 {
+ k1: u32
+ k1 = data32[i]; i += 1
+ k1 *= m
+ k1 ~= k1>>r
+ k1 *= m
+ h1 *= m
+ h1 ~= k1
+ len -= 4
+ }
+
+ // TODO(bill): Fix this
+ #no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
+ switch len {
+ case 3:
+ h2 ~= u32(data8[2]) << 16
+ fallthrough
+ case 2:
+ h2 ~= u32(data8[1]) << 8
+ fallthrough
+ case 1:
+ h2 ~= u32(data8[0])
+ h2 *= m
+ }
+
+ h1 ~= h2>>18
+ h1 *= m
+ h2 ~= h1>>22
+ h2 *= m
+ h1 ~= h2>>17
+ h1 *= m
+ h2 ~= h1>>19
+ h2 *= m
+
+ return u64(h1)<<32 | u64(h2)
}
@(optimization_mode="speed")
-sdbm :: proc(data: []byte) -> u32 {
- hash: u32 = 0
+sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
+ hash: u32 = seed
for b in data {
hash = u32(b) + (hash<<6) + (hash<<16) - hash
}
diff --git a/core/hash/xxhash/streaming.odin b/core/hash/xxhash/streaming.odin
index 737e37eae..6f630b042 100644
--- a/core/hash/xxhash/streaming.odin
+++ b/core/hash/xxhash/streaming.odin
@@ -52,9 +52,6 @@ XXH3_128_reset_with_seed :: proc(state: ^XXH3_state, seed: XXH64_hash) -> (err:
XXH3_64_reset_with_seed :: XXH3_128_reset_with_seed
XXH3_128_update :: proc(state: ^XXH3_state, input: []u8) -> (err: Error) {
- if len(input) < XXH3_MIDSIZE_MAX {
- return .Error
- }
return XXH3_update(state, input, XXH3_accumulate_512, XXH3_scramble_accumulator)
}
XXH3_64_update :: XXH3_128_update
@@ -96,7 +93,7 @@ XXH3_128_canonical_from_hash :: proc(hash: XXH128_hash_t) -> (canonical: XXH128_
#assert(size_of(XXH128_canonical) == size_of(XXH128_hash_t))
t := hash
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
t.high = byte_swap(t.high)
t.low = byte_swap(t.low)
}
@@ -127,6 +124,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
err = nil if mem_error == nil else .Error
XXH3_init_state(state)
+ XXH3_128_reset(state)
return state, nil
}
@@ -213,7 +211,9 @@ XXH3_update :: #force_inline proc(
length := len(input)
secret := state.custom_secret[:] if len(state.external_secret) == 0 else state.external_secret[:]
- assert(len(input) > 0)
+ if len(input) == 0 {
+ return
+ }
state.total_length += u64(length)
assert(state.buffered_size <= XXH3_INTERNAL_BUFFER_SIZE)
@@ -234,7 +234,9 @@ XXH3_update :: #force_inline proc(
*/
if state.buffered_size > 0 {
load_size := int(XXH3_INTERNAL_BUFFER_SIZE - state.buffered_size)
- mem_copy(&state.buffer[state.buffered_size], &input[0], load_size)
+
+ state_ptr := rawptr(uintptr(raw_data(state.buffer[:])) + uintptr(state.buffered_size))
+ mem_copy(state_ptr, raw_data(input), load_size)
input = input[load_size:]
XXH3_consume_stripes(
diff --git a/core/hash/xxhash/xxhash_32.odin b/core/hash/xxhash/xxhash_32.odin
index e63d998dd..5bc87c2c0 100644
--- a/core/hash/xxhash/xxhash_32.odin
+++ b/core/hash/xxhash/xxhash_32.odin
@@ -197,6 +197,7 @@ XXH32 :: proc(input: []u8, seed := XXH32_DEFAULT_SEED) -> (digest: XXH32_hash) {
*/
XXH32_create_state :: proc(allocator := context.allocator) -> (res: ^XXH32_state, err: Error) {
state := new(XXH32_state, allocator)
+ XXH32_reset_state(state)
return state, .None if state != nil else .Error
}
@@ -258,7 +259,7 @@ XXH32_update :: proc(state: ^XXH32_state, input: []u8) -> (err: Error) {
v3 := state.v3
v4 := state.v4
- for len(buf) >= 15 {
+ for len(buf) >= 16 {
#no_bounds_check v1 = XXH32_round(v1, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
#no_bounds_check v2 = XXH32_round(v2, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
#no_bounds_check v3 = XXH32_round(v3, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
diff --git a/core/hash/xxhash/xxhash_64.odin b/core/hash/xxhash/xxhash_64.odin
index e95842168..9280e9c59 100644
--- a/core/hash/xxhash/xxhash_64.odin
+++ b/core/hash/xxhash/xxhash_64.odin
@@ -163,6 +163,7 @@ XXH64 :: proc(input: []u8, seed := XXH64_DEFAULT_SEED) -> (digest: XXH64_hash) {
*/
XXH64_create_state :: proc(allocator := context.allocator) -> (res: ^XXH64_state, err: Error) {
state := new(XXH64_state, allocator)
+ XXH64_reset_state(state)
return state, .None if state != nil else .Error
}
diff --git a/core/image/common.odin b/core/image/common.odin
index 3ec8e15be..baacd64d9 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -6,6 +6,8 @@
Jeroen van Rijn: Initial implementation, optimization.
Ginger Bill: Cosmetic changes.
*/
+
+// package image implements a general 2D image library to be used with other image related packages
package image
import "core:bytes"
@@ -13,26 +15,56 @@ import "core:mem"
import "core:compress"
import "core:runtime"
+/*
+ 67_108_864 pixels max by default.
+
+ For QOI, the Worst case scenario means all pixels will be encoded as RGBA literals, costing 5 bytes each.
+ This caps memory usage at 320 MiB.
+
+ The tunable is limited to 4_294_836_225 pixels maximum, or 4 GiB per 8-bit channel.
+ It is not advised to tune it this large.
+
+ The 64 Megapixel default is considered to be a decent upper bound you won't run into in practice,
+ except in very specific circumstances.
+
+*/
+MAX_DIMENSIONS :: min(#config(MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+
+// Color
+RGB_Pixel :: [3]u8
+RGBA_Pixel :: [4]u8
+RGB_Pixel_16 :: [3]u16
+RGBA_Pixel_16 :: [4]u16
+// Grayscale
+G_Pixel :: [1]u8
+GA_Pixel :: [2]u8
+G_Pixel_16 :: [1]u16
+GA_Pixel_16 :: [2]u16
+
Image :: struct {
width: int,
height: int,
channels: int,
- depth: int,
+ depth: int, // Channel depth in bits, typically 8 or 16
pixels: bytes.Buffer,
/*
Some image loaders/writers can return/take an optional background color.
For convenience, we return them as u16 so we don't need to switch on the type
in our viewer, and can just test against nil.
*/
- background: Maybe([3]u16),
-
+ background: Maybe(RGB_Pixel_16),
metadata: Image_Metadata,
+ which: Which_File_Type,
}
-Image_Metadata :: union {
+Image_Metadata :: union #shared_nil {
+ ^Netpbm_Info,
^PNG_Info,
+ ^QOI_Info,
}
+
+
/*
IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options,
therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`,
@@ -44,13 +76,13 @@ Image_Metadata :: union {
/*
Image_Option:
`.info`
- This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used
+ This option behaves as `.return_metadata` and `.do_not_decompress_image` and can be used
to gather an image's dimensions and color information.
`.return_header`
- Fill out img.sidecar.header with the image's format-specific header struct.
+ Fill out img.metadata.header with the image's format-specific header struct.
If we only care about the image specs, we can set `.return_header` +
- `.do_not_decompress_image`, or `.info`, which works as if both of these were set.
+ `.do_not_decompress_image`, or `.info`.
`.return_metadata`
Returns all chunks not needed to decode the data.
@@ -86,7 +118,7 @@ Image_Option:
`.alpha_premultiply`
If the image has an alpha channel, returns image data as follows:
- RGB *= A, Gray = Gray *= A
+ RGB *= A, Gray = Gray *= A
`.blend_background`
If a bKGD chunk is present in a PNG, we normally just set `img.background`
@@ -101,24 +133,31 @@ Image_Option:
*/
Option :: enum {
+ // LOAD OPTIONS
info = 0,
do_not_decompress_image,
return_header,
return_metadata,
- alpha_add_if_missing,
- alpha_drop_if_present,
- alpha_premultiply,
- blend_background,
+ alpha_add_if_missing, // Ignored for QOI. Always returns RGBA8.
+ alpha_drop_if_present, // Unimplemented for QOI. Returns error.
+ alpha_premultiply, // Unimplemented for QOI. Returns error.
+ blend_background, // Ignored for non-PNG formats
+
// Unimplemented
do_not_expand_grayscale,
do_not_expand_indexed,
do_not_expand_channels,
+
+ // SAVE OPTIONS
+ qoi_all_channels_linear, // QOI, informative only. If not set, defaults to sRGB with linear alpha.
}
Options :: distinct bit_set[Option]
-Error :: union {
+Error :: union #shared_nil {
General_Image_Error,
+ Netpbm_Error,
PNG_Error,
+ QOI_Error,
compress.Error,
compress.General_Error,
@@ -129,13 +168,76 @@ Error :: union {
General_Image_Error :: enum {
None = 0,
- Invalid_Image_Dimensions,
+ // File I/O
+ Unable_To_Read_File,
+ Unable_To_Write_File,
+
+ // Invalid
+ Unsupported_Format,
+ Invalid_Signature,
+ Invalid_Input_Image,
Image_Dimensions_Too_Large,
+ Invalid_Image_Dimensions,
+ Invalid_Number_Of_Channels,
Image_Does_Not_Adhere_to_Spec,
+ Invalid_Image_Depth,
+ Invalid_Bit_Depth,
+ Invalid_Color_Space,
+
+ // More data than pixels to decode into, for example.
+ Corrupt,
+
+ // Output buffer is the wrong size
+ Invalid_Output,
+
+ // Allocation
+ Unable_To_Allocate_Or_Resize,
}
+/*
+ Netpbm-specific definitions
+*/
+Netpbm_Format :: enum {
+ P1, P2, P3, P4, P5, P6, P7, Pf, PF,
+}
+
+Netpbm_Header :: struct {
+ format: Netpbm_Format,
+ width: int,
+ height: int,
+ channels: int,
+ depth: int,
+ maxval: int,
+ tupltype: string,
+ scale: f32,
+ little_endian: bool,
+}
+
+Netpbm_Info :: struct {
+ header: Netpbm_Header,
+}
+
+Netpbm_Error :: enum {
+ None = 0,
+
+ // reading
+ Invalid_Header_Token_Character,
+ Incomplete_Header,
+ Invalid_Header_Value,
+ Duplicate_Header_Field,
+ Buffer_Too_Small,
+ Invalid_Buffer_ASCII_Token,
+ Invalid_Buffer_Value,
+
+ // writing
+ Invalid_Format,
+}
+
+/*
+ PNG-specific definitions
+*/
PNG_Error :: enum {
- Invalid_PNG_Signature,
+ None = 0,
IHDR_Not_First_Chunk,
IHDR_Corrupt,
IDAT_Missing,
@@ -144,7 +246,9 @@ PNG_Error :: enum {
IDAT_Size_Too_Large,
PLTE_Encountered_Unexpectedly,
PLTE_Invalid_Length,
+ PLTE_Missing,
TRNS_Encountered_Unexpectedly,
+ TNRS_Invalid_Length,
BKGD_Invalid_Length,
Unknown_Color_Type,
Invalid_Color_Bit_Depth_Combo,
@@ -155,9 +259,6 @@ PNG_Error :: enum {
Invalid_Chunk_Length,
}
-/*
- PNG-specific structs
-*/
PNG_Info :: struct {
header: PNG_IHDR,
chunks: [dynamic]PNG_Chunk,
@@ -220,7 +321,7 @@ PNG_Chunk_Type :: enum u32be {
*/
iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
- CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
+ CgBI = 'C' << 24 | 'g' << 16 | 'B' << 8 | 'I',
}
PNG_IHDR :: struct #packed {
@@ -248,16 +349,53 @@ PNG_Interlace_Method :: enum u8 {
}
/*
- Functions to help with image buffer calculations
+ QOI-specific definitions
*/
+QOI_Error :: enum {
+ None = 0,
+ Missing_Or_Corrupt_Trailer, // Image seemed to have decoded okay, but trailer is missing or corrupt.
+}
+
+QOI_Magic :: u32be(0x716f6966) // "qoif"
+
+QOI_Color_Space :: enum u8 {
+ sRGB = 0,
+ Linear = 1,
+}
+
+QOI_Header :: struct #packed {
+ magic: u32be,
+ width: u32be,
+ height: u32be,
+ channels: u8,
+ color_space: QOI_Color_Space,
+}
+#assert(size_of(QOI_Header) == 14)
+
+QOI_Info :: struct {
+ header: QOI_Header,
+}
+
+TGA_Header :: struct #packed {
+ id_length: u8,
+ color_map_type: u8,
+ data_type_code: u8,
+ color_map_origin: u16le,
+ color_map_length: u16le,
+ color_map_depth: u8,
+ origin: [2]u16le,
+ dimensions: [2]u16le,
+ bits_per_pixel: u8,
+ image_descriptor: u8,
+}
+#assert(size_of(TGA_Header) == 18)
+
+// Function to help with image buffer calculations
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
return
}
-/*
- For when you have an RGB(A) image, but want a particular channel.
-*/
Channel :: enum u8 {
R = 1,
G = 2,
@@ -265,7 +403,13 @@ Channel :: enum u8 {
A = 4,
}
+// When you have an RGB(A) image, but want a particular channel.
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return nil, false
+ }
+
ok = false
t: bytes.Buffer
@@ -295,7 +439,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
o = o[1:]
}
case 16:
- buffer_size := compute_buffer_size(img.width, img.height, 2, 8)
+ buffer_size := compute_buffer_size(img.width, img.height, 1, 16)
t = bytes.Buffer{}
resize(&t.buf, buffer_size)
@@ -323,3 +467,724 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
return res, true
}
+
+// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ // Are we a Gray or Gray + Alpha image?
+ if img.channels != 1 && img.channels != 2 {
+ return false
+ }
+
+ // Do we have an acceptable bit depth?
+ if img.depth != 8 && img.depth != 16 {
+ return false
+ }
+
+ // This returns 0 if any of the inputs is zero.
+ bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
+
+ // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ return true
+}
+
+// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ // Are we an RGB or RGBA image?
+ if img.channels != 3 && img.channels != 4 {
+ return false
+ }
+
+ // Do we have an acceptable bit depth?
+ if img.depth != 8 && img.depth != 16 {
+ return false
+ }
+
+ // This returns 0 if any of the inputs is zero.
+ bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
+
+ // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ return true
+}
+
+// Does the image have 1..4 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ return is_valid_color_image(img) || is_valid_grayscale_image(img)
+}
+
+Alpha_Key :: union {
+ GA_Pixel,
+ RGBA_Pixel,
+ GA_Pixel_16,
+ RGBA_Pixel_16,
+}
+
+/*
+ Add alpha channel if missing, in-place.
+
+ Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
+ Any other number of channels will be considered an error, returning `false` without modifying the image.
+ If the input image already has an alpha channel, it'll return `true` early (without considering optional keyed alpha).
+
+ If an image doesn't already have an alpha channel:
+ If the optional `alpha_key` is provided, it will be resolved as follows:
+ - For RGB, if pix = key.rgb -> pix = {0, 0, 0, key.a}
+ - For Gray, if pix = key.r -> pix = {0, key.g}
+ Otherwise, an opaque alpha channel will be added.
+*/
+alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_image(img) {
+ return false
+ }
+
+ // We should now have a valid Image with 1..4 channels. Do we already have alpha?
+ if img.channels == 2 || img.channels == 4 {
+ // We're done.
+ return true
+ }
+
+ channels := img.channels + 1
+ bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth)
+
+ buf := bytes.Buffer{}
+
+ // Can we allocate the return buffer?
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch channels {
+ case 2:
+ // Turn Gray into Gray + Alpha
+ inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]GA_Pixel, buf.buf[:])
+
+ if key, key_ok := alpha_key.(GA_Pixel); key_ok {
+ // We have keyed alpha.
+ o: GA_Pixel
+ for p in inp {
+ if p == key.r {
+ o = GA_Pixel{0, key.g}
+ } else {
+ o = GA_Pixel{p.r, 255}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := GA_Pixel{0, 255}
+ for p in inp {
+ o.r = p.r
+ out[0] = o
+ out = out[1:]
+ }
+ }
+
+ case 4:
+ // Turn RGB into RGBA
+ inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ if key, key_ok := alpha_key.(RGBA_Pixel); key_ok {
+ // We have keyed alpha.
+ o: RGBA_Pixel
+ for p in inp {
+ if p == key.rgb {
+ o = RGBA_Pixel{0, 0, 0, key.a}
+ } else {
+ o = RGBA_Pixel{p.r, p.g, p.b, 255}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := RGBA_Pixel{0, 0, 0, 255}
+ for p in inp {
+ o.rgb = p
+ out[0] = o
+ out = out[1:]
+ }
+ }
+ case:
+ // We shouldn't get here.
+ unreachable()
+ }
+ case 16:
+ switch channels {
+ case 2:
+ // Turn Gray into Gray + Alpha
+ inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]GA_Pixel_16, buf.buf[:])
+
+ if key, key_ok := alpha_key.(GA_Pixel_16); key_ok {
+ // We have keyed alpha.
+ o: GA_Pixel_16
+ for p in inp {
+ if p == key.r {
+ o = GA_Pixel_16{0, key.g}
+ } else {
+ o = GA_Pixel_16{p.r, 65535}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := GA_Pixel_16{0, 65535}
+ for p in inp {
+ o.r = p.r
+ out[0] = o
+ out = out[1:]
+ }
+ }
+
+ case 4:
+ // Turn RGB into RGBA
+ inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
+
+ if key, key_ok := alpha_key.(RGBA_Pixel_16); key_ok {
+ // We have keyed alpha.
+ o: RGBA_Pixel_16
+ for p in inp {
+ if p == key.rgb {
+ o = RGBA_Pixel_16{0, 0, 0, key.a}
+ } else {
+ o = RGBA_Pixel_16{p.r, p.g, p.b, 65535}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := RGBA_Pixel_16{0, 0, 0, 65535}
+ for p in inp {
+ o.rgb = p
+ out[0] = o
+ out = out[1:]
+ }
+ }
+ case:
+ // We shouldn't get here.
+ unreachable()
+ }
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel added.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = channels
+ return true
+}
+alpha_apply_keyed_alpha :: alpha_add_if_missing
+
+/*
+ Drop alpha channel if present, in-place.
+
+ Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
+ Any other number of channels will be considered an error, returning `false` without modifying the image.
+
+ Of the `options`, the following are considered:
+ `.alpha_premultiply`
+ If the image has an alpha channel, returns image data as follows:
+ RGB *= A, Gray = Gray *= A
+
+ `.blend_background`
+ If `img.background` is set, it'll be blended in like this:
+ RGB = (1 - A) * Background + A * RGB
+
+ If an image has 1 (Gray) or 3 (RGB) channels, it'll return early without modifying the image,
+ with one exception: `alpha_key` and `img.background` are present, and `.blend_background` is set.
+
+ In this case a keyed alpha pixel will be replaced with the background color.
+*/
+alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_image(img) {
+ return false
+ }
+
+ // Do we have a background to blend?
+ will_it_blend := false
+ switch v in img.background {
+ case RGB_Pixel_16: will_it_blend = true if .blend_background in options else false
+ }
+
+ // Do we have keyed alpha?
+ keyed := false
+ switch v in alpha_key {
+ case GA_Pixel: keyed = true if img.channels == 1 && img.depth == 8 else false
+ case RGBA_Pixel: keyed = true if img.channels == 3 && img.depth == 8 else false
+ case GA_Pixel_16: keyed = true if img.channels == 1 && img.depth == 16 else false
+ case RGBA_Pixel_16: keyed = true if img.channels == 3 && img.depth == 16 else false
+ }
+
+ // We should now have a valid Image with 1..4 channels. Do we have alpha?
+ if img.channels == 1 || img.channels == 3 {
+ if !(will_it_blend && keyed) {
+ // We're done
+ return true
+ }
+ }
+
+ // # of destination channels
+ channels := 1 if img.channels < 3 else 3
+
+ bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth)
+ buf := bytes.Buffer{}
+
+ // Can we allocate the return buffer?
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch img.channels {
+ case 1: // Gray to Gray, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel, buf.buf[:])
+
+ key := alpha_key.(GA_Pixel).r
+ bg := G_Pixel{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = u8(temp_bg.r >> 8)
+ }
+
+ for p in inp {
+ out[0] = bg if p == key else p
+ out = out[1:]
+ }
+
+ case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background.
+ inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := f32(0.0)
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = f32(temp_bg.r >> 8)
+ }
+
+ for p in inp {
+ a := f32(p.g) / 255.0
+ c := ((1.0 - a) * bg + a * f32(p.r))
+ out[0] = u8(c)
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.g) / 255.0
+ c := f32(p.r) * a
+ out[0] = u8(c)
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.r
+ out = out[1:]
+ }
+ }
+
+ case 3: // RGB to RGB, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ key := alpha_key.(RGBA_Pixel)
+ bg := RGB_Pixel{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, squash down to 8 bits.
+ bg = {u8(temp_bg.r >> 8), u8(temp_bg.g >> 8), u8(temp_bg.b >> 8)}
+ }
+
+ for p in inp {
+ out[0] = bg if p == key.rgb else p
+ out = out[1:]
+ }
+
+ case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply.
+ inp := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := [3]f32{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = {f32(temp_bg.r >> 8), f32(temp_bg.g >> 8), f32(temp_bg.b >> 8)}
+ }
+
+ for p in inp {
+ a := f32(p.a) / 255.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := ((1.0 - a) * bg + a * rgb)
+
+ out[0] = {u8(c.r), u8(c.g), u8(c.b)}
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.a) / 255.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := rgb * a
+
+ out[0] = {u8(c.r), u8(c.g), u8(c.b)}
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.rgb
+ out = out[1:]
+ }
+ }
+ }
+
+ case 16:
+ switch img.channels {
+ case 1: // Gray to Gray, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:])
+
+ key := alpha_key.(GA_Pixel_16).r
+ bg := G_Pixel_16{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel.
+ bg = temp_bg.r
+ }
+
+ for p in inp {
+ out[0] = bg if p == key else p
+ out = out[1:]
+ }
+
+ case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background.
+ inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := f32(0.0)
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel.
+ bg = f32(temp_bg.r)
+ }
+
+ for p in inp {
+ a := f32(p.g) / 65535.0
+ c := ((1.0 - a) * bg + a * f32(p.r))
+ out[0] = u16(c)
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.g) / 65535.0
+ c := f32(p.r) * a
+ out[0] = u16(c)
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.r
+ out = out[1:]
+ }
+ }
+
+ case 3: // RGB to RGB, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ key := alpha_key.(RGBA_Pixel_16)
+ bg := img.background.(RGB_Pixel_16)
+
+ for p in inp {
+ out[0] = bg if p == key.rgb else p
+ out = out[1:]
+ }
+
+ case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply.
+ inp := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := [3]f32{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, convert to [3]f32 to blend.
+ bg = {f32(temp_bg.r), f32(temp_bg.g), f32(temp_bg.b)}
+ }
+
+ for p in inp {
+ a := f32(p.a) / 65535.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := ((1.0 - a) * bg + a * rgb)
+
+ out[0] = {u16(c.r), u16(c.g), u16(c.b)}
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.a) / 65535.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := rgb * a
+
+ out[0] = {u16(c.r), u16(c.g), u16(c.b)}
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.rgb
+ out = out[1:]
+ }
+ }
+ }
+
+ case:
+ unreachable()
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = channels
+ return true
+}
+
+// Apply palette to 8-bit single-channel image and return an 8-bit RGB image, in-place.
+// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early.
+apply_palette_rgb :: proc(img: ^Image, palette: [256]RGB_Pixel, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if img == nil || img.channels != 1 || img.depth != 8 {
+ return false
+ }
+
+ bytes_expected := compute_buffer_size(img.width, img.height, 1, 8)
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ // Can we allocate the return buffer?
+ buf := bytes.Buffer{}
+ bytes_wanted := compute_buffer_size(img.width, img.height, 3, 8)
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ // Apply the palette
+ for p, i in img.pixels.buf {
+ out[i] = palette[p]
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = 3
+ return true
+}
+
+// Apply palette to 8-bit single-channel image and return an 8-bit RGBA image, in-place.
+// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early.
+apply_palette_rgba :: proc(img: ^Image, palette: [256]RGBA_Pixel, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if img == nil || img.channels != 1 || img.depth != 8 {
+ return false
+ }
+
+ bytes_expected := compute_buffer_size(img.width, img.height, 1, 8)
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ // Can we allocate the return buffer?
+ buf := bytes.Buffer{}
+ bytes_wanted := compute_buffer_size(img.width, img.height, 4, 8)
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ // Apply the palette
+ for p, i in img.pixels.buf {
+ out[i] = palette[p]
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = 4
+ return true
+}
+apply_palette :: proc{apply_palette_rgb, apply_palette_rgba}
+
+
+// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
+// Returns early with `false` if already an RGB(A) image.
+expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_grayscale_image(img) {
+ return false
+ }
+
+ // We should have 1 or 2 channels of 8- or 16 bits now. We need to turn that into 3 or 4.
+ // Can we allocate the return buffer?
+ buf := bytes.Buffer{}
+ bytes_wanted := compute_buffer_size(img.width, img.height, img.channels + 2, img.depth)
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch img.channels {
+ case 1: // Turn Gray into RGB
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ for p in img.pixels.buf {
+ out[0] = p // Broadcast gray value into RGB components.
+ out = out[1:]
+ }
+
+ case 2: // Turn Gray + Alpha into RGBA
+ inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ for p in inp {
+ out[0].rgb = p.r // Gray component.
+ out[0].a = p.g // Alpha component.
+ }
+
+ case:
+ unreachable()
+ }
+
+ case 16:
+ switch img.channels {
+ case 1: // Turn Gray into RGB
+ inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ for p in inp {
+ out[0] = p // Broadcast gray value into RGB components.
+ out = out[1:]
+ }
+
+ case 2: // Turn Gray + Alpha into RGBA
+ inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
+
+ for p in inp {
+ out[0].rgb = p.r // Gray component.
+ out[0].a = p.g // Alpha component.
+ }
+
+ case:
+ unreachable()
+ }
+
+ case:
+ unreachable()
+ }
+
+
+ // If we got here, that means we've now got a buffer with the extra alpha channel.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels += 2
+ return true
+}
+
+/*
+ Helper functions to read and write data from/to a Context, etc.
+*/
+@(optimization_mode="speed")
+read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
+ if r, e := compress.read_data(z, T); e != .None {
+ return {}, .Stream_Too_Short
+ } else {
+ return r, nil
+ }
+}
+
+@(optimization_mode="speed")
+read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
+ if r, e := compress.read_u8(z); e != .None {
+ return {}, .Stream_Too_Short
+ } else {
+ return r, nil
+ }
+}
+
+write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Error) {
+ if len(data) == 0 {
+ return nil
+ } else if len(data) == 1 {
+ if bytes.buffer_write_byte(buf, data[0]) != nil {
+ return .Resize_Failed
+ }
+ } else if n, _ := bytes.buffer_write(buf, data); n != len(data) {
+ return .Resize_Failed
+ }
+ return nil
+}
diff --git a/core/image/general_loader.odin b/core/image/general_loader.odin
new file mode 100644
index 000000000..36629c39e
--- /dev/null
+++ b/core/image/general_loader.odin
@@ -0,0 +1,61 @@
+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/netpbm/doc.odin b/core/image/netpbm/doc.odin
new file mode 100644
index 000000000..1b5b46856
--- /dev/null
+++ b/core/image/netpbm/doc.odin
@@ -0,0 +1,33 @@
+/*
+Formats:
+ PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
+ PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
+ PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
+ PAM (P7 ): Portable Arbitrary Map, stores arbitrary channel images (1 or 2 bytes per value)
+ PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel)
+
+Reading:
+ All formats fill out header fields `format`, `width`, `height`, `channels`, `depth`
+ Specific formats use more fields
+ PGM, PPM, and PAM set `maxval` (maximum of 65535)
+ PAM sets `tupltype` if there is one, and can set `channels` to any value (not just 1 or 3)
+ PFM sets `scale` (float equivalent of `maxval`) and `little_endian` (endianness of stored floats)
+ Currently doesn't support reading multiple images from one binary-format file
+
+Writing:
+ You can use your own `Netpbm_Info` struct to control how images are written
+ All formats require the header field `format` to be specified
+ Additional header fields are required for specific formats
+ PGM, PPM, and PAM require `maxval` (maximum of 65535)
+ PAM also uses `tupltype`, though it may be left as default (empty or nil string)
+ PFM requires `scale`, and optionally `little_endian`
+
+Some syntax differences from the specifications:
+ `channels` stores the number of values per pixel, what the PAM specification calls `depth`
+ `depth` instead is the number of bits for a single value (32 for PFM, 16 or 8 otherwise)
+ `scale` and `little_endian` are separated, so the `header` will always store a positive `scale`
+ `little_endian` will only be true for a negative `scale` PFM, every other format will be false
+ `little_endian` only describes the netpbm data being read/written, the image buffer will be native
+*/
+
+package netpbm
diff --git a/core/image/netpbm/helpers.odin b/core/image/netpbm/helpers.odin
new file mode 100644
index 000000000..016f9453e
--- /dev/null
+++ b/core/image/netpbm/helpers.odin
@@ -0,0 +1,27 @@
+package netpbm
+
+import "core:bytes"
+import "core:image"
+
+destroy :: proc(img: ^image.Image) -> bool {
+ if img == nil do return false
+
+ defer free(img)
+ bytes.buffer_destroy(&img.pixels)
+
+ info, ok := img.metadata.(^image.Netpbm_Info)
+ if !ok do return false
+
+ header_destroy(&info.header)
+ free(info)
+ img.metadata = nil
+
+ return true
+}
+
+header_destroy :: proc(using header: ^Header) {
+ if format == .P7 && tupltype != "" {
+ delete(tupltype)
+ tupltype = ""
+ }
+}
diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin
new file mode 100644
index 000000000..18545092d
--- /dev/null
+++ b/core/image/netpbm/netpbm.odin
@@ -0,0 +1,763 @@
+package netpbm
+
+import "core:bytes"
+import "core:fmt"
+import "core:image"
+import "core:mem"
+import "core:os"
+import "core:strconv"
+import "core:strings"
+import "core:unicode"
+
+Image :: image.Image
+Format :: image.Netpbm_Format
+Header :: image.Netpbm_Header
+Info :: image.Netpbm_Info
+Error :: image.Error
+Format_Error :: image.Netpbm_Error
+
+Formats :: bit_set[Format]
+PBM :: Formats{.P1, .P4}
+PGM :: Formats{.P2, .P5}
+PPM :: Formats{.P3, .P6}
+PNM :: PBM + PGM + PPM
+PAM :: Formats{.P7}
+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
+
+ img = new(Image)
+ img.which = .NetPBM
+
+ header: Header; defer header_destroy(&header)
+ header_size: int
+ header, header_size = parse_header(data) or_return
+
+ img_data := data[header_size:]
+ decode_image(img, header, img_data) or_return
+
+ info := new(Info)
+ info.header = header
+ if header.format == .P7 && header.tupltype != "" {
+ info.header.tupltype = strings.clone(header.tupltype)
+ }
+ img.metadata = info
+
+ 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
+
+ info: Info = {}
+ if custom_info.header.width > 0 {
+ // Custom info has been set, use it.
+ info = custom_info
+ } else {
+ img_info, ok := img.metadata.(^image.Netpbm_Info)
+ if !ok {
+ // image doesn't have .Netpbm info, guess it
+ auto_info, auto_info_found := autoselect_pbm_format_from_image(img)
+ if auto_info_found {
+ info = auto_info
+ } else {
+ return {}, .Invalid_Input_Image
+ }
+ } else {
+ // use info as stored on image
+ info = img_info^
+ }
+ }
+
+ // using info so we can just talk about the header
+ using info
+
+ // validation
+ if header.format in (PBM + PGM + Formats{.Pf}) && img.channels != 1 \
+ || header.format in (PPM + Formats{.PF}) && img.channels != 3 {
+ err = .Invalid_Number_Of_Channels
+ return
+ }
+
+ if header.format in (PNM + PAM) {
+ if header.maxval <= int(max(u8)) && img.depth != 8 \
+ || header.maxval > int(max(u8)) && header.maxval <= int(max(u16)) && img.depth != 16 {
+ err = .Invalid_Image_Depth
+ return
+ }
+ } else if header.format in PFM && img.depth != 32 {
+ err = .Invalid_Image_Depth
+ return
+ }
+
+ // we will write to a string builder
+ data: strings.Builder
+ strings.builder_init(&data)
+
+ // all PNM headers start with the format
+ fmt.sbprintf(&data, "%s\n", header.format)
+ if header.format in PNM {
+ fmt.sbprintf(&data, "%i %i\n", img.width, img.height)
+ if header.format not_in PBM {
+ fmt.sbprintf(&data, "%i\n", header.maxval)
+ }
+ } else if header.format in PAM {
+ if len(header.tupltype) > 0 {
+ fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nTUPLTYPE %s\nENDHDR\n",
+ img.width, img.height, header.maxval, img.channels, header.tupltype)
+ } else {
+ fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nENDHDR\n",
+ img.width, img.height, header.maxval, img.channels)
+ }
+
+ } else if header.format in PFM {
+ scale := -header.scale if header.little_endian else header.scale
+ fmt.sbprintf(&data, "%i %i\n%f\n", img.width, img.height, scale)
+ }
+
+ switch header.format {
+ // Compressed binary
+ case .P4:
+ header_buf := data.buf[:]
+ pixels := img.pixels.buf[:]
+
+ p4_buffer_size := (img.width / 8 + 1) * img.height
+ reserve(&data.buf, len(header_buf) + p4_buffer_size)
+
+ // we build up a byte value until it is completely filled
+ // or we reach the end the row
+ for y in 0 ..< img.height {
+ b: byte
+
+ for x in 0 ..< img.width {
+ i := y * img.width + x
+ bit := byte(7 - (x % 8))
+ v : byte = 0 if pixels[i] == 0 else 1
+ b |= (v << bit)
+
+ if bit == 0 {
+ append(&data.buf, b)
+ b = 0
+ }
+ }
+
+ if b != 0 {
+ append(&data.buf, b)
+ b = 0
+ }
+ }
+
+ // Simple binary
+ case .P5, .P6, .P7, .Pf, .PF:
+ header_buf := data.buf[:]
+ pixels := img.pixels.buf[:]
+
+ resize(&data.buf, len(header_buf) + len(pixels))
+ mem.copy(raw_data(data.buf[len(header_buf):]), raw_data(pixels), len(pixels))
+
+ // convert from native endianness
+ if img.depth == 16 {
+ pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):])
+ for p in &pixels {
+ p = u16be(transmute(u16) p)
+ }
+ } else if header.format in PFM {
+ if header.little_endian {
+ pixels := mem.slice_data_cast([]f32le, data.buf[len(header_buf):])
+ for p in &pixels {
+ p = f32le(transmute(f32) p)
+ }
+ } else {
+ pixels := mem.slice_data_cast([]f32be, data.buf[len(header_buf):])
+ for p in &pixels {
+ p = f32be(transmute(f32) p)
+ }
+ }
+ }
+
+ // If-it-looks-like-a-bitmap ASCII
+ case .P1:
+ pixels := img.pixels.buf[:]
+ for y in 0 ..< img.height {
+ for x in 0 ..< img.width {
+ i := y * img.width + x
+ append(&data.buf, '0' if pixels[i] == 0 else '1')
+ }
+ append(&data.buf, '\n')
+ }
+
+ // Token ASCII
+ case .P2, .P3:
+ switch img.depth {
+ case 8:
+ pixels := img.pixels.buf[:]
+ for y in 0 ..< img.height {
+ for x in 0 ..< img.width {
+ i := y * img.width + x
+ for c in 0 ..< img.channels {
+ i := i * img.channels + c
+ fmt.sbprintf(&data, "%i ", pixels[i])
+ }
+ fmt.sbprint(&data, "\n")
+ }
+ fmt.sbprint(&data, "\n")
+ }
+
+ case 16:
+ pixels := mem.slice_data_cast([]u16, img.pixels.buf[:])
+ for y in 0 ..< img.height {
+ for x in 0 ..< img.width {
+ i := y * img.width + x
+ for c in 0 ..< img.channels {
+ i := i * img.channels + c
+ fmt.sbprintf(&data, "%i ", pixels[i])
+ }
+ fmt.sbprint(&data, "\n")
+ }
+ fmt.sbprint(&data, "\n")
+ }
+
+ case:
+ return data.buf[:], .Invalid_Image_Depth
+ }
+
+ case:
+ return data.buf[:], .Invalid_Format
+ }
+
+ return data.buf[:], Format_Error.None
+}
+
+parse_header :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) {
+ context.allocator = allocator
+
+ // we need the signature and a space
+ if len(data) < 3 {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+
+ if data[0] == 'P' {
+ switch data[1] {
+ case '1' ..= '6':
+ return _parse_header_pnm(data)
+ case '7':
+ return _parse_header_pam(data, allocator)
+ case 'F', 'f':
+ return _parse_header_pfm(data)
+ }
+ }
+
+ err = .Invalid_Signature
+ return
+}
+
+@(private)
+_parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Error) {
+ SIG_LENGTH :: 2
+
+ {
+ header_formats := []Format{.P1, .P2, .P3, .P4, .P5, .P6}
+ header.format = header_formats[data[1] - '0' - 1]
+ }
+
+ // have a list of fielda for easy iteration
+ header_fields: []^int
+ if header.format in PBM {
+ header_fields = {&header.width, &header.height}
+ header.maxval = 1 // we know maxval for a bitmap
+ } else {
+ header_fields = {&header.width, &header.height, &header.maxval}
+ }
+
+ // we're keeping track of the header byte length
+ length = SIG_LENGTH
+
+ // loop state
+ in_comment := false
+ already_in_space := true
+ current_field := 0
+ current_value := header_fields[0]
+
+ parse_loop: for d, i in data[SIG_LENGTH:] {
+ length += 1
+
+ // handle comments
+ if in_comment {
+ switch d {
+ // comments only go up to next carriage return or line feed
+ case '\r', '\n':
+ in_comment = false
+ }
+ continue
+ } else if d == '#' {
+ in_comment = true
+ continue
+ }
+
+ // handle whitespace
+ in_space := unicode.is_white_space(rune(d))
+ if in_space {
+ if already_in_space {
+ continue
+ }
+ already_in_space = true
+
+ // switch to next value
+ current_field += 1
+ if current_field == len(header_fields) {
+ // header byte length is 1-index so we'll increment again
+ length += 1
+ break parse_loop
+ }
+ current_value = header_fields[current_field]
+ } else {
+ already_in_space = false
+
+ if !unicode.is_digit(rune(d)) {
+ err = Format_Error.Invalid_Header_Token_Character
+ return
+ }
+
+ val := int(d - '0')
+ current_value^ = current_value^ * 10 + val
+ }
+ }
+
+ // set extra info
+ header.channels = 3 if header.format in PPM else 1
+ header.depth = 16 if header.maxval > int(max(u8)) else 8
+
+ // limit checking
+ if current_field < len(header_fields) {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+
+ if header.width < 1 \
+ || header.height < 1 \
+ || header.maxval < 1 || header.maxval > int(max(u16)) {
+ fmt.printf("[pnm] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval)
+ err = .Invalid_Header_Value
+ return
+ }
+
+ length -= 1
+ err = Format_Error.None
+ return
+}
+
+@(private)
+_parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) {
+ context.allocator = allocator
+
+ // the spec needs the newline apparently
+ if string(data[0:3]) != "P7\n" {
+ err = .Invalid_Signature
+ return
+ }
+ header.format = .P7
+
+ SIGNATURE_LENGTH :: 3
+ HEADER_END :: "ENDHDR\n"
+
+ // we can already work out the size of the header
+ header_end_index := strings.index(string(data), HEADER_END)
+ if header_end_index == -1 {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+ length = header_end_index + len(HEADER_END)
+
+ // string buffer for the tupltype
+ tupltype: strings.Builder
+ strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
+ fmt.sbprint(&tupltype, "")
+
+ // PAM uses actual lines, so we can iterate easily
+ line_iterator := string(data[SIGNATURE_LENGTH : header_end_index])
+ parse_loop: for line in strings.split_lines_iterator(&line_iterator) {
+ line := line
+
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+
+ field, ok := strings.fields_iterator(&line)
+ value := strings.trim_space(line)
+
+ // the field will change, but the logic stays the same
+ current_field: ^int
+
+ switch field {
+ case "WIDTH": current_field = &header.width
+ case "HEIGHT": current_field = &header.height
+ case "DEPTH": current_field = &header.channels
+ case "MAXVAL": current_field = &header.maxval
+
+ case "TUPLTYPE":
+ if len(value) == 0 {
+ err = .Invalid_Header_Value
+ return
+ }
+
+ if len(tupltype.buf) == 0 {
+ fmt.sbprint(&tupltype, value)
+ } else {
+ fmt.sbprint(&tupltype, "", value)
+ }
+
+ continue
+
+ case:
+ continue
+ }
+
+ if current_field^ != 0 {
+ err = Format_Error.Duplicate_Header_Field
+ return
+ }
+ current_field^, ok = strconv.parse_int(value)
+ if !ok {
+ err = Format_Error.Invalid_Header_Value
+ return
+ }
+ }
+
+ // extra info
+ header.depth = 16 if header.maxval > int(max(u8)) else 8
+
+ // limit checking
+ if header.width < 1 \
+ || header.height < 1 \
+ || header.maxval < 1 \
+ || header.maxval > int(max(u16)) {
+ fmt.printf("[pam] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval)
+ err = Format_Error.Invalid_Header_Value
+ return
+ }
+
+ header.tupltype = strings.clone(strings.to_string(tupltype))
+ err = Format_Error.None
+ return
+}
+
+@(private)
+_parse_header_pfm :: proc(data: []byte) -> (header: Header, length: int, err: Error) {
+ // we can just cycle through tokens for PFM
+ field_iterator := string(data)
+ field, ok := strings.fields_iterator(&field_iterator)
+
+ switch field {
+ case "Pf":
+ header.format = .Pf
+ header.channels = 1
+ case "PF":
+ header.format = .PF
+ header.channels = 3
+ case:
+ err = .Invalid_Signature
+ return
+ }
+
+ // floating point
+ header.depth = 32
+
+ // width
+ field, ok = strings.fields_iterator(&field_iterator)
+ if !ok {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+ header.width, ok = strconv.parse_int(field)
+ if !ok {
+ err = Format_Error.Invalid_Header_Value
+ return
+ }
+
+ // height
+ field, ok = strings.fields_iterator(&field_iterator)
+ if !ok {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+ header.height, ok = strconv.parse_int(field)
+ if !ok {
+ err = Format_Error.Invalid_Header_Value
+ return
+ }
+
+ // scale (sign is endianness)
+ field, ok = strings.fields_iterator(&field_iterator)
+ if !ok {
+ err = Format_Error.Incomplete_Header
+ return
+ }
+ header.scale, ok = strconv.parse_f32(field)
+ if !ok {
+ err = Format_Error.Invalid_Header_Value
+ return
+ }
+
+ if header.scale < 0.0 {
+ header.little_endian = true
+ header.scale = -header.scale
+ }
+
+ // pointer math to get header size
+ length = int((uintptr(raw_data(field_iterator)) + 1) - uintptr(raw_data(data)))
+
+ // limit checking
+ if header.width < 1 \
+ || header.height < 1 \
+ || header.scale == 0.0 {
+ fmt.printf("[pfm] Header: {{width = %v, height = %v, scale: %v}}\n", header.width, header.height, header.scale)
+ err = .Invalid_Header_Value
+ return
+ }
+
+ err = Format_Error.None
+ return
+}
+
+decode_image :: proc(img: ^Image, header: Header, data: []byte, allocator := context.allocator) -> (err: Error) {
+ assert(img != nil)
+ context.allocator = allocator
+
+ img.width = header.width
+ img.height = header.height
+ img.channels = header.channels
+ img.depth = header.depth
+
+ buffer_size := image.compute_buffer_size(img.width, img.height, img.channels, img.depth)
+
+ // we can check data size for binary formats
+ if header.format in BINARY {
+ if len(data) < buffer_size {
+ fmt.printf("len(data): %v, buffer size: %v\n", len(data), buffer_size)
+ return .Buffer_Too_Small
+ }
+ }
+
+ // for ASCII and P4, we use length for the termination condition, so start at 0
+ // BINARY will be a simple memcopy so the buffer length should also be initialised
+ if header.format in ASCII || header.format == .P4 {
+ bytes.buffer_init_allocator(&img.pixels, 0, buffer_size)
+ } else {
+ bytes.buffer_init_allocator(&img.pixels, buffer_size, buffer_size)
+ }
+
+ switch header.format {
+ // Compressed binary
+ case .P4:
+ for d in data {
+ for b in 1 ..= 8 {
+ bit := byte(8 - b)
+ pix := (d >> bit) & 1
+ bytes.buffer_write_byte(&img.pixels, pix)
+ if len(img.pixels.buf) % img.width == 0 {
+ break
+ }
+ }
+
+ if len(img.pixels.buf) == cap(img.pixels.buf) {
+ break
+ }
+ }
+
+ // Simple binary
+ case .P5, .P6, .P7, .Pf, .PF:
+ copy(img.pixels.buf[:], data[:])
+
+ // convert to native endianness
+ if header.format in PFM {
+ pixels := mem.slice_data_cast([]f32, img.pixels.buf[:])
+ if header.little_endian {
+ for p in &pixels {
+ p = f32(transmute(f32le) p)
+ }
+ } else {
+ for p in &pixels {
+ p = f32(transmute(f32be) p)
+ }
+ }
+ } else {
+ if img.depth == 16 {
+ pixels := mem.slice_data_cast([]u16, img.pixels.buf[:])
+ for p in &pixels {
+ p = u16(transmute(u16be) p)
+ }
+ }
+ }
+
+ // If-it-looks-like-a-bitmap ASCII
+ case .P1:
+ for c in data {
+ switch c {
+ case '0', '1':
+ bytes.buffer_write_byte(&img.pixels, c - '0')
+ }
+
+ if len(img.pixels.buf) == cap(img.pixels.buf) {
+ break
+ }
+ }
+
+ if len(img.pixels.buf) < cap(img.pixels.buf) {
+ err = Format_Error.Buffer_Too_Small
+ return
+ }
+
+ // Token ASCII
+ case .P2, .P3:
+ field_iterator := string(data)
+ for field in strings.fields_iterator(&field_iterator) {
+ value, ok := strconv.parse_int(field)
+ if !ok {
+ err = Format_Error.Invalid_Buffer_ASCII_Token
+ return
+ }
+
+ //? do we want to enforce the maxval, the limit, or neither
+ if value > int(max(u16)) /*header.maxval*/ {
+ err = Format_Error.Invalid_Buffer_Value
+ return
+ }
+
+ switch img.depth {
+ case 8:
+ bytes.buffer_write_byte(&img.pixels, u8(value))
+ case 16:
+ vb := transmute([2]u8) u16(value)
+ bytes.buffer_write(&img.pixels, vb[:])
+ }
+
+ if len(img.pixels.buf) == cap(img.pixels.buf) {
+ break
+ }
+ }
+
+ if len(img.pixels.buf) < cap(img.pixels.buf) {
+ err = Format_Error.Buffer_Too_Small
+ return
+ }
+ }
+
+ err = Format_Error.None
+ return
+}
+
+// Automatically try to select an appropriate format to save to based on `img.channel` and `img.depth`
+autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, force_black_and_white := false, pfm_scale := f32(1.0)) -> (res: Info, ok: bool) {
+ /*
+ PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
+ PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
+ PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
+ PAM (P7 ): Portable Arbitrary Map, stores arbitrary channel images (1 or 2 bytes per value)
+ PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel)
+
+ ASCII :: Formats{.P1, .P2, .P3}
+ */
+ using res.header
+
+ width = img.width
+ height = img.height
+ channels = img.channels
+ depth = img.depth
+ maxval = 255 if img.depth == 8 else 65535
+ little_endian = true if ODIN_ENDIAN == .Little else false
+
+ // Assume we'll find a suitable format
+ ok = true
+
+ switch img.channels {
+ case 1:
+ // Must be Portable Float Map
+ if img.depth == 32 {
+ format = .Pf
+ return
+ }
+
+ if force_black_and_white {
+ // Portable Bit Map
+ format = .P4 if prefer_binary else .P1
+ maxval = 1
+ return
+ } else {
+ // Portable Gray Map
+ format = .P5 if prefer_binary else .P2
+ return
+ }
+
+ case 3:
+ // Must be Portable Float Map
+ if img.depth == 32 {
+ format = .PF
+ return
+ }
+
+ // Portable Pixel Map
+ format = .P6 if prefer_binary else .P3
+ return
+
+ case:
+ // Portable Arbitrary Map
+ if img.depth == 8 || img.depth == 16 {
+ format = .P7
+ scale = pfm_scale
+ return
+ }
+ }
+
+ // We couldn't find a suitable format
+ return {}, false
+}
+
+@(init, private)
+_register :: proc() {
+ loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) {
+ return load_from_bytes(data, allocator)
+ }
+ destroyer :: proc(img: ^Image) {
+ _ = destroy(img)
+ }
+ image.register(.NetPBM, loader, destroyer)
+}
\ No newline at end of file
diff --git a/core/image/png/example.odin b/core/image/png/example.odin
index 5e7dca4c8..17436c260 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -189,7 +189,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
img := image
// PBM 16-bit images are big endian
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap back.
input := mem.slice_data_cast([]u16, img.pixels.buf[:])
@@ -207,7 +207,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
}
mode: int = 0
- when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index ecc0183bc..0ebf0b20b 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -242,17 +242,16 @@ srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) {
}
plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
- if c.header.type != .PLTE {
+ if c.header.type != .PLTE || c.header.length % 3 != 0 || c.header.length > 768 {
return {}, false
}
- i := 0; j := 0; ok = true
- for j < int(c.header.length) {
- res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]}
- i += 1; j += 3
+ plte := mem.slice_data_cast([]image.RGB_Pixel, c.data[:])
+ for color, i in plte {
+ res.entries[i] = color
}
- res.used = u16(i)
- return
+ res.used = u16(len(plte))
+ return res, true
}
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
@@ -439,18 +438,18 @@ when false {
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
- return E_PNG.Invalid_Image_Dimensions
+ return .Invalid_Image_Dimensions
}
mode: int = 0
- when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
- return E_General.Cannot_Open_File
+ return .Cannot_Open_File
}
defer close(fd)
@@ -473,7 +472,7 @@ when false {
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
- return E_PNG.Unknown_Color_Type
+ return .Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index f77bf7519..35fdb58d8 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -6,6 +6,11 @@
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
*/
+
+
+// package png implements a PNG image reader
+//
+// The PNG specification is at https://www.w3.org/TR/PNG/.
package png
import "core:compress"
@@ -13,23 +18,16 @@ import "core:compress/zlib"
import "core:image"
import "core:os"
-import "core:strings"
import "core:hash"
import "core:bytes"
import "core:io"
import "core:mem"
import "core:intrinsics"
-/*
- 67_108_864 pixels max by default.
- Maximum allowed dimensions are capped at 65535 * 65535.
-*/
-MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+// Limit chunk sizes.
+// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
+// The total number of pixels defaults to 64 Megapixel and can be tuned in image/common.odin.
-/*
- Limit chunk sizes.
- By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
-*/
_MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */
_MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */
@@ -59,7 +57,7 @@ Row_Filter :: enum u8 {
Paeth = 4,
}
-PLTE_Entry :: [3]u8
+PLTE_Entry :: image.RGB_Pixel
PLTE :: struct #packed {
entries: [256]PLTE_Entry,
@@ -239,7 +237,7 @@ append_chunk :: proc(list: ^[dynamic]image.PNG_Chunk, src: image.PNG_Chunk, allo
append(list, c)
if len(list) != length + 1 {
// Resize during append failed.
- return mem.Allocator_Error.Out_Of_Memory
+ return .Unable_To_Allocate_Or_Resize
}
return
@@ -254,7 +252,7 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
header := (^image.PNG_IHDR)(raw_data(c.data))^
// Validate IHDR
using header
- if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS {
+ if width == 0 || height == 0 || u128(width) * u128(height) > image.MAX_DIMENSIONS {
return {}, .Invalid_Image_Dimensions
}
@@ -319,13 +317,12 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
}
chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string {
- t := transmute(^u8)type
- return strings.string_from_ptr(t, 4)
+ return string(([^]u8)(type)[:4])
}
-load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
ctx := &compress.Context_Memory_Input{
- input_data = slice,
+ input_data = data,
}
/*
@@ -345,10 +342,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
defer delete(data)
if ok {
- return load_from_slice(data, options)
+ return load_from_bytes(data, options)
} else {
- img = new(Image)
- return img, compress.General_Error.File_Not_Found
+ return nil, .Unable_To_Read_File
}
}
@@ -361,6 +357,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
options -= {.info}
}
+ if .return_header in options && .return_metadata in options {
+ options -= {.return_header}
+ }
+
if .alpha_drop_if_present in options && .alpha_add_if_missing in options {
return {}, compress.General_Error.Incompatible_Options
}
@@ -372,13 +372,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
if img == nil {
img = new(Image)
}
+ img.which = .PNG
info := new(image.PNG_Info)
img.metadata = info
signature, io_error := compress.read_data(ctx, Signature)
if io_error != .None || signature != .PNG {
- return img, .Invalid_PNG_Signature
+ return img, .Invalid_Signature
}
idat: []u8
@@ -387,7 +388,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
idat_length := u64(0)
- c: image.PNG_Chunk
+ c: image.PNG_Chunk
ch: image.PNG_Chunk_Header
e: io.Error
@@ -468,6 +469,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
info.header = h
+ if .return_header in options && .return_metadata not_in options && .do_not_decompress_image not_in options {
+ return img, nil
+ }
+
case .PLTE:
seen_plte = true
// PLTE must appear before IDAT and can't appear for color types 0, 4.
@@ -535,9 +540,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
seen_iend = true
case .bKGD:
-
- // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
-
c = read_chunk(ctx) or_return
seen_bkgd = true
if .return_metadata in options {
@@ -589,23 +591,36 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
*/
final_image_channels += 1
-
seen_trns = true
+
+ if .Paletted in header.color_type {
+ if len(c.data) > 256 {
+ return img, .TNRS_Invalid_Length
+ }
+ } else if .Color in header.color_type {
+ if len(c.data) != 6 {
+ return img, .TNRS_Invalid_Length
+ }
+ } else if len(c.data) != 2 {
+ return img, .TNRS_Invalid_Length
+ }
+
if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type {
// Rescale tRNS data so key matches intensity
- dsc := depth_scale_table
+ dsc := depth_scale_table
scale := dsc[info.header.bit_depth]
if scale != 1 {
key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale)
c.data = []u8{0, u8(key & 255)}
}
}
+
trns = c
- case .iDOT, .CbGI:
+ case .iDOT, .CgBI:
/*
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
- We're not going to add support for it. If you have the misfortunte of coming
+ We're not going to add support for it. If you have the misfortune of coming
across one of these files, use a utility to defry it.
*/
return img, .Image_Does_Not_Adhere_to_Spec
@@ -630,6 +645,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, .IDAT_Missing
}
+ if .Paletted in header.color_type && !seen_plte {
+ return img, .PLTE_Missing
+ }
+
/*
Calculate the expected output size, to help `inflate` make better decisions about the output buffer.
We'll also use it to check the returned buffer size is what we expected it to be.
@@ -678,15 +697,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return {}, defilter_error
}
- /*
- Now we'll handle the relocoring of paletted images, handling of tRNS chunks,
- and we'll expand grayscale images to RGB(A).
-
- For the sake of convenience we return only RGB(A) images. In the future we
- may supply an option to return Gray/Gray+Alpha as-is, in which case RGB(A)
- will become the default.
- */
-
if .Paletted in header.color_type && .do_not_expand_indexed in options {
return img, nil
}
@@ -694,7 +704,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, nil
}
-
+ /*
+ Now we're going to optionally apply various post-processing stages,
+ to for example expand grayscale, apply a palette, premultiply alpha, etc.
+ */
raw_image_channels := img.channels
out_image_channels := 3
@@ -732,7 +745,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
if !resize(&t.buf, dest_raw_size) {
- return {}, mem.Allocator_Error.Out_Of_Memory
+ return {}, .Unable_To_Allocate_Or_Resize
}
i := 0; j := 0
@@ -813,7 +826,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16)
t := bytes.Buffer{}
if !resize(&t.buf, dest_raw_size) {
- return {}, mem.Allocator_Error.Out_Of_Memory
+ return {}, .Unable_To_Allocate_Or_Resize
}
p16 := mem.slice_data_cast([]u16, temp.buf[:])
@@ -1012,7 +1025,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
if !resize(&t.buf, dest_raw_size) {
- return {}, mem.Allocator_Error.Out_Of_Memory
+ return {}, .Unable_To_Allocate_Or_Resize
}
p := mem.slice_data_cast([]u8, temp.buf[:])
@@ -1199,7 +1212,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, nil
}
-
filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 {
aa, bb, cc := i16(left), i16(up), i16(up_left)
p := aa + bb - cc
@@ -1521,7 +1533,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8)
if !resize(&img.pixels.buf, num_bytes) {
- return mem.Allocator_Error.Out_Of_Memory
+ return .Unable_To_Allocate_Or_Resize
}
filter_ok: bool
@@ -1563,7 +1575,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
temp: bytes.Buffer
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8)
if !resize(&temp.buf, temp_len) {
- return mem.Allocator_Error.Out_Of_Memory
+ return .Unable_To_Allocate_Or_Resize
}
params := Filter_Params{
@@ -1611,7 +1623,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
}
}
}
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap.
input := mem.slice_data_cast([]u16be, img.pixels.buf[:])
@@ -1625,4 +1637,10 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
return nil
}
-load :: proc{load_from_file, load_from_slice, load_from_context}
+load :: proc{load_from_file, load_from_bytes, load_from_context}
+
+
+@(init, private)
+_register :: proc() {
+ image.register(.PNG, load_from_bytes, destroy)
+}
\ No newline at end of file
diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin
new file mode 100644
index 000000000..29a17d4f4
--- /dev/null
+++ b/core/image/qoi/qoi.odin
@@ -0,0 +1,411 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+
+// package qoi implements a QOI image reader
+//
+// The QOI specification is at https://qoiformat.org.
+package qoi
+
+import "core:image"
+import "core:compress"
+import "core:bytes"
+import "core:os"
+
+Error :: image.Error
+Image :: image.Image
+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) {
+ context.allocator = allocator
+
+ if img == nil {
+ return .Invalid_Input_Image
+ }
+
+ if output == nil {
+ return .Invalid_Output
+ }
+
+ pixels := img.width * img.height
+ if pixels == 0 || pixels > image.MAX_DIMENSIONS {
+ return .Invalid_Input_Image
+ }
+
+ // QOI supports only 8-bit images with 3 or 4 channels.
+ if img.depth != 8 || img.channels < 3 || img.channels > 4 {
+ return .Invalid_Input_Image
+ }
+
+ if img.channels * pixels != len(img.pixels.buf) {
+ return .Invalid_Input_Image
+ }
+
+ written := 0
+
+ // Calculate and allocate maximum size. We'll reclaim space to actually written output at the end.
+ max_size := pixels * (img.channels + 1) + size_of(image.QOI_Header) + size_of(u64be)
+
+ if !resize(&output.buf, max_size) {
+ return .Unable_To_Allocate_Or_Resize
+ }
+
+ header := image.QOI_Header{
+ magic = image.QOI_Magic,
+ width = u32be(img.width),
+ height = u32be(img.height),
+ channels = u8(img.channels),
+ color_space = .Linear if .qoi_all_channels_linear in options else .sRGB,
+ }
+ header_bytes := transmute([size_of(image.QOI_Header)]u8)header
+
+ copy(output.buf[written:], header_bytes[:])
+ written += size_of(image.QOI_Header)
+
+ /*
+ Encode loop starts here.
+ */
+ seen: [64]RGBA_Pixel
+ pix := RGBA_Pixel{0, 0, 0, 255}
+ prev := pix
+
+ seen[qoi_hash(pix)] = pix
+
+ input := img.pixels.buf[:]
+ run := u8(0)
+
+ for len(input) > 0 {
+ if img.channels == 4 {
+ pix = (^RGBA_Pixel)(raw_data(input))^
+ } else {
+ pix.rgb = (^RGB_Pixel)(raw_data(input))^
+ }
+ input = input[img.channels:]
+
+ if pix == prev {
+ run += 1
+ // As long as the pixel matches the last one, accumulate the run total.
+ // If we reach the max run length or the end of the image, write the run.
+ if run == 62 || len(input) == 0 {
+ // Encode and write run
+ output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1)
+ written += 1
+ run = 0
+ }
+ } else {
+ if run > 0 {
+ // The pixel differs from the previous one, but we still need to write the pending run.
+ // Encode and write run
+ output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1)
+ written += 1
+ run = 0
+ }
+
+ index := qoi_hash(pix)
+
+ if seen[index] == pix {
+ // Write indexed pixel
+ output.buf[written] = u8(QOI_Opcode_Tag.INDEX) | index
+ written += 1
+ } else {
+ // Add pixel to index
+ seen[index] = pix
+
+ // If the alpha matches the previous pixel's alpha, we don't need to write a full RGBA literal.
+ if pix.a == prev.a {
+ // Delta
+ d := pix.rgb - prev.rgb
+
+ // DIFF, biased and modulo 256
+ _d := d + 2
+
+ // LUMA, biased and modulo 256
+ _l := RGB_Pixel{ d.r - d.g + 8, d.g + 32, d.b - d.g + 8 }
+
+ if _d.r < 4 && _d.g < 4 && _d.b < 4 {
+ // Delta is between -2 and 1 inclusive
+ output.buf[written] = u8(QOI_Opcode_Tag.DIFF) | _d.r << 4 | _d.g << 2 | _d.b
+ written += 1
+ } else if _l.r < 16 && _l.g < 64 && _l.b < 16 {
+ // Biased luma is between {-8..7, -32..31, -8..7}
+ output.buf[written ] = u8(QOI_Opcode_Tag.LUMA) | _l.g
+ output.buf[written + 1] = _l.r << 4 | _l.b
+ written += 2
+ } else {
+ // Write RGB literal
+ output.buf[written] = u8(QOI_Opcode_Tag.RGB)
+ pix_bytes := transmute([4]u8)pix
+ copy(output.buf[written + 1:], pix_bytes[:3])
+ written += 4
+ }
+ } else {
+ // Write RGBA literal
+ output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
+ pix_bytes := transmute([4]u8)pix
+ copy(output.buf[written + 1:], pix_bytes[:])
+ written += 5
+ }
+ }
+ }
+ prev = pix
+ }
+
+ trailer := []u8{0, 0, 0, 0, 0, 0, 0, 1}
+ copy(output.buf[written:], trailer[:])
+ written += len(trailer)
+
+ resize(&output.buf, written)
+ 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,
+ }
+
+ img, err = load_from_context(ctx, options, allocator)
+ 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
+ options := options
+
+ if .info in options {
+ options |= {.return_metadata, .do_not_decompress_image}
+ options -= {.info}
+ }
+
+ if .return_header in options && .return_metadata in options {
+ options -= {.return_header}
+ }
+
+ header := image.read_data(ctx, image.QOI_Header) or_return
+ if header.magic != image.QOI_Magic {
+ return img, .Invalid_Signature
+ }
+
+ if img == nil {
+ img = new(Image)
+ }
+ img.which = .QOI
+
+ if .return_metadata in options {
+ info := new(image.QOI_Info)
+ info.header = header
+ img.metadata = info
+ }
+
+ if header.channels != 3 && header.channels != 4 {
+ return img, .Invalid_Number_Of_Channels
+ }
+
+ if header.color_space != .sRGB && header.color_space != .Linear {
+ return img, .Invalid_Color_Space
+ }
+
+ if header.width == 0 || header.height == 0 {
+ return img, .Invalid_Image_Dimensions
+ }
+
+ total_pixels := header.width * header.height
+ if total_pixels > image.MAX_DIMENSIONS {
+ return img, .Image_Dimensions_Too_Large
+ }
+
+ img.width = int(header.width)
+ img.height = int(header.height)
+ img.channels = 4 if .alpha_add_if_missing in options else int(header.channels)
+ img.depth = 8
+
+ if .do_not_decompress_image in options {
+ img.channels = int(header.channels)
+ return
+ }
+
+ bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
+
+ if !resize(&img.pixels.buf, bytes_needed) {
+ return img, .Unable_To_Allocate_Or_Resize
+ }
+
+ /*
+ Decode loop starts here.
+ */
+ seen: [64]RGBA_Pixel
+ pix := RGBA_Pixel{0, 0, 0, 255}
+ seen[qoi_hash(pix)] = pix
+ pixels := img.pixels.buf[:]
+
+ decode: for len(pixels) > 0 {
+ data := image.read_u8(ctx) or_return
+
+ tag := QOI_Opcode_Tag(data)
+ #partial switch tag {
+ case .RGB:
+ pix.rgb = image.read_data(ctx, RGB_Pixel) or_return
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .RGBA:
+ pix = image.read_data(ctx, RGBA_Pixel) or_return
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case:
+ // 2-bit tag
+ tag = QOI_Opcode_Tag(data & QOI_Opcode_Mask)
+ #partial switch tag {
+ case .INDEX:
+ pix = seen[data & 63]
+
+ case .DIFF:
+ diff_r := ((data >> 4) & 3) - 2
+ diff_g := ((data >> 2) & 3) - 2
+ diff_b := ((data >> 0) & 3) - 2
+
+ pix += {diff_r, diff_g, diff_b, 0}
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .LUMA:
+ data2 := image.read_u8(ctx) or_return
+
+ diff_g := (data & 63) - 32
+ diff_r := diff_g - 8 + ((data2 >> 4) & 15)
+ diff_b := diff_g - 8 + (data2 & 15)
+
+ pix += {diff_r, diff_g, diff_b, 0}
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .RUN:
+ if length := int(data & 63) + 1; (length * img.channels) > len(pixels) {
+ return img, .Corrupt
+ } else {
+ #no_bounds_check for in 0.. (index: u8) {
+ i1 := u16(pixel.r) * 3
+ i2 := u16(pixel.g) * 5
+ i3 := u16(pixel.b) * 7
+ i4 := u16(pixel.a) * 11
+
+ return u8((i1 + i2 + i3 + i4) & 63)
+}
+
+@(init, private)
+_register :: proc() {
+ image.register(.QOI, load_from_bytes, destroy)
+}
\ No newline at end of file
diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin
new file mode 100644
index 000000000..67a088eb5
--- /dev/null
+++ b/core/image/tga/tga.odin
@@ -0,0 +1,101 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+
+// package tga implements a TGA image writer for 8-bit RGB and RGBA images.
+package tga
+
+import "core:mem"
+import "core:image"
+import "core:bytes"
+import "core:os"
+
+Error :: image.Error
+Image :: image.Image
+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) {
+ context.allocator = allocator
+
+ if img == nil {
+ return .Invalid_Input_Image
+ }
+
+ if output == nil {
+ return .Invalid_Output
+ }
+
+ pixels := img.width * img.height
+ if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 {
+ return .Invalid_Input_Image
+ }
+
+ // Our TGA writer supports only 8-bit images with 3 or 4 channels.
+ if img.depth != 8 || img.channels < 3 || img.channels > 4 {
+ return .Invalid_Input_Image
+ }
+
+ if img.channels * pixels != len(img.pixels.buf) {
+ return .Invalid_Input_Image
+ }
+
+ written := 0
+
+ // Calculate and allocate necessary space.
+ necessary := pixels * img.channels + size_of(image.TGA_Header)
+
+ if !resize(&output.buf, necessary) {
+ return .Unable_To_Allocate_Or_Resize
+ }
+
+ header := image.TGA_Header{
+ data_type_code = 0x02, // Color, uncompressed.
+ dimensions = {u16le(img.width), u16le(img.height)},
+ bits_per_pixel = u8(img.depth * img.channels),
+ image_descriptor = 1 << 5, // Origin is top left.
+ }
+ header_bytes := transmute([size_of(image.TGA_Header)]u8)header
+
+ copy(output.buf[written:], header_bytes[:])
+ written += size_of(image.TGA_Header)
+
+ /*
+ Encode loop starts here.
+ */
+ if img.channels == 3 {
+ pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:])
+ for p, i in pix {
+ out[i] = p.bgr
+ }
+ } else if img.channels == 4 {
+ pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:])
+ for p, i in pix {
+ out[i] = p.bgra
+ }
+ }
+ 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}
\ No newline at end of file
diff --git a/core/image/which.odin b/core/image/which.odin
new file mode 100644
index 000000000..ab608174f
--- /dev/null
+++ b/core/image/which.odin
@@ -0,0 +1,179 @@
+package image
+
+import "core:os"
+
+Which_File_Type :: enum {
+ Unknown,
+
+ BMP,
+ DjVu, // AT&T DjVu file format
+ EXR,
+ FLIF,
+ GIF,
+ HDR, // Radiance RGBE HDR
+ ICNS, // Apple Icon Image
+ JPEG,
+ JPEG_2000,
+ JPEG_XL,
+ NetPBM, // NetPBM family
+ PIC, // Softimage PIC
+ PNG, // Portable Network Graphics
+ PSD, // Photoshop PSD
+ QOI, // Quite Okay Image
+ SGI_RGB, // Silicon Graphics Image RGB file format
+ Sun_Rast, // Sun Raster Graphic
+ TGA, // Targa Truevision
+ TIFF, // Tagged Image File Format
+ WebP,
+ 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 {
+ v := s[0]
+ s^ = s[1:]
+ return v
+ }
+ get16le :: #force_inline proc(s: ^string) -> u16 {
+ v := u16(s[0]) | u16(s[1])<<16
+ s^ = s[2:]
+ return v
+ }
+ s := s
+ s = s[1:] // skip offset
+
+ color_type := get8(&s)
+ if color_type > 1 {
+ return false
+ }
+ image_type := get8(&s) // image type
+ if color_type == 1 { // Colormap (Paletted) Image
+ if image_type != 1 && image_type != 9 { // color type requires 1 or 9
+ return false
+ }
+ s = s[4:] // skip index of first colormap
+ bpcme := get8(&s) // check bits per colormap entry
+ if bpcme != 8 && bpcme != 15 && bpcme != 16 && bpcme != 24 && bpcme != 32 {
+ return false
+ }
+ s = s[4:] // skip image origin (x, y)
+ } else { // Normal image without colormap
+ if image_type != 2 && image_type != 3 && image_type != 10 && image_type != 11 {
+ return false
+ }
+ s = s[9:] // skip colormap specification
+ }
+ if get16le(&s) < 1 || get16le(&s) < 1 { // test width and height
+ return false
+ }
+ bpp := get8(&s) // bits per pixel
+ if color_type == 1 && bpp != 8 && bpp != 16 {
+ return false
+ }
+ if bpp != 8 && bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 {
+ return false
+ }
+ return true
+ }
+
+ header: [128]byte
+ copy(header[:], data)
+ s := string(header[:])
+
+ switch {
+ case s[:2] == "BM":
+ return .BMP
+ case s[:8] == "AT&TFORM":
+ switch s[12:16] {
+ case "DJVU", "DJVM":
+ return .DjVu
+ }
+ case s[:4] == "\x76\x2f\x31\x01":
+ return .EXR
+ case s[:6] == "GIF87a", s[:6] == "GIF89a":
+ return .GIF
+ case s[6:10] == "JFIF", s[6:10] == "Exif":
+ return .JPEG
+ case s[:3] == "\xff\xd8\xff":
+ switch s[4] {
+ case 0xdb, 0xee, 0xe1, 0xe0:
+ return .JPEG
+ }
+ switch {
+ case s[:12] == "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01":
+ return .JPEG
+ }
+ case s[:4] == "\xff\x4f\xff\x51", s[:12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a":
+ return .JPEG_2000
+ case s[:12] == "\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a":
+ return .JPEG_XL
+ case s[0] == 'P':
+ switch s[2] {
+ case '\t', '\n', '\r':
+ switch s[1] {
+ case '1', '4': // PBM
+ return .NetPBM
+ case '2', '5': // PGM
+ return .NetPBM
+ case '3', '6': // PPM
+ return .NetPBM
+ case '7': // PAM
+ return .NetPBM
+ case 'F', 'f': // PFM
+ return .NetPBM
+ }
+ }
+ case s[:8] == "\x89PNG\r\n\x1a\n":
+ return .PNG
+ case s[:4] == "qoif":
+ return .QOI
+ case s[:2] == "\x01\xda":
+ return .SGI_RGB
+ case s[:4] == "\x59\xA6\x6A\x95":
+ return .Sun_Rast
+ case s[:4] == "MM\x2a\x00", s[:4] == "II\x00\x2A":
+ return .TIFF
+ case s[:4] == "RIFF" && s[8:12] == "WEBP":
+ return .WebP
+ case s[:8] == "#define ":
+ return .XBM
+
+ case s[:11] == "#?RADIANCE\n", s[:7] == "#?RGBE\n":
+ return .HDR
+ case s[:4] == "\x38\x42\x50\x53":
+ return .PSD
+ case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
+ return .PIC
+ case s[:4] == "\x69\x63\x6e\x73":
+ return .ICNS
+ case s[:4] == "\x46\x4c\x49\x46":
+ return .FLIF
+ case:
+ // More complex formats
+ if test_tga(s) {
+ return .TGA
+ }
+
+
+ }
+ 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/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 2da7a7439..22b5d953d 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -6,12 +6,14 @@ package intrinsics
is_package_imported :: proc(package_name: string) -> bool ---
// Types
-simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
// Volatile
volatile_load :: proc(dst: ^$T) -> T ---
-volatile_store :: proc(dst: ^$T, val: T) -> T ---
+volatile_store :: proc(dst: ^$T, val: T) ---
+
+non_temporal_load :: proc(dst: ^$T) -> T ---
+non_temporal_store :: proc(dst: ^$T, val: T) ---
// Trapping
debug_trap :: proc() ---
@@ -23,24 +25,32 @@ alloca :: proc(size, align: int) -> [^]u8 ---
cpu_relax :: proc() ---
read_cycle_counter :: proc() -> i64 ---
-count_ones :: proc(x: $T) -> T where type_is_integer(T) ---
-count_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
-count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
-count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
-reverse_bits :: proc(x: $T) -> T where type_is_integer(T) ---
+count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
+count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
+count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
+count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
+reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
-sqrt :: proc(x: $T) -> T where type_is_float(T) ---
+sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
+
+fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
mem_copy :: proc(dst, src: rawptr, len: int) ---
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
mem_zero :: proc(ptr: rawptr, len: int) ---
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
+// prefer [^]T operations if possible
+ptr_offset :: proc(ptr: ^$T, offset: int) -> ^T ---
+ptr_sub :: proc(a, b: ^$T) -> int ---
+
+unaligned_load :: proc(src: ^$T) -> T ---
+unaligned_store :: proc(dst: ^$T, val: T) -> T ---
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
@@ -60,77 +70,47 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// Atomics
-atomic_fence :: proc() ---
-atomic_fence_acq :: proc() ---
-atomic_fence_rel :: proc() ---
-atomic_fence_acqrel :: proc() ---
+Atomic_Memory_Order :: enum {
+ Relaxed = 0, // Unordered
+ Consume = 1, // Monotonic
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
+}
-atomic_store :: proc(dst: ^$T, val: T) ---
-atomic_store_rel :: proc(dst: ^$T, val: T) ---
-atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
-atomic_store_unordered :: proc(dst: ^$T, val: T) ---
+atomic_type_is_lock_free :: proc($T: typeid) -> bool ---
+
+atomic_thread_fence :: proc(order: Atomic_Memory_Order) ---
+atomic_signal_fence :: proc(order: Atomic_Memory_Order) ---
+
+atomic_store :: proc(dst: ^$T, val: T) ---
+atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) ---
atomic_load :: proc(dst: ^$T) -> T ---
-atomic_load_acq :: proc(dst: ^$T) -> T ---
-atomic_load_relaxed :: proc(dst: ^$T) -> T ---
-atomic_load_unordered :: proc(dst: ^$T) -> T ---
+atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
-atomic_add :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_and :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_or :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
+// fetch then operator
+atomic_add :: proc(dst: ^$T, val: T) -> T ---
+atomic_add_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_sub :: proc(dst: ^$T, val: T) -> T ---
+atomic_sub_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_and :: proc(dst: ^$T, val: T) -> T ---
+atomic_and_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_nand :: proc(dst: ^$T, val: T) -> T ---
+atomic_nand_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_or :: proc(dst: ^$T, val: T) -> T ---
+atomic_or_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_xor :: proc(dst: ^$T, val: T) -> T ---
+atomic_xor_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_exchange :: proc(dst: ^$T, val: T) -> T ---
+atomic_exchange_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
-atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
+atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
-atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-
-atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
// Constant type tests
@@ -148,22 +128,24 @@ type_is_string :: proc($T: typeid) -> bool ---
type_is_typeid :: proc($T: typeid) -> bool ---
type_is_any :: proc($T: typeid) -> bool ---
-type_is_endian_platform :: proc($T: typeid) -> bool ---
-type_is_endian_little :: proc($T: typeid) -> bool ---
-type_is_endian_big :: proc($T: typeid) -> bool ---
-type_is_unsigned :: proc($T: typeid) -> bool ---
-type_is_numeric :: proc($T: typeid) -> bool ---
-type_is_ordered :: proc($T: typeid) -> bool ---
-type_is_ordered_numeric :: proc($T: typeid) -> bool ---
-type_is_indexable :: proc($T: typeid) -> bool ---
-type_is_sliceable :: proc($T: typeid) -> bool ---
-type_is_comparable :: proc($T: typeid) -> bool ---
-type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
-type_is_dereferenceable :: proc($T: typeid) -> bool ---
-type_is_valid_map_key :: proc($T: typeid) -> bool ---
+type_is_endian_platform :: proc($T: typeid) -> bool ---
+type_is_endian_little :: proc($T: typeid) -> bool ---
+type_is_endian_big :: proc($T: typeid) -> bool ---
+type_is_unsigned :: proc($T: typeid) -> bool ---
+type_is_numeric :: proc($T: typeid) -> bool ---
+type_is_ordered :: proc($T: typeid) -> bool ---
+type_is_ordered_numeric :: proc($T: typeid) -> bool ---
+type_is_indexable :: proc($T: typeid) -> bool ---
+type_is_sliceable :: proc($T: typeid) -> bool ---
+type_is_comparable :: proc($T: typeid) -> bool ---
+type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
+type_is_dereferenceable :: proc($T: typeid) -> bool ---
+type_is_valid_map_key :: proc($T: typeid) -> bool ---
+type_is_valid_matrix_elements :: proc($T: typeid) -> bool ---
type_is_named :: proc($T: typeid) -> bool ---
type_is_pointer :: proc($T: typeid) -> bool ---
+type_is_multi_pointer :: proc($T: typeid) -> bool ---
type_is_array :: proc($T: typeid) -> bool ---
type_is_enumerated_array :: proc($T: typeid) -> bool ---
type_is_slice :: proc($T: typeid) -> bool ---
@@ -175,6 +157,7 @@ type_is_enum :: proc($T: typeid) -> bool ---
type_is_proc :: proc($T: typeid) -> bool ---
type_is_bit_set :: proc($T: typeid) -> bool ---
type_is_simd_vector :: proc($T: typeid) -> bool ---
+type_is_matrix :: proc($T: typeid) -> bool ---
type_has_nil :: proc($T: typeid) -> bool ---
@@ -182,6 +165,7 @@ type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
type_has_field :: proc($T: typeid, $name: string) -> bool ---
+type_field_type :: proc($T: typeid, $name: string) -> typeid ---
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
@@ -189,11 +173,128 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
+type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
+
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
+type_is_specialized_polymorphic_record :: proc($T: typeid) -> bool ---
+type_is_unspecialized_polymorphic_record :: proc($T: typeid) -> bool ---
+
+type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
+
+constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
+
+// SIMD related
+simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
+
+// Keeps Odin's Behaviour
+// (x << y) if y <= mask else 0
+simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
+simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
+
+// Similar to C's Behaviour
+// x << (y & mask)
+simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
+simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
+
+simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+
+simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_and_not :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+
+simd_neg :: proc(a: #simd[N]T) -> #simd[N]T ---
+
+simd_abs :: proc(a: #simd[N]T) -> #simd[N]T ---
+
+simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T ---
+simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T ---
+
+// Return an unsigned integer of the same size as the input type
+// NOT A BOOLEAN
+// element-wise:
+// false => 0x00...00
+// true => 0xff...ff
+simd_lanes_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+simd_lanes_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+simd_lanes_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+simd_lanes_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+simd_lanes_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
+
+simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
+simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
+
+simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T ---
+simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T ---
+simd_reduce_min :: proc(a: #simd[N]T) -> T ---
+simd_reduce_max :: proc(a: #simd[N]T) -> T ---
+simd_reduce_and :: proc(a: #simd[N]T) -> T ---
+simd_reduce_or :: proc(a: #simd[N]T) -> T ---
+simd_reduce_xor :: proc(a: #simd[N]T) -> T ---
+
+simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
+simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
+
+// Lane-wise operations
+simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
+simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
+simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
+// rounding to the nearest integral value; if two values are equally near, rounds to the even one
+simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
+
+simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
+
+// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
+simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
+
+simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
+simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
+
+
+// WASM targets only
+wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
+wasm_memory_size :: proc(index: uintptr) -> int ---
+
+// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for
+// A negative value will be blocked forever
+// Return value:
+// 0 - indicates that the thread blocked and then was woken up
+// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
+// 2 - the thread blocked, but the timeout
+wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
+wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
+
+// x86 Targets (i386, amd64)
+x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
+x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
+
+
+// Darwin targets only
+objc_object :: struct{}
+objc_selector :: struct{}
+objc_class :: struct{}
+objc_id :: ^objc_object
+objc_SEL :: ^objc_selector
+objc_Class :: ^objc_class
+
+objc_find_selector :: proc($name: string) -> objc_SEL ---
+objc_register_selector :: proc($name: string) -> objc_SEL ---
+objc_find_class :: proc($name: string) -> objc_Class ---
+objc_register_class :: proc($name: string) -> objc_Class ---
+
+// Internal compiler use only
+
+__entry_point :: proc() ---
\ No newline at end of file
diff --git a/core/io/io.odin b/core/io/io.odin
index b4757f8e5..3ad34d607 100644
--- a/core/io/io.odin
+++ b/core/io/io.odin
@@ -1,9 +1,12 @@
+// package io provides basic interfaces for generic data stream primitives.
+// The purpose of this package is wrap existing data structures and their
+// operations into an abstracted stream interface.
package io
import "core:intrinsics"
-import "core:runtime"
import "core:unicode/utf8"
+// Seek whence values
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
@@ -139,6 +142,10 @@ destroy :: proc(s: Stream) -> Error {
return .Empty
}
+// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
+//
+// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
+// bytes read along with the error.
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_read != nil {
n, err = s->impl_read(p)
@@ -150,6 +157,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
return 0, .Empty
}
+// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_write != nil {
n, err = s->impl_write(p)
@@ -161,6 +169,13 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
return 0, .Empty
}
+// seek sets the offset of the next read or write to offset.
+//
+// .Start means seek relative to the origin of the file.
+// .Current means seek relative to the current offset.
+// .End means seek relative to the end.
+//
+// seek returns the new offset to the start of the file/stream, and any error if occurred.
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if s.stream_vtable != nil && s.impl_seek != nil {
return s->impl_seek(offset, whence)
@@ -168,6 +183,8 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
return 0, .Empty
}
+// The behaviour of close after the first call is stream implementation defined.
+// Different streams may document their own behaviour.
close :: proc(s: Closer) -> Error {
if s.stream_vtable != nil && s.impl_close != nil {
return s->impl_close()
@@ -184,6 +201,7 @@ flush :: proc(s: Flusher) -> Error {
return .None
}
+// size returns the size of the stream. If the stream does not support querying its size, 0 will be returned.
size :: proc(s: Stream) -> i64 {
if s.stream_vtable == nil {
return 0
@@ -214,7 +232,12 @@ size :: proc(s: Stream) -> i64 {
-
+// read_at reads len(p) bytes into p starting with the provided offset in the underlying Reader_At stream r.
+// It returns the number of bytes read and any error if occurred.
+//
+// When read_at returns n < len(p), it returns a non-nil Error explaining why.
+//
+// If n == len(p), err may be either nil or .EOF
read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) {
defer if n_read != nil {
n_read^ += n
@@ -230,11 +253,7 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
return 0, .Empty
}
- curr_offset: i64
- curr_offset, err = r->impl_seek(offset, .Current)
- if err != nil {
- return 0, err
- }
+ curr_offset := r->impl_seek(offset, .Current) or_return
n, err = r->impl_read(p)
_, err1 := r->impl_seek(curr_offset, .Start)
@@ -245,6 +264,11 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
}
+// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
+// It returns the number of bytes written and any error if occurred.
+//
+// If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying
+// seek offset.
write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) {
defer if n_written != nil {
n_written^ += n
@@ -294,6 +318,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
}
+// read_byte reads and returns the next byte from r.
read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += 1
@@ -347,6 +372,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
return err
}
+// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += size
@@ -405,10 +431,12 @@ unread_rune :: proc(s: Rune_Scanner) -> Error {
}
+// write_string writes the contents of the string s to w.
write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) {
return write(s, transmute([]byte)str, n_written)
}
+// write_rune writes a UTF-8 encoded rune to w.
write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) {
defer if err == nil && n_written != nil {
n_written^ += size
@@ -430,12 +458,16 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
}
-
+// read_full expected exactly len(buf) bytes from r into buf.
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
return read_at_least(r, buf, len(buf))
}
+// read_at_least reads from r into buf until it has read at least min bytes. It returns the number
+// of bytes copied and an error if fewer bytes were read. `.EOF` is only returned if no bytes were read.
+// `.Unexpected_EOF` is returned when an `.EOF ` is returned by the passed Reader after reading
+// fewer than min bytes. If len(buf) is less than min, `.Short_Buffer` is returned.
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer
@@ -515,7 +547,7 @@ _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, er
}
}
// NOTE(bill): alloca is fine here
- buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size}
+ buf = intrinsics.alloca(size, 2*align_of(rawptr))[:size]
}
for {
nr, er := read(src, buf)
diff --git a/core/io/util.odin b/core/io/util.odin
index 9d253807b..46aa97919 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
return
}
+// writer append a quoted rune into the byte buffer, return the written size
+write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
+ _write_byte :: #force_inline proc(w: Writer, c: byte) -> int {
+ err := write_byte(w, c)
+ return 1 if err == nil else 0
+ }
+
+ quote := byte('\'')
+ n += _write_byte(w, quote)
+ buf, width := utf8.encode_rune(r)
+ if width == 1 && r == utf8.RUNE_ERROR {
+ n += _write_byte(w, '\\')
+ n += _write_byte(w, 'x')
+ n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
+ n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
+ } else {
+ i, _ := write_escaped_rune(w, r, quote)
+ n += i
+ }
+ n += _write_byte(w, quote)
+ return
+}
+
+
Tee_Reader :: struct {
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
index cc019617f..7f0d3b07a 100644
--- a/core/log/file_console_logger.odin
+++ b/core/log/file_console_logger.odin
@@ -67,7 +67,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
h = data.file_handle
}
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
- buf := strings.builder_from_slice(backing[:])
+ buf := strings.builder_from_bytes(backing[:])
do_level_header(options, level, &buf)
diff --git a/core/math/big/api.odin b/core/math/big/api.odin
index c9be04da0..bf19e83b6 100644
--- a/core/math/big/api.odin
+++ b/core/math/big/api.odin
@@ -2,12 +2,10 @@
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
-
This file collects public proc maps and their aliases.
*/
+
+
package math_big
/*
diff --git a/core/math/big/common.odin b/core/math/big/common.odin
index 5b7d162bc..74a641d83 100644
--- a/core/math/big/common.odin
+++ b/core/math/big/common.odin
@@ -1,11 +1,9 @@
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
-
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+
package math_big
import "core:intrinsics"
@@ -158,13 +156,14 @@ Error :: enum int {
Invalid_Pointer = 2,
Invalid_Argument = 3,
- Assignment_To_Immutable = 4,
- Max_Iterations_Reached = 5,
- Buffer_Overflow = 6,
- Integer_Overflow = 7,
+ Assignment_To_Immutable = 10,
+ Max_Iterations_Reached = 11,
+ Buffer_Overflow = 12,
+ Integer_Overflow = 13,
+ Integer_Underflow = 14,
- Division_by_Zero = 8,
- Math_Domain_Error = 9,
+ Division_by_Zero = 30,
+ Math_Domain_Error = 31,
Cannot_Open_File = 50,
Cannot_Read_File = 51,
@@ -173,7 +172,7 @@ Error :: enum int {
Unimplemented = 127,
}
-Error_String :: #partial [Error]string{
+Error_String :: #sparse[Error]string{
.Okay = "Okay",
.Out_Of_Memory = "Out of memory",
.Invalid_Pointer = "Invalid pointer",
@@ -183,6 +182,7 @@ Error_String :: #partial [Error]string{
.Max_Iterations_Reached = "Max iterations reached",
.Buffer_Overflow = "Buffer overflow",
.Integer_Overflow = "Integer overflow",
+ .Integer_Underflow = "Integer underflow",
.Division_by_Zero = "Division by zero",
.Math_Domain_Error = "Math domain error",
@@ -215,7 +215,7 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS)
/*
Maximum number of digits.
- Must be small enough such that `_bit_count` does not overflow.
- - Must be small enough such that `_radix_size` for base 2 does not overflow.
+ - Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_BIT_COUNT :: (max(int) - 2)
@@ -251,7 +251,7 @@ Order :: enum i8 {
}
Endianness :: enum i8 {
- Little = -1,
- Platform = 0,
- Big = 1,
-};
\ No newline at end of file
+ Little = -1,
+ Platform = 0,
+ Big = 1,
+}
\ No newline at end of file
diff --git a/core/math/big/doc.odin b/core/math/big/doc.odin
new file mode 100644
index 000000000..0f9b88d01
--- /dev/null
+++ b/core/math/big/doc.odin
@@ -0,0 +1,6 @@
+/*
+A BigInt implementation in Odin.
+For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
+The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
+*/
+package math_big
diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin
index 6d13d32bb..6c4b5dd01 100644
--- a/core/math/big/helpers.odin
+++ b/core/math/big/helpers.odin
@@ -1,11 +1,9 @@
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
-
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin
index 4702e76a3..dbcd16509 100644
--- a/core/math/big/internal.odin
+++ b/core/math/big/internal.odin
@@ -1,12 +1,7 @@
-//+ignore
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
- A BigInt implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
-
========================== Low-level routines ==========================
IMPORTANT: `internal_*` procedures make certain assumptions about their input.
@@ -29,11 +24,15 @@
TODO: Handle +/- Infinity and NaN.
*/
+
+
+//+ignore
package math_big
import "core:mem"
import "core:intrinsics"
import rnd "core:math/rand"
+import "core:builtin"
/*
Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7.
@@ -1880,8 +1879,6 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
where intrinsics.type_is_integer(T) {
context.allocator = allocator
- src := src
-
internal_error_if_immutable(dest) or_return
/*
Most internal procs asssume an Int to have already been initialize,
@@ -1892,13 +1889,27 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
dest.flags = {} // We're not -Inf, Inf, NaN or Immutable.
dest.used = 0
- dest.sign = .Zero_or_Positive if src >= 0 else .Negative
- src = internal_abs(src)
+ dest.sign = .Negative if src < 0 else .Zero_or_Positive
- #no_bounds_check for src != 0 {
- dest.digit[dest.used] = DIGIT(src) & _MASK
+ temp := src
+
+ is_maximally_negative := src == min(T)
+ if is_maximally_negative {
+ /*
+ Prevent overflow on abs()
+ */
+ temp += 1
+ }
+ temp = -temp if temp < 0 else temp
+
+ #no_bounds_check for temp != 0 {
+ dest.digit[dest.used] = DIGIT(temp) & _MASK
dest.used += 1
- src >>= _DIGIT_BITS
+ temp >>= _DIGIT_BITS
+ }
+
+ if is_maximally_negative {
+ return internal_sub(dest, dest, 1)
}
internal_zero_unused(dest)
return nil
@@ -2307,28 +2318,69 @@ internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) {
}
internal_get_i32 :: proc { internal_int_get_i32, }
+internal_get_low_u32 :: proc(a: ^Int) -> u32 #no_bounds_check {
+ if a == nil {
+ return 0
+ }
+
+ if a.used == 0 {
+ return 0
+ }
+
+ return u32(a.digit[0])
+}
+internal_get_low_u64 :: proc(a: ^Int) -> u64 #no_bounds_check {
+ if a == nil {
+ return 0
+ }
+
+ if a.used == 0 {
+ return 0
+ }
+
+ v := u64(a.digit[0])
+ when size_of(DIGIT) == 4 {
+ if a.used > 1 {
+ return u64(a.digit[1])<<32 | v
+ }
+ }
+ return v
+}
+
/*
TODO: Think about using `count_bits` to check if the value could be returned completely,
and maybe return max(T), .Integer_Overflow if not?
*/
internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) {
- size_in_bits := int(size_of(T) * 8)
- i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS)
- i = min(int(a.used), i)
-
- #no_bounds_check for ; i >= 0; i -= 1 {
- res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS
- res |= T(a.digit[i])
- if size_in_bits <= _DIGIT_BITS {
- break
+ /*
+ Calculate target bit size.
+ */
+ target_bit_size := int(size_of(T) * 8)
+ when !intrinsics.type_is_unsigned(T) {
+ if a.sign == .Zero_or_Positive {
+ target_bit_size -= 1
+ }
+ } else {
+ if a.sign == .Negative {
+ return 0, .Integer_Underflow
}
}
+ bits_used := internal_count_bits(a)
+
+ if bits_used > target_bit_size {
+ if a.sign == .Negative {
+ return min(T), .Integer_Underflow
+ }
+ return max(T), .Integer_Overflow
+ }
+
+ for i := a.used; i > 0; i -= 1 {
+ res <<= _DIGIT_BITS
+ res |= T(a.digit[i - 1])
+ }
+
when !intrinsics.type_is_unsigned(T) {
- /*
- Mask off sign bit.
- */
- res ~= 1 << uint(size_in_bits - 1)
/*
Set the sign.
*/
@@ -2594,7 +2646,7 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all
Shift by as many digits in the bit count.
*/
if bits >= _DIGIT_BITS {
- internal_shr_digit(quotient, bits / _DIGIT_BITS) or_return
+ _private_int_shr_leg(quotient, bits / _DIGIT_BITS) or_return
}
/*
@@ -2633,37 +2685,6 @@ internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.all
}
internal_shr :: proc { internal_int_shr, }
-/*
- Shift right by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- if digits <= 0 { return nil }
-
- /*
- If digits > used simply zero and return.
- */
- if digits > quotient.used { return internal_zero(quotient) }
-
- /*
- Much like `int_shl_digit`, this is implemented using a sliding window,
- except the window goes the other way around.
-
- b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
- /\ | ---->
- \-------------------/ ---->
- */
-
- #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
- quotient.digit[x] = quotient.digit[x + digits]
- }
- quotient.used -= digits
- internal_zero_unused(quotient)
- return internal_clamp(quotient)
-}
-internal_shr_digit :: proc { internal_int_shr_digit, }
-
/*
Shift right by a certain bit count with sign extension.
*/
@@ -2702,7 +2723,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
Shift by as many digits in the bit count as we have.
*/
if bits >= _DIGIT_BITS {
- internal_shl_digit(dest, bits / _DIGIT_BITS) or_return
+ _private_int_shl_leg(dest, bits / _DIGIT_BITS) or_return
}
/*
@@ -2732,45 +2753,6 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
}
internal_shl :: proc { internal_int_shl, }
-
-/*
- Shift left by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- if digits <= 0 { return nil }
-
- /*
- No need to shift a zero.
- */
- if #force_inline internal_is_zero(quotient) {
- return nil
- }
-
- /*
- Resize `quotient` to accomodate extra digits.
- */
- #force_inline internal_grow(quotient, quotient.used + digits) or_return
-
- /*
- Increment the used by the shift amount then copy upwards.
- */
-
- /*
- Much like `int_shr_digit`, this is implemented using a sliding window,
- except the window goes the other way around.
- */
- #no_bounds_check for x := quotient.used; x > 0; x -= 1 {
- quotient.digit[x+digits-1] = quotient.digit[x-1]
- }
-
- quotient.used += digits
- mem.zero_slice(quotient.digit[:digits])
- return nil
-}
-internal_shl_digit :: proc { internal_int_shl_digit, }
-
/*
Count bits in an `Int`.
Assumes `a` not to be `nil` and to have been initialized.
diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin
index dbcf566c8..b5de4cabf 100644
--- a/core/math/big/logical.odin
+++ b/core/math/big/logical.odin
@@ -8,6 +8,8 @@
This file contains logical operations like `and`, `or` and `xor`.
*/
+
+
package math_big
/*
@@ -86,21 +88,6 @@ int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -
}
shr :: proc { int_shr, }
-/*
- Shift right by `digits` * _DIGIT_BITS bits.
-*/
-int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- /*
- Check that `quotient` is usable.
- */
- assert_if_nil(quotient)
- context.allocator = allocator
-
- internal_clear_if_uninitialized(quotient) or_return
- return #force_inline internal_int_shr_digit(quotient, digits)
-}
-shr_digit :: proc { int_shr_digit, }
-
/*
Shift right by a certain bit count with sign extension.
*/
@@ -124,20 +111,4 @@ int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (
internal_clear_if_uninitialized(dest, src) or_return
return #force_inline internal_int_shl(dest, src, bits)
}
-shl :: proc { int_shl, }
-
-
-/*
- Shift left by `digits` * _DIGIT_BITS bits.
-*/
-int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- /*
- Check that `quotient` is usable.
- */
- assert_if_nil(quotient)
- context.allocator = allocator
-
- internal_clear_if_uninitialized(quotient) or_return
- return #force_inline internal_int_shl_digit(quotient, digits)
-}
-shl_digit :: proc { int_shl_digit, };
\ No newline at end of file
+shl :: proc { int_shl, }
\ No newline at end of file
diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin
index eb0cd644c..3cce69675 100644
--- a/core/math/big/prime.odin
+++ b/core/math/big/prime.odin
@@ -8,6 +8,8 @@
This file contains prime finding operations.
*/
+
+
package math_big
import rnd "core:math/rand"
diff --git a/core/math/big/private.odin b/core/math/big/private.odin
index 14a27f600..419f2103f 100644
--- a/core/math/big/private.odin
+++ b/core/math/big/private.odin
@@ -15,6 +15,8 @@
These aren't exported for the same reasons.
*/
+
+
package math_big
import "core:intrinsics"
@@ -211,12 +213,12 @@ _private_int_mul_toom :: proc(dest, a, b: ^Int, allocator := context.allocator)
/*
P = b1*x^4+ S2*x^3+ S1*x^2+ a1*x + a0;
*/
- internal_shl_digit(b1, 4 * B) or_return
- internal_shl_digit(S2, 3 * B) or_return
+ _private_int_shl_leg(b1, 4 * B) or_return
+ _private_int_shl_leg(S2, 3 * B) or_return
internal_add(b1, b1, S2) or_return
- internal_shl_digit(S1, 2 * B) or_return
+ _private_int_shl_leg(S1, 2 * B) or_return
internal_add(b1, b1, S1) or_return
- internal_shl_digit(a1, 1 * B) or_return
+ _private_int_shl_leg(a1, 1 * B) or_return
internal_add(b1, b1, a1) or_return
internal_add(dest, b1, a0) or_return
@@ -317,8 +319,8 @@ _private_int_mul_karatsuba :: proc(dest, a, b: ^Int, allocator := context.alloca
/*
shift by B.
*/
- internal_shl_digit(t1, B) or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<= n then x = x - n
@@ -2026,7 +2028,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
/*
q1 = x / b**(k-1)
*/
- internal_shr_digit(q, um - 1)
+ _private_int_shr_leg(q, um - 1)
/*
According to HAC this optimization is ok.
@@ -2040,7 +2042,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
/*
q3 = q2 / b**(k+1)
*/
- internal_shr_digit(q, um + 1)
+ _private_int_shr_leg(q, um + 1)
/*
x = x mod b**(k+1), quick (no division)
@@ -2062,7 +2064,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
*/
if internal_is_negative(x) {
internal_set(q, 1) or_return
- internal_shl_digit(q, um + 1) or_return
+ _private_int_shl_leg(q, um + 1) or_return
internal_add(x, x, q) or_return
}
@@ -3192,6 +3194,74 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int, offset := int(0)) ->
return nil
}
+
+/*
+ Shift left by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shl_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ if digits <= 0 { return nil }
+
+ /*
+ No need to shift a zero.
+ */
+ if #force_inline internal_is_zero(quotient) {
+ return nil
+ }
+
+ /*
+ Resize `quotient` to accomodate extra digits.
+ */
+ #force_inline internal_grow(quotient, quotient.used + digits) or_return
+
+ /*
+ Increment the used by the shift amount then copy upwards.
+ */
+
+ /*
+ Much like `_private_int_shr_leg`, this is implemented using a sliding window,
+ except the window goes the other way around.
+ */
+ #no_bounds_check for x := quotient.used; x > 0; x -= 1 {
+ quotient.digit[x+digits-1] = quotient.digit[x-1]
+ }
+
+ quotient.used += digits
+ mem.zero_slice(quotient.digit[:digits])
+ return nil
+}
+
+/*
+ Shift right by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shr_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ if digits <= 0 { return nil }
+
+ /*
+ If digits > used simply zero and return.
+ */
+ if digits > quotient.used { return internal_zero(quotient) }
+
+ /*
+ Much like `int_shl_digit`, this is implemented using a sliding window,
+ except the window goes the other way around.
+
+ b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
+ /\ | ---->
+ \-------------------/ ---->
+ */
+
+ #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
+ quotient.digit[x] = quotient.digit[x + digits]
+ }
+ quotient.used -= digits
+ internal_zero_unused(quotient)
+ return internal_clamp(quotient)
+}
+
/*
======================== End of private procedures =======================
diff --git a/core/math/big/public.odin b/core/math/big/public.odin
index 2673a262f..3227d7bc4 100644
--- a/core/math/big/public.odin
+++ b/core/math/big/public.odin
@@ -8,6 +8,8 @@
This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ...
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin
index 760c49d77..2b758dc35 100644
--- a/core/math/big/radix.odin
+++ b/core/math/big/radix.odin
@@ -12,6 +12,8 @@
- Use Barrett reduction for non-powers-of-two.
- Also look at extracting and splatting several digits at once.
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin
index 8acd8c2c6..c3efc30aa 100644
--- a/core/math/big/rat.odin
+++ b/core/math/big/rat.odin
@@ -42,9 +42,9 @@ rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err:
dst.a.sign = .Negative if f < 0 else .Zero_or_Positive
if shift > 0 {
- internal_int_shl_digit(&dst.b, shift) or_return
+ internal_int_shl(&dst.b, &dst.b, shift) or_return
} else {
- internal_int_shl_digit(&dst.a, -shift) or_return
+ internal_int_shl(&dst.a, &dst.a, -shift) or_return
}
return internal_rat_norm(dst)
@@ -112,14 +112,14 @@ rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err:
assert_if_nil(dst)
context.allocator = allocator
internal_set(&dst.a, x) or_return
- internal_set(&dst.a, 1) or_return
+ internal_set(&dst.b, 1) or_return
return
}
rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dst)
context.allocator = allocator
internal_set(&dst.a, x) or_return
- internal_set(&dst.a, 1) or_return
+ internal_set(&dst.b, 1) or_return
return
}
@@ -265,7 +265,7 @@ rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Er
return
}
- int_sub(&dst.a, &x.a, &y.a) or_return
+ int_mul(&dst.a, &x.a, &y.a) or_return
internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
return internal_rat_norm(dst)
}
@@ -389,9 +389,9 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
internal_int_abs(b2, b) or_return
if shift := MSIZE2 - exp; shift > 0 {
- internal_int_shl_digit(a2, shift) or_return
- } else {
- internal_int_shl_digit(b2, -shift) or_return
+ internal_int_shl(a2, a2, shift) or_return
+ } else if shift < 0 {
+ internal_int_shl(b2, b2, -shift) or_return
}
q, r := &Int{}, &Int{}
@@ -436,7 +436,7 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
mantissa >>= 1
- f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1)))
+ f = T(math.ldexp(f64(mantissa), exp-MSIZE1))
if math.is_inf(f, 0) {
exact = false
}
diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin
index d67ff61b4..78a20c12b 100644
--- a/core/math/big/tune.odin
+++ b/core/math/big/tune.odin
@@ -1,4 +1,3 @@
-//+ignore
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
@@ -7,6 +6,8 @@
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+//+ignore
package math_big
import "core:time"
diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin
index bff984cc7..850e8038a 100644
--- a/core/math/bits/bits.odin
+++ b/core/math/bits/bits.odin
@@ -69,29 +69,29 @@ rotate_left :: proc(x: uint, k: int) -> uint {
}
from_be_u8 :: proc(i: u8) -> u8 { return i }
-from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
+from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
from_le_u8 :: proc(i: u8) -> u8 { return i }
-from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
+from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
to_be_u8 :: proc(i: u8) -> u8 { return i }
-to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
-to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i } else { return byte_swap(i) } }
+to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
to_le_u8 :: proc(i: u8) -> u8 { return i }
-to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
-to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i } else { return byte_swap(i) } }
+to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin
new file mode 100644
index 000000000..0bd7c3641
--- /dev/null
+++ b/core/math/ease/ease.odin
@@ -0,0 +1,495 @@
+// easing procedures and flux easing used for animations
+package ease
+
+import "core:math"
+import "core:intrinsics"
+import "core:time"
+
+@(private) PI_2 :: math.PI / 2
+
+// converted to odin from https://github.com/warrenm/AHEasing
+// with additional enum based call
+
+// Modeled after the parabola y = x^2
+quadratic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p
+}
+
+// Modeled after the parabola y = -x^2 + 2x
+quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return -(p * (p - 2))
+}
+
+// Modeled after the piecewise quadratic
+// y = (1/2)((2x)^2) ; [0, 0.5)
+// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
+quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 2 * p * p
+ } else {
+ return (-2 * p * p) + (4 * p) - 1
+ }
+}
+
+// Modeled after the cubic y = x^3
+cubic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p
+}
+
+// Modeled after the cubic y = (x - 1)^3 + 1
+cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f + 1
+}
+
+// Modeled after the piecewise cubic
+// y = (1/2)((2x)^3) ; [0, 0.5)
+// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
+cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 4 * p * p * p
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f + 1
+ }
+}
+
+// Modeled after the quartic x^4
+quartic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p * p
+}
+
+// Modeled after the quartic y = 1 - (x - 1)^4
+quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * (1 - p) + 1
+}
+
+// Modeled after the piecewise quartic
+// y = (1/2)((2x)^4) ; [0, 0.5)
+// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
+quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 8 * p * p * p * p
+ } else {
+ f := p - 1
+ return -8 * f * f * f * f + 1
+ }
+}
+
+// Modeled after the quintic y = x^5
+quintic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p * p * p
+}
+
+// Modeled after the quintic y = (x - 1)^5 + 1
+quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * f * f + 1
+}
+
+// Modeled after the piecewise quintic
+// y = (1/2)((2x)^5) ; [0, 0.5)
+// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
+quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 16 * p * p * p * p * p
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f * f * f + 1
+ }
+}
+
+// Modeled after quarter-cycle of sine wave
+sine_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin((p - 1) * PI_2) + 1
+}
+
+// Modeled after quarter-cycle of sine wave (different phase)
+sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(p * PI_2)
+}
+
+// Modeled after half sine wave
+sine_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 0.5 * (1 - math.cos(p * math.PI))
+}
+
+// Modeled after shifted quadrant IV of unit circle
+circular_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 1 - math.sqrt(1 - (p * p))
+}
+
+// Modeled after shifted quadrant II of unit circle
+circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sqrt((2 - p) * p)
+}
+
+// Modeled after the piecewise circular function
+// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
+// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
+circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * (1 - math.sqrt(1 - 4 * (p * p)))
+ } else {
+ return 0.5 * (math.sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1)
+ }
+}
+
+// Modeled after the exponential function y = 2^(10(x - 1))
+exponential_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p == 0.0 ? p : math.pow(2, 10 * (p - 1))
+}
+
+// Modeled after the exponential function y = -2^(-10x) + 1
+exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p == 1.0 ? p : 1 - math.pow(2, -10 * p)
+}
+
+// Modeled after the piecewise exponential
+// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
+// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
+exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p == 0.0 || p == 1.0 {
+ return p
+ }
+
+ if p < 0.5 {
+ return 0.5 * math.pow(2, (20 * p) - 10)
+ } else {
+ return -0.5 * math.pow(2, (-20 * p) + 10) + 1
+ }
+}
+
+// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
+elastic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(13 * PI_2 * p) * math.pow(2, 10 * (p - 1))
+}
+
+// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
+elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(-13 * PI_2 * (p + 1)) * math.pow(2, -10 * p) + 1
+}
+
+// Modeled after the piecewise exponentially-damped sine wave:
+// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
+// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
+elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * math.sin(13 * PI_2 * (2 * p)) * math.pow(2, 10 * ((2 * p) - 1))
+ } else {
+ return 0.5 * (math.sin(-13 * PI_2 * ((2 * p - 1) + 1)) * math.pow(2, -10 * (2 * p - 1)) + 2)
+ }
+}
+
+// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
+back_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p - p * math.sin(p * math.PI)
+}
+
+// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
+back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := 1 - p
+ return 1 - (f * f * f - f * math.sin(f * math.PI))
+}
+
+// Modeled after the piecewise overshooting cubic function:
+// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
+// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
+back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ f := 2 * p
+ return 0.5 * (f * f * f - f * math.sin(f * math.PI))
+ } else {
+ f := (1 - (2*p - 1))
+ return 0.5 * (1 - (f * f * f - f * math.sin(f * math.PI))) + 0.5
+ }
+}
+
+bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 1 - bounce_out(1 - p)
+}
+
+bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 4/11.0 {
+ return (121 * p * p)/16.0
+ } else if p < 8/11.0 {
+ return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0
+ } else if p < 9/10.0 {
+ return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0
+ } else {
+ return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0
+ }
+}
+
+bounce_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * bounce_in(p*2)
+ } else {
+ return 0.5 * bounce_out(p * 2 - 1) + 0.5
+ }
+}
+
+// additional enum variant
+
+Ease :: enum {
+ Linear,
+
+ Quadratic_In,
+ Quadratic_Out,
+ Quadratic_In_Out,
+
+ Cubic_In,
+ Cubic_Out,
+ Cubic_In_Out,
+
+ Quartic_In,
+ Quartic_Out,
+ Quartic_In_Out,
+
+ Quintic_In,
+ Quintic_Out,
+ Quintic_In_Out,
+
+ Sine_In,
+ Sine_Out,
+ Sine_In_Out,
+
+ Circular_In,
+ Circular_Out,
+ Circular_In_Out,
+
+ Exponential_In,
+ Exponential_Out,
+ Exponential_In_Out,
+
+ Elastic_In,
+ Elastic_Out,
+ Elastic_In_Out,
+
+ Back_In,
+ Back_Out,
+ Back_In_Out,
+
+ Bounce_In,
+ Bounce_Out,
+ Bounce_In_Out,
+}
+
+ease :: proc "contextless" (type: Ease, p: $T) -> T
+ where intrinsics.type_is_float(T) {
+ switch type {
+ case .Linear: return p
+
+ case .Quadratic_In: return quadratic_in(p)
+ case .Quadratic_Out: return quadratic_out(p)
+ case .Quadratic_In_Out: return quadratic_in_out(p)
+
+ case .Cubic_In: return cubic_in(p)
+ case .Cubic_Out: return cubic_out(p)
+ case .Cubic_In_Out: return cubic_in_out(p)
+
+ case .Quartic_In: return quartic_in(p)
+ case .Quartic_Out: return quartic_out(p)
+ case .Quartic_In_Out: return quartic_in_out(p)
+
+ case .Quintic_In: return quintic_in(p)
+ case .Quintic_Out: return quintic_out(p)
+ case .Quintic_In_Out: return quintic_in_out(p)
+
+ case .Sine_In: return sine_in(p)
+ case .Sine_Out: return sine_out(p)
+ case .Sine_In_Out: return sine_in_out(p)
+
+ case .Circular_In: return circular_in(p)
+ case .Circular_Out: return circular_out(p)
+ case .Circular_In_Out: return circular_in_out(p)
+
+ case .Exponential_In: return exponential_in(p)
+ case .Exponential_Out: return exponential_out(p)
+ case .Exponential_In_Out: return exponential_in_out(p)
+
+ case .Elastic_In: return elastic_in(p)
+ case .Elastic_Out: return elastic_out(p)
+ case .Elastic_In_Out: return elastic_in_out(p)
+
+ case .Back_In: return back_in(p)
+ case .Back_Out: return back_out(p)
+ case .Back_In_Out: return back_in_out(p)
+
+ case .Bounce_In: return bounce_in(p)
+ case .Bounce_Out: return bounce_out(p)
+ case .Bounce_In_Out: return bounce_in_out(p)
+ }
+
+ // in case type was invalid
+ return 0
+}
+Flux_Map :: struct($T: typeid) {
+ values: map[^T]Flux_Tween(T),
+ keys_to_be_deleted: [dynamic]^T,
+}
+
+Flux_Tween :: struct($T: typeid) {
+ value: ^T,
+ start: T,
+ diff: T,
+ goal: T,
+
+ delay: f64, // in seconds
+ duration: time.Duration,
+
+ progress: f64,
+ rate: f64,
+ type: Ease,
+
+ inited: bool,
+
+ // callbacks, data can be set, will be pushed to callback
+ data: rawptr, // by default gets set to value input
+ on_start: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_update: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_complete: proc(flux: ^Flux_Map(T), data: rawptr),
+}
+
+// init flux map to a float type and a wanted cap
+flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
+ return {
+ values = make(map[^T]Flux_Tween(T), value_capacity),
+ keys_to_be_deleted = make([dynamic]^T, 0, value_capacity)
+ }
+}
+
+// delete map content
+flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
+ delete(flux.values)
+ delete(flux.keys_to_be_deleted)
+}
+
+// clear map content, stops all animations
+flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
+ clear(&flux.values)
+}
+
+// append / overwrite existing tween value to parameters
+// rest is initialized in flux_tween_init, inside update
+// return value can be used to set callbacks
+flux_to :: proc(
+ flux: ^Flux_Map($T),
+ value: ^T,
+ goal: T,
+ type: Ease = .Quadratic_Out,
+ duration: time.Duration = time.Second,
+ delay: f64 = 0,
+) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) {
+ if res, ok := &flux.values[value]; ok {
+ tween = res
+ } else {
+ flux.values[value] = {}
+ tween = &flux.values[value]
+ }
+
+ tween^ = {
+ value = value,
+ goal = goal,
+ duration = duration,
+ delay = delay,
+ type = type,
+ data = value,
+ }
+
+ return
+}
+
+// init internal properties
+flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) {
+ tween.inited = true
+ tween.start = tween.value^
+ tween.diff = tween.goal - tween.value^
+ s := time.duration_seconds(duration)
+ tween.rate = duration > 0 ? 1.0 / s : 0
+ tween.progress = duration > 0 ? 0 : 1
+}
+
+// update all tweens, wait for their delay if one exists
+// calls callbacks in all stages, when they're filled
+// deletes tween from the map after completion
+flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
+ clear(&flux.keys_to_be_deleted)
+
+ for key, tween in &flux.values {
+ delay_remainder := f64(0)
+
+ // Update delay if necessary.
+ if tween.delay > 0 {
+ tween.delay -= dt
+
+ if tween.delay < 0 {
+ // We finished the delay, but in doing so consumed part of this frame's `dt` budget.
+ // Keep track of it so we can apply it to this tween without affecting others.
+ delay_remainder = tween.delay
+ // We're done with this delay.
+ tween.delay = 0
+ }
+ }
+
+ // We either had no delay, or the delay has been consumed.
+ if tween.delay <= 0 {
+ if !tween.inited {
+ flux_tween_init(&tween, tween.duration)
+
+ if tween.on_start != nil {
+ tween.on_start(flux, tween.data)
+ }
+ }
+
+ // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
+ // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
+ // to the tween so it advances properly, instead of too much or little.
+ tween.progress += tween.rate * (dt + delay_remainder)
+ x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
+ tween.value^ = tween.start + tween.diff * T(x)
+
+ if tween.on_update != nil {
+ tween.on_update(flux, tween.data)
+ }
+
+ if tween.progress >= 1 {
+ // append keys to array that will be deleted after the loop
+ append(&flux.keys_to_be_deleted, key)
+
+ if tween.on_complete != nil {
+ tween.on_complete(flux, tween.data)
+ }
+ }
+ }
+ }
+
+ // loop through keys that should be deleted from the map
+ if len(flux.keys_to_be_deleted) != 0 {
+ for key in flux.keys_to_be_deleted {
+ delete_key(&flux.values, key)
+ }
+ }
+}
+
+// stop a specific key inside the map
+// returns true when it successfully removed the key
+flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
+ if key in flux.values {
+ delete_key(&flux.values, key)
+ return true
+ }
+
+ return false
+}
+
+// returns the amount of time left for the tween animation, if the key exists in the map
+// returns 0 if the tween doesnt exist on the map
+flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 {
+ if tween, ok := flux.values[key]; ok {
+ return ((1 - tween.progress) * tween.rate) + tween.delay
+ } else {
+ return 0
+ }
+}
diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin
index ba64356ce..24b7a90fc 100644
--- a/core/math/linalg/extended.odin
+++ b/core/math/linalg/extended.odin
@@ -103,10 +103,10 @@ max :: proc{max_single, max_double, max_triple}
abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. E where IS_NUMERIC(E) {
}
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
+ 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)
+ dv := dot(N, I)
k := 1 - eta*eta - (1 - dv*dv)
- a := i * eta
- b := n * eta*dv*math.sqrt(k)
+ a := I * eta
+ b := N * eta*dv*math.sqrt(k)
return (a - b) * E(int(k >= 0))
}
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index 6c594945f..9f22fa45e 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -1,6 +1,7 @@
package linalg
import "core:math"
+import "core:builtin"
import "core:intrinsics"
// Generic
@@ -60,14 +61,7 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
inner_product :: dot
-outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0)
@@ -163,65 +157,28 @@ identity :: proc($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
return m
}
-trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
- for i in 0.. (m: (T when N == M else [M][N]E)) #no_bounds_check {
- for j in 0.. (c: M)
+matrix_mul :: proc(a, b: $M/matrix[$N, N]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. (c: M)
+matrix_comp_mul :: proc(a, b: $M/matrix[$I, $J]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for j in 0.. (c: [K][I]E)
+matrix_mul_differ :: proc(a: $A/matrix[$I, $J]$E, b: $B/matrix[J, $K]E) -> (c: matrix[I, K]E)
where !IS_ARRAY(E), IS_NUMERIC(E), I != K #no_bounds_check {
- for k in 0.. (c: B)
+matrix_mul_vector :: proc(a: $A/matrix[$I, $J]$E, b: $B/[J]E) -> (c: B)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. Q where IS_QUATERNION(Q) {
@@ -270,8 +227,8 @@ mul :: proc{
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
return &v[0]
}
-matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
- return &m[0][0]
+matrix_to_ptr :: proc(m: ^$A/matrix[$I, $J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
+ return &m[0, 0]
}
to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
@@ -330,10 +287,10 @@ array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bo
return
}
-matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) #no_bounds_check {
- for i in 0.. (w: matrix[M, N]Elem_Type) #no_bounds_check {
+ for j in 0.. [N]uint { return array_cast(v, ui
to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32) }
to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64) }
to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128) }
-to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64) }
+to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64) }
to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128) }
to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256) }
diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 3b4976452..74753f66f 100644
--- a/core/math/linalg/glsl/linalg_glsl.odin
+++ b/core/math/linalg/glsl/linalg_glsl.odin
@@ -473,6 +473,25 @@ floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), fl
floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+
+round :: proc{
+ round_f32,
+ round_f64,
+ round_vec2,
+ round_vec3,
+ round_vec4,
+ round_dvec2,
+ round_dvec3,
+ round_dvec4,
+}
+round_vec2 :: proc "c" (x: vec2) -> vec2 { return {round(x.x), round(x.y)} }
+round_vec3 :: proc "c" (x: vec3) -> vec3 { return {round(x.x), round(x.y), round(x.z)} }
+round_vec4 :: proc "c" (x: vec4) -> vec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+round_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {round(x.x), round(x.y)} }
+round_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {round(x.x), round(x.y), round(x.z)} }
+round_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+
+
ceil :: proc{
ceil_f32,
ceil_f64,
@@ -693,23 +712,22 @@ saturate :: proc{
saturate_uvec3,
saturate_uvec4,
}
-saturate_i32 :: proc "c" (x, y, z: i32) -> i32 { return builtin.clamp(x, 0, 1) }
-saturate_u32 :: proc "c" (x, y, z: u32) -> u32 { return builtin.clamp(x, 0, 1) }
-saturate_f32 :: proc "c" (x, y, z: f32) -> f32 { return builtin.clamp(x, 0, 1) }
-saturate_f64 :: proc "c" (x, y, z: f64) -> f64 { return builtin.clamp(x, 0, 1) }
-saturate_vec2 :: proc "c" (x, y, z: vec2) -> vec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_vec3 :: proc "c" (x, y, z: vec3) -> vec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_vec4 :: proc "c" (x, y, z: vec4) -> vec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_dvec2 :: proc "c" (x, y, z: dvec2) -> dvec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_dvec3 :: proc "c" (x, y, z: dvec3) -> dvec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_dvec4 :: proc "c" (x, y, z: dvec4) -> dvec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_ivec2 :: proc "c" (x, y, z: ivec2) -> ivec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_ivec3 :: proc "c" (x, y, z: ivec3) -> ivec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_ivec4 :: proc "c" (x, y, z: ivec4) -> ivec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_uvec2 :: proc "c" (x, y, z: uvec2) -> uvec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_uvec3 :: proc "c" (x, y, z: uvec3) -> uvec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_uvec4 :: proc "c" (x, y, z: uvec4) -> uvec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-
+saturate_i32 :: proc "c" (v: i32) -> i32 { return builtin.clamp(v, 0, 1) }
+saturate_u32 :: proc "c" (v: u32) -> u32 { return builtin.clamp(v, 0, 1) }
+saturate_f32 :: proc "c" (v: f32) -> f32 { return builtin.clamp(v, 0, 1) }
+saturate_f64 :: proc "c" (v: f64) -> f64 { return builtin.clamp(v, 0, 1) }
+saturate_vec2 :: proc "c" (v: vec2) -> vec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_vec3 :: proc "c" (v: vec3) -> vec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_vec4 :: proc "c" (v: vec4) -> vec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_dvec2 :: proc "c" (v: dvec2) -> dvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_dvec3 :: proc "c" (v: dvec3) -> dvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_dvec4 :: proc "c" (v: dvec4) -> dvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_ivec2 :: proc "c" (v: ivec2) -> ivec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_ivec3 :: proc "c" (v: ivec3) -> ivec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_ivec4 :: proc "c" (v: ivec4) -> ivec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_uvec2 :: proc "c" (v: uvec2) -> uvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_uvec3 :: proc "c" (v: uvec3) -> uvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_uvec4 :: proc "c" (v: uvec4) -> uvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
mix :: proc{
mix_f32,
@@ -1597,7 +1615,7 @@ quatNlerp :: proc "c" (a, b: quat, t: f32) -> (c: quat) {
c.y = a.y + (b.y-a.y)*t
c.z = a.z + (b.z-a.z)*t
c.w = a.w + (b.w-a.w)*t
- return c/builtin.abs(c)
+ return c/quat(builtin.abs(c))
}
quatSlerp :: proc "c" (x, y: quat, t: f32) -> (q: quat) {
@@ -1699,7 +1717,7 @@ dquatNlerp :: proc "c" (a, b: dquat, t: f64) -> (c: dquat) {
c.y = a.y + (b.y-a.y)*t
c.z = a.z + (b.z-a.z)*t
c.w = a.w + (b.w-a.w)*t
- return c/builtin.abs(c)
+ return c/dquat(builtin.abs(c))
}
dquatSlerp :: proc "c" (x, y: dquat, t: f64) -> (q: dquat) {
diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin
index 68f43a2f7..968a3fa5e 100644
--- a/core/math/linalg/glsl/linalg_glsl_math.odin
+++ b/core/math/linalg/glsl/linalg_glsl_math.odin
@@ -23,6 +23,7 @@ log_f32 :: proc "c" (x: f32) -> f32 { return math.ln(x) }
exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
+round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
fract_f32 :: proc "c" (x: f32) -> f32 {
@@ -53,6 +54,7 @@ log_f64 :: proc "c" (x: f64) -> f64 { return math.ln(x) }
exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
+round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
fract_f64 :: proc "c" (x: f64) -> f64 {
diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin
index 0eb8413a9..3f73dcd1f 100644
--- a/core/math/linalg/hlsl/linalg_hlsl.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl.odin
@@ -551,6 +551,23 @@ floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.
floor_double3 :: proc "c" (x: double3) -> double3 { return {floor(x.x), floor(x.y), floor(x.z)} }
floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+round :: proc{
+ round_float,
+ round_double,
+ round_float2,
+ round_float3,
+ round_float4,
+ round_double2,
+ round_double3,
+ round_double4,
+}
+round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} }
+round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} }
+round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+round_double2 :: proc "c" (x: double2) -> double2 { return {round(x.x), round(x.y)} }
+round_double3 :: proc "c" (x: double3) -> double3 { return {round(x.x), round(x.y), round(x.z)} }
+round_double4 :: proc "c" (x: double4) -> double4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+
ceil :: proc{
ceil_float,
@@ -570,6 +587,69 @@ ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y),
ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+isfinite_float :: proc "c" (x: float) -> bool { return !isinf_float(x) }
+isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} }
+isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} }
+isfinite_float4 :: proc "c" (x: float4) -> bool4 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z), isfinite_float(x.w)} }
+isfinite_double :: proc "c" (x: double) -> bool { return !isinf_double(x) }
+isfinite_double2 :: proc "c" (x: double2) -> bool2 { return {isfinite_double(x.x), isfinite_double(x.y)} }
+isfinite_double3 :: proc "c" (x: double3) -> bool3 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z)} }
+isfinite_double4 :: proc "c" (x: double4) -> bool4 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z), isfinite_double(x.w)} }
+
+// isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite
+isfinite :: proc{
+ isfinite_float,
+ isfinite_float2,
+ isfinite_float3,
+ isfinite_float4,
+ isfinite_double,
+ isfinite_double2,
+ isfinite_double3,
+ isfinite_double4,
+}
+
+
+isinf_float :: proc "c" (x: float) -> bool { return x * 0.5 == x }
+isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} }
+isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} }
+isinf_float4 :: proc "c" (x: float4) -> bool4 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z), isinf_float(x.w)} }
+isinf_double :: proc "c" (x: double) -> bool { return x * 0.5 == x }
+isinf_double2 :: proc "c" (x: double2) -> bool2 { return {isinf_double(x.x), isinf_double(x.y)} }
+isinf_double3 :: proc "c" (x: double3) -> bool3 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z)} }
+isinf_double4 :: proc "c" (x: double4) -> bool4 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z), isinf_double(x.w)} }
+
+// isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite
+isinf :: proc{
+ isinf_float,
+ isinf_float2,
+ isinf_float3,
+ isinf_float4,
+ isinf_double,
+ isinf_double2,
+ isinf_double3,
+ isinf_double4,
+}
+
+
+isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} }
+isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} }
+isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} }
+isnan_double2 :: proc "c" (x: double2) -> bool2 { return {isnan_double(x.x), isnan_double(x.y)} }
+isnan_double3 :: proc "c" (x: double3) -> bool3 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z)} }
+isnan_double4 :: proc "c" (x: double4) -> bool4 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z), isnan_double(x.w)} }
+
+// isnan returns true if the input value is the special case of Not-A-Number
+isnan :: proc{
+ isnan_float,
+ isnan_float2,
+ isnan_float3,
+ isnan_float4,
+ isnan_double,
+ isnan_double2,
+ isnan_double3,
+ isnan_double4,
+}
+
fmod :: proc{
fmod_float,
fmod_double,
@@ -772,22 +852,22 @@ saturate :: proc{
saturate_uint3,
saturate_uint4,
}
-saturate_int :: proc "c" (x, y, z: int) -> int { return builtin.clamp(x, 0, 1) }
-saturate_uint :: proc "c" (x, y, z: uint) -> uint { return builtin.clamp(x, 0, 1) }
-saturate_float :: proc "c" (x, y, z: float) -> float { return builtin.clamp(x, 0, 1) }
-saturate_double :: proc "c" (x, y, z: double) -> double { return builtin.clamp(x, 0, 1) }
-saturate_float2 :: proc "c" (x, y, z: float2) -> float2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_float3 :: proc "c" (x, y, z: float3) -> float3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_float4 :: proc "c" (x, y, z: float4) -> float4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_double2 :: proc "c" (x, y, z: double2) -> double2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_double3 :: proc "c" (x, y, z: double3) -> double3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_double4 :: proc "c" (x, y, z: double4) -> double4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_int2 :: proc "c" (x, y, z: int2) -> int2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_int3 :: proc "c" (x, y, z: int3) -> int3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_int4 :: proc "c" (x, y, z: int4) -> int4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+saturate_int :: proc "c" (v: int) -> int { return builtin.clamp(v, 0, 1) }
+saturate_uint :: proc "c" (v: uint) -> uint { return builtin.clamp(v, 0, 1) }
+saturate_float :: proc "c" (v: float) -> float { return builtin.clamp(v, 0, 1) }
+saturate_double :: proc "c" (v: double) -> double { return builtin.clamp(v, 0, 1) }
+saturate_float2 :: proc "c" (v: float2) -> float2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_float3 :: proc "c" (v: float3) -> float3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_float4 :: proc "c" (v: float4) -> float4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_double2 :: proc "c" (v: double2) -> double2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_double3 :: proc "c" (v: double3) -> double3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_double4 :: proc "c" (v: double4) -> double4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_int2 :: proc "c" (v: int2) -> int2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_int3 :: proc "c" (v: int3) -> int3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_int4 :: proc "c" (v: int4) -> int4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+saturate_uint2 :: proc "c" (v: uint2) -> uint2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+saturate_uint3 :: proc "c" (v: uint3) -> uint3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+saturate_uint4 :: proc "c" (v: uint4) -> uint4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
lerp :: proc{
diff --git a/core/math/linalg/hlsl/linalg_hlsl_math.odin b/core/math/linalg/hlsl/linalg_hlsl_math.odin
index d884c3d31..91c542b59 100644
--- a/core/math/linalg/hlsl/linalg_hlsl_math.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl_math.odin
@@ -26,7 +26,9 @@ log10_float :: proc "c" (x: float) -> float { return math.log(x, 10) }
exp2_float :: proc "c" (x: float) -> float { return math.pow(float(2), x) }
sign_float :: proc "c" (x: float) -> float { return math.sign(x) }
floor_float :: proc "c" (x: float) -> float { return math.floor(x) }
+round_float :: proc "c" (x: float) -> float { return math.round(x) }
ceil_float :: proc "c" (x: float) -> float { return math.ceil(x) }
+isnan_float :: proc "c" (x: float) -> bool { return math.classify(x) == .NaN}
fmod_float :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
frac_float :: proc "c" (x: float) -> float {
if x >= 0 {
@@ -35,6 +37,7 @@ frac_float :: proc "c" (x: float) -> float {
return math.trunc(-x) + x
}
+
cos_double :: proc "c" (x: double) -> double { return math.cos(x) }
sin_double :: proc "c" (x: double) -> double { return math.sin(x) }
tan_double :: proc "c" (x: double) -> double { return math.tan(x) }
@@ -59,7 +62,9 @@ log10_double :: proc "c" (x: double) -> double { return math.log(x, 10)
exp2_double :: proc "c" (x: double) -> double { return math.pow(double(2), x) }
sign_double :: proc "c" (x: double) -> double { return math.sign(x) }
floor_double :: proc "c" (x: double) -> double { return math.floor(x) }
+round_double :: proc "c" (x: double) -> double { return math.round(x) }
ceil_double :: proc "c" (x: double) -> double { return math.ceil(x) }
+isnan_double :: proc "c" (x: double) -> bool { return math.classify(x) == .NaN}
fmod_double :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
frac_double :: proc "c" (x: double) -> double {
if x >= 0 {
diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin
index 3729b0954..c4ecb194f 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -1,5 +1,6 @@
package linalg
+import "core:builtin"
import "core:math"
F16_EPSILON :: 1e-3
@@ -10,25 +11,25 @@ Vector2f16 :: distinct [2]f16
Vector3f16 :: distinct [3]f16
Vector4f16 :: distinct [4]f16
-Matrix1x1f16 :: distinct [1][1]f16
-Matrix1x2f16 :: distinct [1][2]f16
-Matrix1x3f16 :: distinct [1][3]f16
-Matrix1x4f16 :: distinct [1][4]f16
+Matrix1x1f16 :: distinct matrix[1, 1]f16
+Matrix1x2f16 :: distinct matrix[1, 2]f16
+Matrix1x3f16 :: distinct matrix[1, 3]f16
+Matrix1x4f16 :: distinct matrix[1, 4]f16
-Matrix2x1f16 :: distinct [2][1]f16
-Matrix2x2f16 :: distinct [2][2]f16
-Matrix2x3f16 :: distinct [2][3]f16
-Matrix2x4f16 :: distinct [2][4]f16
+Matrix2x1f16 :: distinct matrix[2, 1]f16
+Matrix2x2f16 :: distinct matrix[2, 2]f16
+Matrix2x3f16 :: distinct matrix[2, 3]f16
+Matrix2x4f16 :: distinct matrix[2, 4]f16
-Matrix3x1f16 :: distinct [3][1]f16
-Matrix3x2f16 :: distinct [3][2]f16
-Matrix3x3f16 :: distinct [3][3]f16
-Matrix3x4f16 :: distinct [3][4]f16
+Matrix3x1f16 :: distinct matrix[3, 1]f16
+Matrix3x2f16 :: distinct matrix[3, 2]f16
+Matrix3x3f16 :: distinct matrix[3, 3]f16
+Matrix3x4f16 :: distinct matrix[3, 4]f16
-Matrix4x1f16 :: distinct [4][1]f16
-Matrix4x2f16 :: distinct [4][2]f16
-Matrix4x3f16 :: distinct [4][3]f16
-Matrix4x4f16 :: distinct [4][4]f16
+Matrix4x1f16 :: distinct matrix[4, 1]f16
+Matrix4x2f16 :: distinct matrix[4, 2]f16
+Matrix4x3f16 :: distinct matrix[4, 3]f16
+Matrix4x4f16 :: distinct matrix[4, 4]f16
Matrix1f16 :: Matrix1x1f16
Matrix2f16 :: Matrix2x2f16
@@ -39,25 +40,25 @@ Vector2f32 :: distinct [2]f32
Vector3f32 :: distinct [3]f32
Vector4f32 :: distinct [4]f32
-Matrix1x1f32 :: distinct [1][1]f32
-Matrix1x2f32 :: distinct [1][2]f32
-Matrix1x3f32 :: distinct [1][3]f32
-Matrix1x4f32 :: distinct [1][4]f32
+Matrix1x1f32 :: distinct matrix[1, 1]f32
+Matrix1x2f32 :: distinct matrix[1, 2]f32
+Matrix1x3f32 :: distinct matrix[1, 3]f32
+Matrix1x4f32 :: distinct matrix[1, 4]f32
-Matrix2x1f32 :: distinct [2][1]f32
-Matrix2x2f32 :: distinct [2][2]f32
-Matrix2x3f32 :: distinct [2][3]f32
-Matrix2x4f32 :: distinct [2][4]f32
+Matrix2x1f32 :: distinct matrix[2, 1]f32
+Matrix2x2f32 :: distinct matrix[2, 2]f32
+Matrix2x3f32 :: distinct matrix[2, 3]f32
+Matrix2x4f32 :: distinct matrix[2, 4]f32
-Matrix3x1f32 :: distinct [3][1]f32
-Matrix3x2f32 :: distinct [3][2]f32
-Matrix3x3f32 :: distinct [3][3]f32
-Matrix3x4f32 :: distinct [3][4]f32
+Matrix3x1f32 :: distinct matrix[3, 1]f32
+Matrix3x2f32 :: distinct matrix[3, 2]f32
+Matrix3x3f32 :: distinct matrix[3, 3]f32
+Matrix3x4f32 :: distinct matrix[3, 4]f32
-Matrix4x1f32 :: distinct [4][1]f32
-Matrix4x2f32 :: distinct [4][2]f32
-Matrix4x3f32 :: distinct [4][3]f32
-Matrix4x4f32 :: distinct [4][4]f32
+Matrix4x1f32 :: distinct matrix[4, 1]f32
+Matrix4x2f32 :: distinct matrix[4, 2]f32
+Matrix4x3f32 :: distinct matrix[4, 3]f32
+Matrix4x4f32 :: distinct matrix[4, 4]f32
Matrix1f32 :: Matrix1x1f32
Matrix2f32 :: Matrix2x2f32
@@ -68,25 +69,25 @@ Vector2f64 :: distinct [2]f64
Vector3f64 :: distinct [3]f64
Vector4f64 :: distinct [4]f64
-Matrix1x1f64 :: distinct [1][1]f64
-Matrix1x2f64 :: distinct [1][2]f64
-Matrix1x3f64 :: distinct [1][3]f64
-Matrix1x4f64 :: distinct [1][4]f64
+Matrix1x1f64 :: distinct matrix[1, 1]f64
+Matrix1x2f64 :: distinct matrix[1, 2]f64
+Matrix1x3f64 :: distinct matrix[1, 3]f64
+Matrix1x4f64 :: distinct matrix[1, 4]f64
-Matrix2x1f64 :: distinct [2][1]f64
-Matrix2x2f64 :: distinct [2][2]f64
-Matrix2x3f64 :: distinct [2][3]f64
-Matrix2x4f64 :: distinct [2][4]f64
+Matrix2x1f64 :: distinct matrix[2, 1]f64
+Matrix2x2f64 :: distinct matrix[2, 2]f64
+Matrix2x3f64 :: distinct matrix[2, 3]f64
+Matrix2x4f64 :: distinct matrix[2, 4]f64
-Matrix3x1f64 :: distinct [3][1]f64
-Matrix3x2f64 :: distinct [3][2]f64
-Matrix3x3f64 :: distinct [3][3]f64
-Matrix3x4f64 :: distinct [3][4]f64
+Matrix3x1f64 :: distinct matrix[3, 1]f64
+Matrix3x2f64 :: distinct matrix[3, 2]f64
+Matrix3x3f64 :: distinct matrix[3, 3]f64
+Matrix3x4f64 :: distinct matrix[3, 4]f64
-Matrix4x1f64 :: distinct [4][1]f64
-Matrix4x2f64 :: distinct [4][2]f64
-Matrix4x3f64 :: distinct [4][3]f64
-Matrix4x4f64 :: distinct [4][4]f64
+Matrix4x1f64 :: distinct matrix[4, 1]f64
+Matrix4x2f64 :: distinct matrix[4, 2]f64
+Matrix4x3f64 :: distinct matrix[4, 3]f64
+Matrix4x4f64 :: distinct matrix[4, 4]f64
Matrix1f64 :: Matrix1x1f64
Matrix2f64 :: Matrix2x2f64
@@ -97,20 +98,20 @@ Quaternionf16 :: distinct quaternion64
Quaternionf32 :: distinct quaternion128
Quaternionf64 :: distinct quaternion256
-MATRIX1F16_IDENTITY :: Matrix1f16{{1}}
-MATRIX2F16_IDENTITY :: Matrix2f16{{1, 0}, {0, 1}}
-MATRIX3F16_IDENTITY :: Matrix3f16{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F16_IDENTITY :: Matrix4f16{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F16_IDENTITY :: Matrix1f16(1)
+MATRIX2F16_IDENTITY :: Matrix2f16(1)
+MATRIX3F16_IDENTITY :: Matrix3f16(1)
+MATRIX4F16_IDENTITY :: Matrix4f16(1)
-MATRIX1F32_IDENTITY :: Matrix1f32{{1}}
-MATRIX2F32_IDENTITY :: Matrix2f32{{1, 0}, {0, 1}}
-MATRIX3F32_IDENTITY :: Matrix3f32{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F32_IDENTITY :: Matrix4f32{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F32_IDENTITY :: Matrix1f32(1)
+MATRIX2F32_IDENTITY :: Matrix2f32(1)
+MATRIX3F32_IDENTITY :: Matrix3f32(1)
+MATRIX4F32_IDENTITY :: Matrix4f32(1)
-MATRIX1F64_IDENTITY :: Matrix1f64{{1}}
-MATRIX2F64_IDENTITY :: Matrix2f64{{1, 0}, {0, 1}}
-MATRIX3F64_IDENTITY :: Matrix3f64{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F64_IDENTITY :: Matrix4f64{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F64_IDENTITY :: Matrix1f64(1)
+MATRIX2F64_IDENTITY :: Matrix2f64(1)
+MATRIX3F64_IDENTITY :: Matrix3f64(1)
+MATRIX4F64_IDENTITY :: Matrix4f64(1)
QUATERNIONF16_IDENTITY :: Quaternionf16(1)
QUATERNIONF32_IDENTITY :: Quaternionf32(1)
@@ -475,24 +476,24 @@ quaternion_angle_axis :: proc{
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
if abs(q.w) > math.SQRT_THREE*0.5 {
- return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
+ return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
if abs(q.w) > math.SQRT_THREE*0.5 {
- return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
+ return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
if abs(q.w) > math.SQRT_THREE*0.5 {
- return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
+ return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion :: proc{
angle_from_quaternion_f16,
@@ -558,9 +559,9 @@ quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f16{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -571,26 +572,26 @@ quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -601,9 +602,9 @@ quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f32{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -614,26 +615,26 @@ quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -644,9 +645,9 @@ quaternion_from_forward_and_up_f64 :: proc(forward, up: Vector3f64) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f64{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -657,26 +658,26 @@ quaternion_from_forward_and_up_f64 :: proc(forward, up: Vector3f64) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -842,23 +843,23 @@ quaternion_squad :: proc{
quaternion_from_matrix4_f16 :: proc(m: Matrix4f16) -> (q: Quaternionf16) {
m3: Matrix3f16 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ m3[0, 2], m3[1, 2], m3[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return quaternion_from_matrix3(m3)
}
quaternion_from_matrix4_f32 :: proc(m: Matrix4f32) -> (q: Quaternionf32) {
m3: Matrix3f32 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ m3[0, 2], m3[1, 2], m3[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return quaternion_from_matrix3(m3)
}
quaternion_from_matrix4_f64 :: proc(m: Matrix4f64) -> (q: Quaternionf64) {
m3: Matrix3f64 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ m3[0, 2], m3[1, 2], m3[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return quaternion_from_matrix3(m3)
}
quaternion_from_matrix4 :: proc{
@@ -869,10 +870,10 @@ quaternion_from_matrix4 :: proc{
quaternion_from_matrix3_f16 :: proc(m: Matrix3f16) -> (q: Quaternionf16) {
- four_x_squared_minus_1 := m[0][0] - m[1][1] - m[2][2]
- four_y_squared_minus_1 := m[1][1] - m[0][0] - m[2][2]
- four_z_squared_minus_1 := m[2][2] - m[0][0] - m[1][1]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -896,32 +897,32 @@ quaternion_from_matrix3_f16 :: proc(m: Matrix3f16) -> (q: Quaternionf16) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
}
quaternion_from_matrix3_f32 :: proc(m: Matrix3f32) -> (q: Quaternionf32) {
- four_x_squared_minus_1 := m[0][0] - m[1][1] - m[2][2]
- four_y_squared_minus_1 := m[1][1] - m[0][0] - m[2][2]
- four_z_squared_minus_1 := m[2][2] - m[0][0] - m[1][1]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -945,32 +946,32 @@ quaternion_from_matrix3_f32 :: proc(m: Matrix3f32) -> (q: Quaternionf32) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
}
quaternion_from_matrix3_f64 :: proc(m: Matrix3f64) -> (q: Quaternionf64) {
- four_x_squared_minus_1 := m[0][0] - m[1][1] - m[2][2]
- four_y_squared_minus_1 := m[1][1] - m[0][0] - m[2][2]
- four_z_squared_minus_1 := m[2][2] - m[0][0] - m[1][1]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -994,23 +995,23 @@ quaternion_from_matrix3_f64 :: proc(m: Matrix3f64) -> (q: Quaternionf64) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
@@ -1093,30 +1094,30 @@ quaternion_between_two_vector3 :: proc{
matrix2_inverse_transpose_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose :: proc{
@@ -1127,13 +1128,13 @@ matrix2_inverse_transpose :: proc{
matrix2_determinant_f16 :: proc(m: Matrix2f16) -> f16 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant_f32 :: proc(m: Matrix2f32) -> f32 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant_f64 :: proc(m: Matrix2f64) -> f64 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant :: proc{
matrix2_determinant_f16,
@@ -1143,30 +1144,30 @@ matrix2_determinant :: proc{
matrix2_inverse_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse :: proc{
@@ -1177,24 +1178,24 @@ matrix2_inverse :: proc{
matrix2_adjoint_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ c[0, 0] = +m[1, 1]
+ c[1, 0] = -m[0, 1]
+ c[0, 1] = -m[1, 0]
+ c[1, 1] = +m[0, 0]
return c
}
matrix2_adjoint_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ c[0, 0] = +m[1, 1]
+ c[1, 0] = -m[0, 1]
+ c[0, 1] = -m[1, 0]
+ c[1, 1] = +m[0, 0]
return c
}
matrix2_adjoint_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ c[0, 0] = +m[1, 1]
+ c[1, 0] = -m[0, 1]
+ c[0, 1] = -m[1, 0]
+ c[1, 1] = +m[0, 0]
return c
}
matrix2_adjoint :: proc{
@@ -1215,17 +1216,17 @@ matrix3_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix3f16) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
@@ -1239,17 +1240,17 @@ matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix3f64) {
@@ -1263,17 +1264,17 @@ matrix3_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix3f64) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion :: proc{
@@ -1300,21 +1301,21 @@ matrix3_inverse :: proc{
matrix3_determinant_f16 :: proc(m: Matrix3f16) -> f16 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
+ b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
+ c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
matrix3_determinant_f32 :: proc(m: Matrix3f32) -> f32 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
+ b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
+ c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
matrix3_determinant_f64 :: proc(m: Matrix3f64) -> f64 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
+ b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
+ c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
matrix3_determinant :: proc{
@@ -1325,39 +1326,39 @@ matrix3_determinant :: proc{
matrix3_adjoint_f16 :: proc(m: Matrix3f16) -> (adjoint: Matrix3f16) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
matrix3_adjoint_f32 :: proc(m: Matrix3f32) -> (adjoint: Matrix3f32) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
matrix3_adjoint_f64 :: proc(m: Matrix3f64) -> (adjoint: Matrix3f64) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
matrix3_adjoint :: proc{
@@ -1369,37 +1370,13 @@ matrix3_adjoint :: proc{
matrix3_inverse_transpose_f16 :: proc(m: Matrix3f16) -> (inverse_transpose: Matrix3f16) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose_f32 :: proc(m: Matrix3f32) -> (inverse_transpose: Matrix3f32) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose_f64 :: proc(m: Matrix3f64) -> (inverse_transpose: Matrix3f64) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose :: proc{
matrix3_inverse_transpose_f16,
@@ -1409,21 +1386,21 @@ matrix3_inverse_transpose :: proc{
matrix3_scale_f16 :: proc(s: Vector3f16) -> (m: Matrix3f16) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ m[0, 0] = s[0]
+ m[1, 1] = s[1]
+ m[2, 2] = s[2]
return m
}
matrix3_scale_f32 :: proc(s: Vector3f32) -> (m: Matrix3f32) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ m[0, 0] = s[0]
+ m[1, 1] = s[1]
+ m[2, 2] = s[2]
return m
}
matrix3_scale_f64 :: proc(s: Vector3f64) -> (m: Matrix3f64) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ m[0, 0] = s[0]
+ m[1, 1] = s[1]
+ m[2, 2] = s[2]
return m
}
matrix3_scale :: proc{
@@ -1440,17 +1417,17 @@ matrix3_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f1
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1461,17 +1438,17 @@ matrix3_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f3
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1482,17 +1459,17 @@ matrix3_rotate_f64 :: proc(angle_radians: f64, v: Vector3f64) -> (rot: Matrix3f6
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1508,9 +1485,9 @@ matrix3_look_at_f16 :: proc(eye, centre, up: Vector3f16) -> Matrix3f16 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f16{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
@@ -1518,9 +1495,9 @@ matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f32{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at_f64 :: proc(eye, centre, up: Vector3f64) -> Matrix3f64 {
@@ -1528,9 +1505,9 @@ matrix3_look_at_f64 :: proc(eye, centre, up: Vector3f64) -> Matrix3f64 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f64{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at :: proc{
@@ -1551,19 +1528,19 @@ matrix4_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix4f16) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1578,19 +1555,19 @@ matrix4_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix4f32) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1605,19 +1582,19 @@ matrix4_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix4f64) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1992,10 +1969,10 @@ matrix4_look_at_f16 :: proc(eye, centre, up: Vector3f16, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
@@ -2006,10 +1983,10 @@ matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_f64 :: proc(eye, centre, up: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
@@ -2020,10 +1997,10 @@ matrix4_look_at_f64 :: proc(eye, centre, up: Vector3f64, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at :: proc{
@@ -2033,13 +2010,62 @@ matrix4_look_at :: proc{
}
+matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
+ f, s, u := f, r, u
+ f = normalize(f)
+ s = normalize(s)
+ u = normalize(u)
+ fe := dot(f, eye)
+
+ return {
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
+ }
+}
+matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
+ f, s, u := f, r, u
+ f = normalize(f)
+ s = normalize(s)
+ u = normalize(u)
+ fe := dot(f, eye)
+
+ return {
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
+ }
+}
+matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
+ f, s, u := f, r, u
+ f = normalize(f)
+ s = normalize(s)
+ u = normalize(u)
+ fe := dot(f, eye)
+
+ return {
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
+ }
+}
+matrix4_look_at_from_fru :: proc{
+ matrix4_look_at_from_fru_f16,
+ matrix4_look_at_from_fru_f32,
+ matrix4_look_at_from_fru_f64,
+}
+
+
matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2049,11 +2075,11 @@ matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := tru
}
matrix4_perspective_f32 :: proc(fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2063,11 +2089,11 @@ matrix4_perspective_f32 :: proc(fovy, aspect, near, far: f32, flip_z_axis := tru
}
matrix4_perspective_f64 :: proc(fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2084,13 +2110,13 @@ matrix4_perspective :: proc{
matrix_ortho3d_f16 :: proc(left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
- m[0][0] = +2 / (right - left)
- m[1][1] = +2 / (top - bottom)
- m[2][2] = +2 / (far - near)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2099,13 +2125,13 @@ matrix_ortho3d_f16 :: proc(left, right, bottom, top, near, far: f16, flip_z_axis
return
}
matrix_ortho3d_f32 :: proc(left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
- m[0][0] = +2 / (right - left)
- m[1][1] = +2 / (top - bottom)
- m[2][2] = +2 / (far - near)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2114,13 +2140,13 @@ matrix_ortho3d_f32 :: proc(left, right, bottom, top, near, far: f32, flip_z_axis
return
}
matrix_ortho3d_f64 :: proc(left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
- m[0][0] = +2 / (right - left)
- m[1][1] = +2 / (top - bottom)
- m[2][2] = +2 / (far - near)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2138,11 +2164,11 @@ matrix_ortho3d :: proc{
matrix4_infinite_perspective_f16 :: proc(fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2152,11 +2178,11 @@ matrix4_infinite_perspective_f16 :: proc(fovy, aspect, near: f16, flip_z_axis :=
}
matrix4_infinite_perspective_f32 :: proc(fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2166,11 +2192,11 @@ matrix4_infinite_perspective_f32 :: proc(fovy, aspect, near: f32, flip_z_axis :=
}
matrix4_infinite_perspective_f64 :: proc(fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) {
tan_half_fovy := math.tan(0.5 * fovy)
- m[0][0] = 1 / (aspect*tan_half_fovy)
- m[1][1] = 1 / (tan_half_fovy)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2187,18 +2213,18 @@ matrix4_infinite_perspective :: proc{
matrix2_from_scalar_f16 :: proc(f: f16) -> (m: Matrix2f16) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ m[0, 0], m[1, 0] = f, 0
+ m[0, 1], m[1, 1] = 0, f
return
}
matrix2_from_scalar_f32 :: proc(f: f32) -> (m: Matrix2f32) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ m[0, 0], m[1, 0] = f, 0
+ m[0, 1], m[1, 1] = 0, f
return
}
matrix2_from_scalar_f64 :: proc(f: f64) -> (m: Matrix2f64) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ m[0, 0], m[1, 0] = f, 0
+ m[0, 1], m[1, 1] = 0, f
return
}
matrix2_from_scalar :: proc{
@@ -2209,21 +2235,21 @@ matrix2_from_scalar :: proc{
matrix3_from_scalar_f16 :: proc(f: f16) -> (m: Matrix3f16) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
+ m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
+ m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
matrix3_from_scalar_f32 :: proc(f: f32) -> (m: Matrix3f32) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
+ m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
+ m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
matrix3_from_scalar_f64 :: proc(f: f64) -> (m: Matrix3f64) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
+ m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
+ m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
matrix3_from_scalar :: proc{
@@ -2234,24 +2260,24 @@ matrix3_from_scalar :: proc{
matrix4_from_scalar_f16 :: proc(f: f16) -> (m: Matrix4f16) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
+ m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
+ m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
+ m[0, 3], m[1, 3], m[2, 3], m[3, 3] = 0, 0, 0, f
return
}
matrix4_from_scalar_f32 :: proc(f: f32) -> (m: Matrix4f32) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
+ m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
+ m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
+ m[0, 3], m[1, 3], m[2, 3], m[3, 3] = 0, 0, 0, f
return
}
matrix4_from_scalar_f64 :: proc(f: f64) -> (m: Matrix4f64) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
+ m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
+ m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
+ m[0, 3], m[1, 3], m[2, 3], m[3, 3] = 0, 0, 0, f
return
}
matrix4_from_scalar :: proc{
@@ -2262,18 +2288,18 @@ matrix4_from_scalar :: proc{
matrix2_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix2f16) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix3_f32 :: proc(m: Matrix3f32) -> (r: Matrix2f32) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix3_f64 :: proc(m: Matrix3f64) -> (r: Matrix2f64) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix3 :: proc{
@@ -2284,18 +2310,18 @@ matrix2_from_matrix3 :: proc{
matrix2_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix2f16) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix4_f32 :: proc(m: Matrix4f32) -> (r: Matrix2f32) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix4_f64 :: proc(m: Matrix4f64) -> (r: Matrix2f64) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
+ r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
matrix2_from_matrix4 :: proc{
@@ -2306,21 +2332,21 @@ matrix2_from_matrix4 :: proc{
matrix3_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix3f16) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
+ r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
matrix3_from_matrix2_f32 :: proc(m: Matrix2f32) -> (r: Matrix3f32) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
+ r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
matrix3_from_matrix2_f64 :: proc(m: Matrix2f64) -> (r: Matrix3f64) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
+ r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
matrix3_from_matrix2 :: proc{
@@ -2331,21 +2357,21 @@ matrix3_from_matrix2 :: proc{
matrix3_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix3f16) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
matrix3_from_matrix4_f32 :: proc(m: Matrix4f32) -> (r: Matrix3f32) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
matrix3_from_matrix4_f64 :: proc(m: Matrix4f64) -> (r: Matrix3f64) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
+ r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
+ r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
matrix3_from_matrix4 :: proc{
@@ -2356,24 +2382,24 @@ matrix3_from_matrix4 :: proc{
matrix4_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix4f16) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix2_f32 :: proc(m: Matrix2f32) -> (r: Matrix4f32) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix2_f64 :: proc(m: Matrix2f64) -> (r: Matrix4f64) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix2 :: proc{
@@ -2384,24 +2410,24 @@ matrix4_from_matrix2 :: proc{
matrix4_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix4f16) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix3_f32 :: proc(m: Matrix3f32) -> (r: Matrix4f32) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix3_f64 :: proc(m: Matrix3f64) -> (r: Matrix4f64) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
+ r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
+ r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
+ r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
matrix4_from_matrix3 :: proc{
diff --git a/core/math/linalg/specific_euler_angles_f16.odin b/core/math/linalg/specific_euler_angles_f16.odin
index d0fb1beb3..9e21c7f97 100644
--- a/core/math/linalg/specific_euler_angles_f16.odin
+++ b/core/math/linalg/specific_euler_angles_f16.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix3f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix3f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ matrix3_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix3f16) {
matrix3_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x: f16) -> (m: Matrix3f16) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y: f16) -> (m: Matrix3f16) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z: f16) -> (m: Matrix3f16) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ matrix3_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z:
matrix3_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ matrix3_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix3f1
matrix3_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f16
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
matrix4_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix4f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix4f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ matrix4_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix4f16) {
matrix4_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x: f16) -> (m: Matrix4f16) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y: f16) -> (m: Matrix4f16) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z: f16) -> (m: Matrix4f16) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ matrix4_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z:
matrix4_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ matrix4_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix4f1
matrix4_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f16
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/specific_euler_angles_f32.odin b/core/math/linalg/specific_euler_angles_f32.odin
index 6ae1b0fa0..80e19ce85 100644
--- a/core/math/linalg/specific_euler_angles_f32.odin
+++ b/core/math/linalg/specific_euler_angles_f32.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix3f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix3f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ matrix3_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix3f32) {
matrix3_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x: f32) -> (m: Matrix3f32) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y: f32) -> (m: Matrix3f32) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z: f32) -> (m: Matrix3f32) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ matrix3_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z:
matrix3_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ matrix3_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix3f3
matrix3_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f32
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
matrix4_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix4f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix4f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ matrix4_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix4f32) {
matrix4_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x: f32) -> (m: Matrix4f32) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y: f32) -> (m: Matrix4f32) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z: f32) -> (m: Matrix4f32) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ matrix4_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z:
matrix4_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ matrix4_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix4f3
matrix4_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f32
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/specific_euler_angles_f64.odin b/core/math/linalg/specific_euler_angles_f64.odin
index efaddd651..2f8f758b0 100644
--- a/core/math/linalg/specific_euler_angles_f64.odin
+++ b/core/math/linalg/specific_euler_angles_f64.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix3f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix3f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ matrix3_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix3f64) {
matrix3_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x: f64) -> (m: Matrix3f64) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y: f64) -> (m: Matrix3f64) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z: f64) -> (m: Matrix3f64) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ matrix3_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z:
matrix3_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ matrix3_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix3f6
matrix3_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f64
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
matrix4_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix4f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix4f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ matrix4_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix4f64) {
matrix4_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x: f64) -> (m: Matrix4f64) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y: f64) -> (m: Matrix4f64) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z: f64) -> (m: Matrix4f64) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ matrix4_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z:
matrix4_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ matrix4_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix4f6
matrix4_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = cos_y
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f64
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ T1 := math.atan2(m[1, 2], m[2, 2])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
+ T2 := math.atan2(-m[0, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ T1 := math.atan2(m[0, 2], m[2, 2])
+ C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
+ T2 := math.atan2(-m[1, 2], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[2, 0], m[1, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ T1 := math.atan2(m[1, 0], -m[2, 0])
+ S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(S2, m[0, 0])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[0, 1], m[2, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ T1 := math.atan2(m[2, 1], -m[0, 1])
+ S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(S2, m[1, 1])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[1, 2], m[0, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ T1 := math.atan2(m[0, 2], -m[1, 2])
+ S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
+ T2 := math.atan2(S2, m[2, 2])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ T1 := math.atan2(m[2, 1], m[1, 1])
+ C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
+ T2 := math.atan2(-m[0, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ T1 := math.atan2(-m[2, 0], m[0, 0])
+ C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
+ T2 := math.atan2(m[1, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ T1 := math.atan2(m[1, 0], m[0, 0])
+ C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(-m[2, 0], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ T1 := math.atan2(-m[0, 1], m[1, 1])
+ C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
+ T2 := math.atan2(m[2, 1], C2)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/swizzle.odin b/core/math/linalg/swizzle.odin
index f035a5276..ada4aebcf 100644
--- a/core/math/linalg/swizzle.odin
+++ b/core/math/linalg/swizzle.odin
@@ -1,5 +1,10 @@
package linalg
+/*
+ These procedures are to allow for swizzling with non-compile (runtime) known components
+*/
+
+
Scalar_Components :: enum u8 {
x = 0,
r = 0,
diff --git a/core/math/math.odin b/core/math/math.odin
index 512577906..88cba965a 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -96,18 +96,6 @@ fmuladd :: proc{
fmuladd_f64, fmuladd_f64le, fmuladd_f64be,
}
-ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f16(f16(x))) }
-ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f16(f16(x))) }
-ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f32(f32(x))) }
-ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f32(f32(x))) }
-ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
-ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
-ln :: proc{
- ln_f16, ln_f16le, ln_f16be,
- ln_f32, ln_f32le, ln_f32be,
- ln_f64, ln_f64le, ln_f64be,
-}
-
exp_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(exp_f16(f16(x))) }
exp_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(exp_f16(f16(x))) }
exp_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(exp_f32(f32(x))) }
@@ -120,13 +108,60 @@ exp :: proc{
exp_f64, exp_f64le, exp_f64be,
}
-ldexp_f16le :: proc "contextless" (val: f16le, exp: i32) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
-ldexp_f16be :: proc "contextless" (val: f16be, exp: i32) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
-ldexp_f32le :: proc "contextless" (val: f32le, exp: i32) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
-ldexp_f32be :: proc "contextless" (val: f32be, exp: i32) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
-ldexp_f64le :: proc "contextless" (val: f64le, exp: i32) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
-ldexp_f64be :: proc "contextless" (val: f64be, exp: i32) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
-ldexp :: proc{
+
+
+ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
+ mask :: F64_MASK
+ shift :: F64_SHIFT
+ bias :: F64_BIAS
+
+ switch {
+ case val == 0:
+ return val
+ case is_inf(val) || is_nan(val):
+ return val
+ }
+ exp := exp
+ frac, e := normalize_f64(val)
+ exp += e
+ x := transmute(u64)frac
+ exp += int(x>>shift)&mask - bias
+ if exp < -1075 { // underflow
+ return copy_sign(0, frac)
+ } else if exp > 1023 { // overflow
+ if frac < 0 {
+ return inf_f64(-1)
+ }
+ return inf_f64(+1)
+ }
+
+ m: f64 = 1
+ if exp < -1022 { // denormal
+ exp += 53
+ m = 1.0 / (1<<53)
+ }
+ x &~= mask << shift
+ x |= u64(exp+bias) << shift
+ return m * transmute(f64)x
+}
+ldexp_f16 :: proc "contextless" (val: f16, exp: int) -> f16 { return f16(ldexp_f64(f64(val), exp)) }
+ldexp_f32 :: proc "contextless" (val: f32, exp: int) -> f32 { return f32(ldexp_f64(f64(val), exp)) }
+ldexp_f16le :: proc "contextless" (val: f16le, exp: int) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
+ldexp_f16be :: proc "contextless" (val: f16be, exp: int) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
+ldexp_f32le :: proc "contextless" (val: f32le, exp: int) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
+ldexp_f32be :: proc "contextless" (val: f32be, exp: int) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
+ldexp_f64le :: proc "contextless" (val: f64le, exp: int) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
+ldexp_f64be :: proc "contextless" (val: f64be, exp: int) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
+// ldexp is the inverse of frexp
+// it returns val * 2**exp.
+//
+// Special cases:
+// ldexp(+0, exp) = +0
+// ldexp(-0, exp) = -0
+// ldexp(+inf, exp) = +inf
+// ldexp(-inf, exp) = -inf
+// ldexp(NaN, exp) = NaN
+ldexp :: proc{
ldexp_f16, ldexp_f16le, ldexp_f16be,
ldexp_f32, ldexp_f32le, ldexp_f32be,
ldexp_f64, ldexp_f64le, ldexp_f64be,
@@ -150,22 +185,16 @@ log :: proc{
log_f64, log_f64le, log_f64be,
}
-log2_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN2 }
-log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log2_f16(f16(x))) }
-log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log2_f16(f16(x))) }
-
-log2_f32 :: proc "contextless" (x: f32) -> f32 { return ln(x)/LN2 }
-log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log2_f32(f32(x))) }
-log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log2_f32(f32(x))) }
-
-log2_f64 :: proc "contextless" (x: f64) -> f64 { return ln(x)/LN2 }
-log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log2_f64(f64(x))) }
-log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log2_f64(f64(x))) }
-log2 :: proc{
- log2_f16, log2_f16le, log2_f16be,
- log2_f32, log2_f32le, log2_f32be,
- log2_f64, log2_f64le, log2_f64be,
-}
+log2_f16 :: logb_f16
+log2_f16le :: logb_f16le
+log2_f16be :: logb_f16be
+log2_f32 :: logb_f32
+log2_f32le :: logb_f32le
+log2_f32be :: logb_f32be
+log2_f64 :: logb_f64
+log2_f64le :: logb_f64le
+log2_f64be :: logb_f64be
+log2 :: logb
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
@@ -351,9 +380,9 @@ to_degrees :: proc{
trunc_f16 :: proc "contextless" (x: f16) -> f16 {
trunc_internal :: proc "contextless" (f: f16) -> f16 {
- mask :: 0x1f
- shift :: 16 - 6
- bias :: 0xf
+ mask :: F16_MASK
+ shift :: F16_SHIFT
+ bias :: F16_BIAS
if f < 1 {
switch {
@@ -367,7 +396,7 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f16)x
}
@@ -383,9 +412,9 @@ trunc_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16
trunc_f32 :: proc "contextless" (x: f32) -> f32 {
trunc_internal :: proc "contextless" (f: f32) -> f32 {
- mask :: 0xff
- shift :: 32 - 9
- bias :: 0x7f
+ mask :: F32_MASK
+ shift :: F32_SHIFT
+ bias :: F32_BIAS
if f < 1 {
switch {
@@ -399,7 +428,7 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f32)x
}
@@ -415,9 +444,9 @@ trunc_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32
trunc_f64 :: proc "contextless" (x: f64) -> f64 {
trunc_internal :: proc "contextless" (f: f64) -> f64 {
- mask :: 0x7ff
- shift :: 64 - 12
- bias :: 0x3ff
+ mask :: F64_MASK
+ shift :: F64_SHIFT
+ bias :: F64_BIAS
if f < 1 {
switch {
@@ -431,7 +460,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f64)x
}
@@ -444,6 +473,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
}
trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) }
trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) }
+// Removes the fractional part of the value, i.e. rounds towards zero.
trunc :: proc{
trunc_f16, trunc_f16le, trunc_f16be,
trunc_f32, trunc_f32le, trunc_f32be,
@@ -578,9 +608,9 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
}
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
- shift :: 16 - 5 - 1
- mask :: 0x1f
- bias :: 15
+ shift :: F16_SHIFT
+ mask :: F16_MASK
+ bias :: F16_BIAS
if x < 1 {
switch {
@@ -612,9 +642,9 @@ modf_f16be :: proc "contextless" (x: f16be) -> (int: f16be, frac: f16be) {
return f16be(i), f16be(f)
}
modf_f32 :: proc "contextless" (x: f32) -> (int: f32, frac: f32) {
- shift :: 32 - 8 - 1
- mask :: 0xff
- bias :: 127
+ shift :: F32_SHIFT
+ mask :: F32_MASK
+ bias :: F32_BIAS
if x < 1 {
switch {
@@ -645,10 +675,10 @@ modf_f32be :: proc "contextless" (x: f32be) -> (int: f32be, frac: f32be) {
i, f := #force_inline modf_f32(f32(x))
return f32be(i), f32be(f)
}
-modf_f64 :: proc "contextless" (x: f64) -> (int: f64, frac: f64) {
- shift :: 64 - 11 - 1
- mask :: 0x7ff
- bias :: 1023
+modf_f64 :: proc "contextless" (x: f64) -> (int: f64, frac: f64) {
+ shift :: F64_SHIFT
+ mask :: F64_MASK
+ bias :: F64_BIAS
if x < 1 {
switch {
@@ -679,7 +709,7 @@ modf_f64be :: proc "contextless" (x: f64be) -> (int: f64be, frac: f64be) {
i, f := #force_inline modf_f64(f64(x))
return f64be(i), f64be(f)
}
-modf :: proc{
+modf :: proc{
modf_f16, modf_f16le, modf_f16be,
modf_f32, modf_f32le, modf_f32be,
modf_f64, modf_f64le, modf_f64be,
@@ -752,6 +782,44 @@ lcm :: proc "contextless" (x, y: $T) -> T
return x / gcd(x, y) * y
}
+normalize_f16 :: proc "contextless" (x: f16) -> (y: f16, exponent: int) {
+ if abs(x) < F16_MIN {
+ return x * (1< (y: f32, exponent: int) {
+ if abs(x) < F32_MIN {
+ return x * (1< (y: f64, exponent: int) {
+ if abs(x) < F64_MIN {
+ return x * (1< (y: f16le, exponent: int) { y0, e := normalize_f16(f16(x)); return f16le(y0), e }
+normalize_f16be :: proc "contextless" (x: f16be) -> (y: f16be, exponent: int) { y0, e := normalize_f16(f16(x)); return f16be(y0), e }
+normalize_f32le :: proc "contextless" (x: f32le) -> (y: f32le, exponent: int) { y0, e := normalize_f32(f32(x)); return f32le(y0), e }
+normalize_f32be :: proc "contextless" (x: f32be) -> (y: f32be, exponent: int) { y0, e := normalize_f32(f32(x)); return f32be(y0), e }
+normalize_f64le :: proc "contextless" (x: f64le) -> (y: f64le, exponent: int) { y0, e := normalize_f64(f64(x)); return f64le(y0), e }
+normalize_f64be :: proc "contextless" (x: f64be) -> (y: f64be, exponent: int) { y0, e := normalize_f64(f64(x)); return f64be(y0), e }
+
+normalize :: proc{
+ normalize_f16,
+ normalize_f32,
+ normalize_f64,
+ normalize_f16le,
+ normalize_f16be,
+ normalize_f32le,
+ normalize_f32be,
+ normalize_f64le,
+ normalize_f64be,
+}
+
frexp_f16 :: proc "contextless" (x: f16) -> (significand: f16, exponent: int) {
f, e := frexp_f64(f64(x))
return f16(f), e
@@ -776,24 +844,25 @@ frexp_f32be :: proc "contextless" (x: f32be) -> (significand: f32be, exponent: i
f, e := frexp_f64(f64(x))
return f32be(f), e
}
-frexp_f64 :: proc "contextless" (x: f64) -> (significand: f64, exponent: int) {
+frexp_f64 :: proc "contextless" (f: f64) -> (significand: f64, exponent: int) {
+ mask :: F64_MASK
+ shift :: F64_SHIFT
+ bias :: F64_BIAS
+
switch {
- case x == 0:
+ case f == 0:
return 0, 0
- case x < 0:
- significand, exponent = frexp(-x)
- return -significand, exponent
- }
- ex := trunc(log2(x))
- exponent = int(ex)
- significand = x / pow(2.0, ex)
- if abs(significand) >= 1 {
- exponent += 1
- significand /= 2
- }
- if exponent == 1024 && significand == 0 {
- significand = 0.99999999999999988898
+ case is_inf(f) || is_nan(f):
+ return f, 0
}
+ f := f
+
+ f, exponent = normalize_f64(f)
+ x := transmute(u64)f
+ exponent += int((x>>shift)&mask) - bias + 1
+ x &~= mask << shift
+ x |= (-1 + bias) << shift
+ significand = transmute(f64)x
return
}
frexp_f64le :: proc "contextless" (x: f64le) -> (significand: f64le, exponent: int) {
@@ -804,7 +873,18 @@ frexp_f64be :: proc "contextless" (x: f64be) -> (significand: f64be, exponent: i
f, e := frexp_f64(f64(x))
return f64be(f), e
}
-frexp :: proc{
+
+// frexp breaks the value into a normalized fraction, and an integral power of two
+// It returns a significand and exponent satisfying x == significand * 2**exponent
+// with the absolute value of significand in the intervalue of [0.5, 1).
+//
+// Special cases:
+// frexp(+0) = +0, 0
+// frexp(-0) = -0, 0
+// frexp(+inf) = +inf, 0
+// frexp(-inf) = -inf, 0
+// frexp(NaN) = NaN, 0
+frexp :: proc{
frexp_f16, frexp_f16le, frexp_f16be,
frexp_f32, frexp_f32le, frexp_f32be,
frexp_f64, frexp_f64le, frexp_f64be,
@@ -879,7 +959,7 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
return .Neg_Zero
}
return .Zero
- case x*0.5 == x:
+ case x*0.25 == x:
if x < 0 {
return .Neg_Inf
}
@@ -948,6 +1028,8 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class {
}
classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) }
classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) }
+// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or
+// negative infinity.
classify :: proc{
classify_f16, classify_f16le, classify_f16be,
classify_f32, classify_f32le, classify_f32be,
@@ -1048,13 +1130,11 @@ inf_f32be :: proc "contextless" (sign: int) -> f32be {
return f32be(inf_f64(sign))
}
inf_f64 :: proc "contextless" (sign: int) -> f64 {
- v: u64
if sign >= 0 {
- v = 0x7ff00000_00000000
+ return 0h7ff00000_00000000
} else {
- v = 0xfff00000_00000000
+ return 0hfff00000_00000000
}
- return transmute(f64)v
}
inf_f64le :: proc "contextless" (sign: int) -> f64le {
return f64le(inf_f64(sign))
@@ -1082,8 +1162,7 @@ nan_f32be :: proc "contextless" () -> f32be {
return f32be(nan_f64())
}
nan_f64 :: proc "contextless" () -> f64 {
- v: u64 = 0x7ff80000_00000001
- return transmute(f64)v
+ return 0h7ff80000_00000001
}
nan_f64le :: proc "contextless" () -> f64le {
return f64le(nan_f64())
@@ -1120,13 +1199,14 @@ sum :: proc "contextless" (x: $T/[]$E) -> (res: E)
prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
where intrinsics.type_is_numeric(E) {
+ res = 1
for i in x {
res *= i
}
return
}
-cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T
+cumsum_inplace :: proc "contextless" (x: $T/[]$E)
where intrinsics.type_is_numeric(E) {
for i in 1.. T where intrinsics.type_is_float(T) {
}
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
- return atan2(x, 1 + sqrt(1 - x*x))
+ return atan2(x, sqrt(1 - x*x))
}
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
@@ -1297,18 +1377,295 @@ tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return (t - 1) / (t + 1)
}
-asinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
- return ln(x + sqrt(x*x + 1))
+asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+ // The original C code, the long comment, and the constants
+ // below are from FreeBSD's /usr/src/lib/msun/src/s_asinh.c
+ // and came with this notice.
+ //
+ // ====================================================
+ // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ //
+ // Developed at SunPro, a Sun Microsystems, Inc. business.
+ // Permission to use, copy, modify, and distribute this
+ // software is freely granted, provided that this notice
+ // is preserved.
+ // ====================================================
+
+ LN2 :: 0h3FE62E42FEFA39EF
+ NEAR_ZERO :: 1.0 / (1 << 28)
+ LARGE :: 1 << 28
+
+ x := f64(y)
+
+ if is_nan(x) || is_inf(x) {
+ return T(x)
+ }
+ sign := false
+ if x < 0 {
+ x = -x
+ sign = true
+ }
+ temp: f64
+ switch {
+ case x > LARGE:
+ temp = ln(x) + LN2
+ case x > 2:
+ temp = ln(2*x + 1/(sqrt(x*x + 1) + x))
+ case x < NEAR_ZERO:
+ temp = x
+ case:
+ temp = log1p(x + x*x/(1 + sqrt(1 + x*x)))
+ }
+
+ if sign {
+ temp = -temp
+ }
+ return T(temp)
}
-acosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
- return ln(x + sqrt(x*x - 1))
+acosh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+ // The original C code, the long comment, and the constants
+ // below are from FreeBSD's /usr/src/lib/msun/src/e_acosh.c
+ // and came with this notice.
+ //
+ // ====================================================
+ // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ //
+ // Developed at SunPro, a Sun Microsystems, Inc. business.
+ // Permission to use, copy, modify, and distribute this
+ // software is freely granted, provided that this notice
+ // is preserved.
+ // ====================================================
+
+ LARGE :: 1<<28
+ LN2 :: 0h3FE62E42FEFA39EF
+ x := f64(y)
+ switch {
+ case x < 1 || is_nan(x):
+ return T(nan_f64())
+ case x == 1:
+ return 0
+ case x >= LARGE:
+ return T(ln(x) + LN2)
+ case x > 2:
+ return T(ln(2*x - 1/(x+sqrt(x*x-1))))
+ }
+ t := x-1
+ return T(log1p(t + sqrt(2*t + t*t)))
}
-atanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
- return 0.5*ln((1+x)/(1-x))
+atanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+ // The original C code, the long comment, and the constants
+ // below are from FreeBSD's /usr/src/lib/msun/src/e_atanh.c
+ // and came with this notice.
+ //
+ // ====================================================
+ // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ //
+ // Developed at SunPro, a Sun Microsystems, Inc. business.
+ // Permission to use, copy, modify, and distribute this
+ // software is freely granted, provided that this notice
+ // is preserved.
+ // ====================================================
+ NEAR_ZERO :: 1.0 / (1 << 28)
+ x := f64(y)
+ switch {
+ case x < -1 || x > 1 || is_nan(x):
+ return T(nan_f64())
+ case x == 1:
+ return T(inf_f64(1))
+ case x == -1:
+ return T(inf_f64(-1))
+ }
+ sign := false
+ if x < 0 {
+ x = -x
+ sign = true
+ }
+ temp: f64
+ switch {
+ case x < NEAR_ZERO:
+ temp = x
+ case x < 0.5:
+ temp = x + x
+ temp = 0.5 * log1p(temp + temp*x/(1-x))
+ case:
+ temp = 0.5 * log1p((x+x)/(1-x))
+ }
+ if sign {
+ temp = -temp
+ }
+ return T(temp)
}
+ilogb_f16 :: proc "contextless" (val: f16) -> int {
+ switch {
+ case val == 0: return int(min(i32))
+ case is_nan(val): return int(max(i32))
+ case is_inf(val): return int(max(i32))
+ }
+ x, exp := normalize_f16(val)
+ return int(((transmute(u16)x)>>F16_SHIFT)&F16_MASK) - F16_BIAS + exp
+}
+ilogb_f32 :: proc "contextless" (val: f32) -> int {
+ switch {
+ case val == 0: return int(min(i32))
+ case is_nan(val): return int(max(i32))
+ case is_inf(val): return int(max(i32))
+ }
+ x, exp := normalize_f32(val)
+ return int(((transmute(u32)x)>>F32_SHIFT)&F32_MASK) - F32_BIAS + exp
+}
+ilogb_f64 :: proc "contextless" (val: f64) -> int {
+ switch {
+ case val == 0: return int(min(i32))
+ case is_nan(val): return int(max(i32))
+ case is_inf(val): return int(max(i32))
+ }
+ x, exp := normalize_f64(val)
+ return int(((transmute(u64)x)>>F64_SHIFT)&F64_MASK) - F64_BIAS + exp
+}
+ilogb_f16le :: proc "contextless" (value: f16le) -> int { return ilogb_f16(f16(value)) }
+ilogb_f16be :: proc "contextless" (value: f16be) -> int { return ilogb_f16(f16(value)) }
+ilogb_f32le :: proc "contextless" (value: f32le) -> int { return ilogb_f32(f32(value)) }
+ilogb_f32be :: proc "contextless" (value: f32be) -> int { return ilogb_f32(f32(value)) }
+ilogb_f64le :: proc "contextless" (value: f64le) -> int { return ilogb_f64(f64(value)) }
+ilogb_f64be :: proc "contextless" (value: f64be) -> int { return ilogb_f64(f64(value)) }
+ilogb :: proc {
+ ilogb_f16,
+ ilogb_f32,
+ ilogb_f64,
+ ilogb_f16le,
+ ilogb_f16be,
+ ilogb_f32le,
+ ilogb_f32be,
+ ilogb_f64le,
+ ilogb_f64be,
+}
+
+logb_f16 :: proc "contextless" (val: f16) -> f16 {
+ switch {
+ case val == 0: return inf_f16(-1)
+ case is_inf(val): return inf_f16(+1)
+ case is_nan(val): return val
+ }
+ return f16(ilogb(val))
+}
+logb_f32 :: proc "contextless" (val: f32) -> f32 {
+ switch {
+ case val == 0: return inf_f32(-1)
+ case is_inf(val): return inf_f32(+1)
+ case is_nan(val): return val
+ }
+ return f32(ilogb(val))
+}
+logb_f64 :: proc "contextless" (val: f64) -> f64 {
+ switch {
+ case val == 0: return inf_f64(-1)
+ case is_inf(val): return inf_f64(+1)
+ case is_nan(val): return val
+ }
+ return f64(ilogb(val))
+}
+logb_f16le :: proc "contextless" (value: f16le) -> f16le { return f16le(logb_f16(f16(value))) }
+logb_f16be :: proc "contextless" (value: f16be) -> f16be { return f16be(logb_f16(f16(value))) }
+logb_f32le :: proc "contextless" (value: f32le) -> f32le { return f32le(logb_f32(f32(value))) }
+logb_f32be :: proc "contextless" (value: f32be) -> f32be { return f32be(logb_f32(f32(value))) }
+logb_f64le :: proc "contextless" (value: f64le) -> f64le { return f64le(logb_f64(f64(value))) }
+logb_f64be :: proc "contextless" (value: f64be) -> f64be { return f64be(logb_f64(f64(value))) }
+logb :: proc {
+ logb_f16,
+ logb_f32,
+ logb_f64,
+ logb_f16le,
+ logb_f16be,
+ logb_f32le,
+ logb_f32be,
+ logb_f64le,
+ logb_f64be,
+}
+
+nextafter_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
+ switch {
+ case is_nan(x) || is_nan(y):
+ r = nan_f16()
+ case x == y:
+ r = x
+ case x == 0:
+ r = copy_sign_f16(1, y)
+ case (y > x) == (x > 0):
+ r = transmute(f16)(transmute(u16)x + 1)
+ case:
+ r = transmute(f16)(transmute(u16)x - 1)
+ }
+ return
+}
+nextafter_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
+ switch {
+ case is_nan(x) || is_nan(y):
+ r = nan_f32()
+ case x == y:
+ r = x
+ case x == 0:
+ r = copy_sign_f32(1, y)
+ case (y > x) == (x > 0):
+ r = transmute(f32)(transmute(u32)x + 1)
+ case:
+ r = transmute(f32)(transmute(u32)x - 1)
+ }
+ return
+}
+nextafter_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
+ switch {
+ case is_nan(x) || is_nan(y):
+ r = nan_f64()
+ case x == y:
+ r = x
+ case x == 0:
+ r = copy_sign_f64(1, y)
+ case (y > x) == (x > 0):
+ r = transmute(f64)(transmute(u64)x + 1)
+ case:
+ r = transmute(f64)(transmute(u64)x - 1)
+ }
+ return
+}
+nextafter_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(nextafter_f16(f16(x), f16(y))) }
+nextafter_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(nextafter_f16(f16(x), f16(y))) }
+nextafter_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(nextafter_f32(f32(x), f32(y))) }
+nextafter_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(nextafter_f32(f32(x), f32(y))) }
+nextafter_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(nextafter_f64(f64(x), f64(y))) }
+nextafter_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(nextafter_f64(f64(x), f64(y))) }
+
+nextafter :: proc{
+ nextafter_f16, nextafter_f16le, nextafter_f16be,
+ nextafter_f32, nextafter_f32le, nextafter_f32be,
+ nextafter_f64, nextafter_f64le, nextafter_f64be,
+}
+
+signbit_f16 :: proc "contextless" (x: f16) -> bool {
+ return (transmute(u16)x)&(1<<15) != 0
+}
+signbit_f32 :: proc "contextless" (x: f32) -> bool {
+ return (transmute(u32)x)&(1<<31) != 0
+}
+signbit_f64 :: proc "contextless" (x: f64) -> bool {
+ return (transmute(u64)x)&(1<<63) != 0
+}
+signbit_f16le :: proc "contextless" (x: f16le) -> bool { return signbit_f16(f16(x)) }
+signbit_f32le :: proc "contextless" (x: f32le) -> bool { return signbit_f32(f32(x)) }
+signbit_f64le :: proc "contextless" (x: f64le) -> bool { return signbit_f64(f64(x)) }
+signbit_f16be :: proc "contextless" (x: f16be) -> bool { return signbit_f16(f16(x)) }
+signbit_f32be :: proc "contextless" (x: f32be) -> bool { return signbit_f32(f32(x)) }
+signbit_f64be :: proc "contextless" (x: f64be) -> bool { return signbit_f64(f64(x)) }
+
+signbit :: proc{
+ signbit_f16, signbit_f16le, signbit_f16be,
+ signbit_f32, signbit_f32le, signbit_f32be,
+ signbit_f64, signbit_f64le, signbit_f64be,
+}
+
+
F16_DIG :: 3
F16_EPSILON :: 0.00097656
F16_GUARD :: 0
@@ -1349,3 +1706,34 @@ F64_MIN_10_EXP :: -307 // min decimal exponent
F64_MIN_EXP :: -1021 // min binary exponent
F64_RADIX :: 2 // exponent radix
F64_ROUNDS :: 1 // addition rounding: near
+
+
+F16_MASK :: 0x1f
+F16_SHIFT :: 16 - 6
+F16_BIAS :: 0xf
+
+F32_MASK :: 0xff
+F32_SHIFT :: 32 - 9
+F32_BIAS :: 0x7f
+
+F64_MASK :: 0x7ff
+F64_SHIFT :: 64 - 12
+F64_BIAS :: 0x3ff
+
+INF_F16 :f16: 0h7C00
+NEG_INF_F16 :f16: 0hFC00
+
+SNAN_F16 :f16: 0h7C01
+QNAN_F16 :f16: 0h7E01
+
+INF_F32 :f32: 0h7F80_0000
+NEG_INF_F32 :f32: 0hFF80_0000
+
+SNAN_F32 :f32: 0hFF80_0001
+QNAN_F32 :f32: 0hFFC0_0001
+
+INF_F64 :f64: 0h7FF0_0000_0000_0000
+NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
+
+SNAN_F64 :f64: 0h7FF0_0000_0000_0001
+QNAN_F64 :f64: 0h7FF8_0000_0000_0001
diff --git a/core/math/math_basic.odin b/core/math/math_basic.odin
index 26bf4691d..c9d2e632d 100644
--- a/core/math/math_basic.odin
+++ b/core/math/math_basic.odin
@@ -1,15 +1,10 @@
//+build !js
package math
+import "core:intrinsics"
+
@(default_calling_convention="none")
foreign _ {
- @(link_name="llvm.sqrt.f16")
- sqrt_f16 :: proc(x: f16) -> f16 ---
- @(link_name="llvm.sqrt.f32")
- sqrt_f32 :: proc(x: f32) -> f32 ---
- @(link_name="llvm.sqrt.f64")
- sqrt_f64 :: proc(x: f64) -> f64 ---
-
@(link_name="llvm.sin.f16")
sin_f16 :: proc(θ: f16) -> f16 ---
@(link_name="llvm.sin.f32")
@@ -38,24 +33,137 @@ foreign _ {
@(link_name="llvm.fmuladd.f64")
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
- @(link_name="llvm.log.f16")
- ln_f16 :: proc(x: f16) -> f16 ---
- @(link_name="llvm.log.f32")
- ln_f32 :: proc(x: f32) -> f32 ---
- @(link_name="llvm.log.f64")
- ln_f64 :: proc(x: f64) -> f64 ---
-
@(link_name="llvm.exp.f16")
exp_f16 :: proc(x: f16) -> f16 ---
@(link_name="llvm.exp.f32")
exp_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.exp.f64")
exp_f64 :: proc(x: f64) -> f64 ---
-
- @(link_name="llvm.ldexp.f16")
- ldexp_f16 :: proc(val: f16, exp: i32) -> f16 ---
- @(link_name="llvm.ldexp.f32")
- ldexp_f32 :: proc(val: f32, exp: i32) -> f32 ---
- @(link_name="llvm.ldexp.f64")
- ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---
}
+
+sqrt_f16 :: proc "contextless" (x: f16) -> f16 {
+ return intrinsics.sqrt(x)
+}
+sqrt_f32 :: proc "contextless" (x: f32) -> f32 {
+ return intrinsics.sqrt(x)
+}
+sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
+ return intrinsics.sqrt(x)
+}
+
+
+
+ln_f64 :: proc "contextless" (x: f64) -> f64 {
+ // The original C code, the long comment, and the constants
+ // below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
+ // and came with this notice.
+ //
+ // ====================================================
+ // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+ //
+ // Developed at SunPro, a Sun Microsystems, Inc. business.
+ // Permission to use, copy, modify, and distribute this
+ // software is freely granted, provided that this notice
+ // is preserved.
+ // ====================================================
+ //
+ // __ieee754_log(x)
+ // Return the logarithm of x
+ //
+ // Method :
+ // 1. Argument Reduction: find k and f such that
+ // x = 2**k * (1+f),
+ // where sqrt(2)/2 < 1+f < sqrt(2) .
+ //
+ // 2. Approximation of log(1+f).
+ // Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+ // = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+ // = 2s + s*R
+ // We use a special Reme algorithm on [0,0.1716] to generate
+ // a polynomial of degree 14 to approximate R. The maximum error
+ // of this polynomial approximation is bounded by 2**-58.45. In
+ // other words,
+ // 2 4 6 8 10 12 14
+ // R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s +L6*s +L7*s
+ // (the values of L1 to L7 are listed in the program) and
+ // | 2 14 | -58.45
+ // | L1*s +...+L7*s - R(z) | <= 2
+ // | |
+ // Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+ // In order to guarantee error in log below 1ulp, we compute log by
+ // log(1+f) = f - s*(f - R) (if f is not too large)
+ // log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
+ //
+ // 3. Finally, log(x) = k*Ln2 + log(1+f).
+ // = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
+ // Here Ln2 is split into two floating point number:
+ // Ln2_hi + Ln2_lo,
+ // where n*Ln2_hi is always exact for |n| < 2000.
+ //
+ // Special cases:
+ // log(x) is NaN with signal if x < 0 (including -INF) ;
+ // log(+INF) is +INF; log(0) is -INF with signal;
+ // log(NaN) is that NaN with no signal.
+ //
+ // Accuracy:
+ // according to an error analysis, the error is always less than
+ // 1 ulp (unit in the last place).
+ //
+ // Constants:
+ // The hexadecimal values are the intended ones for the following
+ // constants. The decimal values may be used, provided that the
+ // compiler will convert from decimal to binary accurately enough
+ // to produce the hexadecimal values shown.
+
+ LN2_HI :: 0h3fe62e42_fee00000 // 6.93147180369123816490e-01
+ LN2_LO :: 0h3dea39ef_35793c76 // 1.90821492927058770002e-10
+ L1 :: 0h3fe55555_55555593 // 6.666666666666735130e-01
+ L2 :: 0h3fd99999_9997fa04 // 3.999999999940941908e-01
+ L3 :: 0h3fd24924_94229359 // 2.857142874366239149e-01
+ L4 :: 0h3fcc71c5_1d8e78af // 2.222219843214978396e-01
+ L5 :: 0h3fc74664_96cb03de // 1.818357216161805012e-01
+ L6 :: 0h3fc39a09_d078c69f // 1.531383769920937332e-01
+ L7 :: 0h3fc2f112_df3e5244 // 1.479819860511658591e-01
+
+ switch {
+ case is_nan(x) || is_inf(x, 1):
+ return x
+ case x < 0:
+ return nan_f64()
+ case x == 0:
+ return inf_f64(-1)
+ }
+
+ // reduce
+ f1, ki := frexp(x)
+ if f1 < SQRT_TWO/2 {
+ f1 *= 2
+ ki -= 1
+ }
+ f := f1 - 1
+ k := f64(ki)
+
+ // compute
+ s := f / (2 + f)
+ s2 := s * s
+ s4 := s2 * s2
+ t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7)))
+ t2 := s4 * (L2 + s4*(L4+s4*L6))
+ R := t1 + t2
+ hfsq := 0.5 * f * f
+ return k*LN2_HI - ((hfsq - (s*(hfsq+R) + k*LN2_LO)) - f)
+}
+
+ln_f16 :: proc "contextless" (x: f16) -> f16 { return #force_inline f16(ln_f64(f64(x))) }
+ln_f32 :: proc "contextless" (x: f32) -> f32 { return #force_inline f32(ln_f64(f64(x))) }
+ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
+ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
+ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
+ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
+ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
+ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
+ln :: proc{
+ ln_f16, ln_f16le, ln_f16be,
+ ln_f32, ln_f32le, ln_f32be,
+ ln_f64, ln_f64le, ln_f64be,
+}
\ No newline at end of file
diff --git a/core/math/math_js.odin b/core/math/math_basic_js.odin
similarity index 70%
rename from core/math/math_js.odin
rename to core/math/math_basic_js.odin
index c2ac1bedf..ec572f898 100644
--- a/core/math/math_js.odin
+++ b/core/math/math_basic_js.odin
@@ -1,12 +1,12 @@
//+build js
package math
+import "core:intrinsics"
+
foreign import "odin_env"
@(default_calling_convention="c")
foreign odin_env {
- @(link_name="sqrt")
- sqrt_f64 :: proc(x: f64) -> f64 ---
@(link_name="sin")
sin_f64 :: proc(θ: f64) -> f64 ---
@(link_name="cos")
@@ -19,10 +19,11 @@ foreign odin_env {
ln_f64 :: proc(x: f64) -> f64 ---
@(link_name="exp")
exp_f64 :: proc(x: f64) -> f64 ---
- @(link_name="ldexp")
- ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---
}
+sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
+ return intrinsics.sqrt(x)
+}
sqrt_f16 :: proc "c" (x: f16) -> f16 { return f16(sqrt_f64(f64(x))) }
sin_f16 :: proc "c" (θ: f16) -> f16 { return f16(sin_f64(f64(θ))) }
@@ -31,7 +32,6 @@ pow_f16 :: proc "c" (x, power: f16) -> f16 { return f16(pow_f64(f64(x),
fmuladd_f16 :: proc "c" (a, b, c: f16) -> f16 { return f16(fmuladd_f64(f64(a), f64(a), f64(c))) }
ln_f16 :: proc "c" (x: f16) -> f16 { return f16(ln_f64(f64(x))) }
exp_f16 :: proc "c" (x: f16) -> f16 { return f16(exp_f64(f64(x))) }
-ldexp_f16 :: proc "c" (val: f16, exp: i32) -> f16 { return f16(ldexp_f64(f64(val), exp) ) }
sqrt_f32 :: proc "c" (x: f32) -> f32 { return f32(sqrt_f64(f64(x))) }
sin_f32 :: proc "c" (θ: f32) -> f32 { return f32(sin_f64(f64(θ))) }
@@ -40,4 +40,15 @@ pow_f32 :: proc "c" (x, power: f32) -> f32 { return f32(pow_f64(f64(x),
fmuladd_f32 :: proc "c" (a, b, c: f32) -> f32 { return f32(fmuladd_f64(f64(a), f64(a), f64(c))) }
ln_f32 :: proc "c" (x: f32) -> f32 { return f32(ln_f64(f64(x))) }
exp_f32 :: proc "c" (x: f32) -> f32 { return f32(exp_f64(f64(x))) }
-ldexp_f32 :: proc "c" (val: f32, exp: i32) -> f32 { return f32(ldexp_f64(f64(val), exp) ) }
\ No newline at end of file
+
+ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
+ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
+ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
+ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
+ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
+ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
+ln :: proc{
+ ln_f16, ln_f16le, ln_f16be,
+ ln_f32, ln_f32le, ln_f32be,
+ ln_f64, ln_f64le, ln_f64be,
+}
diff --git a/core/math/math_erf.odin b/core/math/math_erf.odin
new file mode 100644
index 000000000..cdade59c5
--- /dev/null
+++ b/core/math/math_erf.odin
@@ -0,0 +1,410 @@
+package math
+
+// The original C code and the long comment below are
+// from FreeBSD's /usr/src/lib/msun/src/s_erf.c and
+// came with this notice.
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+//
+// double erf(double x)
+// double erfc(double x)
+// x
+// 2 |\
+// erf(x) = --------- | exp(-t*t)dt
+// sqrt(pi) \|
+// 0
+//
+// erfc(x) = 1-erf(x)
+// Note that
+// erf(-x) = -erf(x)
+// erfc(-x) = 2 - erfc(x)
+//
+// Method:
+// 1. For |x| in [0, 0.84375]
+// erf(x) = x + x*R(x**2)
+// erfc(x) = 1 - erf(x) if x in [-.84375,0.25]
+// = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375]
+// where R = P/Q where P is an odd poly of degree 8 and
+// Q is an odd poly of degree 10.
+// -57.90
+// | R - (erf(x)-x)/x | <= 2
+//
+//
+// Remark. The formula is derived by noting
+// erf(x) = (2/sqrt(pi))*(x - x**3/3 + x**5/10 - x**7/42 + ....)
+// and that
+// 2/sqrt(pi) = 1.128379167095512573896158903121545171688
+// is close to one. The interval is chosen because the fix
+// point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
+// near 0.6174), and by some experiment, 0.84375 is chosen to
+// guarantee the error is less than one ulp for erf.
+//
+// 2. For |x| in [0.84375,1.25], let s = |x| - 1, and
+// c = 0.84506291151 rounded to single (24 bits)
+// erf(x) = sign(x) * (c + P1(s)/Q1(s))
+// erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0
+// 1+(c+P1(s)/Q1(s)) if x < 0
+// |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
+// Remark: here we use the taylor series expansion at x=1.
+// erf(1+s) = erf(1) + s*Poly(s)
+// = 0.845.. + P1(s)/Q1(s)
+// That is, we use rational approximation to approximate
+// erf(1+s) - (c = (single)0.84506291151)
+// Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
+// where
+// P1(s) = degree 6 poly in s
+// Q1(s) = degree 6 poly in s
+//
+// 3. For x in [1.25,1/0.35(~2.857143)],
+// erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
+// erf(x) = 1 - erfc(x)
+// where
+// R1(z) = degree 7 poly in z, (z=1/x**2)
+// S1(z) = degree 8 poly in z
+//
+// 4. For x in [1/0.35,28]
+// erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
+// = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6 x >= 28
+// erf(x) = sign(x) *(1 - tiny) (raise inexact)
+// erfc(x) = tiny*tiny (raise underflow) if x > 0
+// = 2 - tiny if x<0
+//
+// 7. Special case:
+// erf(0) = 0, erf(inf) = 1, erf(-inf) = -1,
+// erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
+// erfc/erf(NaN) is NaN
+
+erf :: proc{
+ erf_f16,
+ erf_f16le,
+ erf_f16be,
+ erf_f32,
+ erf_f32le,
+ erf_f32be,
+ erf_f64,
+}
+
+erf_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erf_f64(f64(x))) }
+erf_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erf_f64(f64(x))) }
+erf_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erf_f64(f64(x))) }
+erf_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erf_f64(f64(x))) }
+erf_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erf_f64(f64(x))) }
+erf_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erf_f64(f64(x))) }
+
+erf_f64 :: proc "contextless" (x: f64) -> f64 {
+ erx :: 0h3FEB0AC160000000
+ // Coefficients for approximation to erf in [0, 0.84375]
+ efx :: 0h3FC06EBA8214DB69
+ efx8 :: 0h3FF06EBA8214DB69
+ pp0 :: 0h3FC06EBA8214DB68
+ pp1 :: 0hBFD4CD7D691CB913
+ pp2 :: 0hBF9D2A51DBD7194F
+ pp3 :: 0hBF77A291236668E4
+ pp4 :: 0hBEF8EAD6120016AC
+ qq1 :: 0h3FD97779CDDADC09
+ qq2 :: 0h3FB0A54C5536CEBA
+ qq3 :: 0h3F74D022C4D36B0F
+ qq4 :: 0h3F215DC9221C1A10
+ qq5 :: 0hBED09C4342A26120
+ // Coefficients for approximation to erf in [0.84375, 1.25]
+ pa0 :: 0hBF6359B8BEF77538
+ pa1 :: 0h3FDA8D00AD92B34D
+ pa2 :: 0hBFD7D240FBB8C3F1
+ pa3 :: 0h3FD45FCA805120E4
+ pa4 :: 0hBFBC63983D3E28EC
+ pa5 :: 0h3FA22A36599795EB
+ pa6 :: 0hBF61BF380A96073F
+ qa1 :: 0h3FBB3E6618EEE323
+ qa2 :: 0h3FE14AF092EB6F33
+ qa3 :: 0h3FB2635CD99FE9A7
+ qa4 :: 0h3FC02660E763351F
+ qa5 :: 0h3F8BEDC26B51DD1C
+ qa6 :: 0h3F888B545735151D
+ // Coefficients for approximation to erfc in [1.25, 1/0.35]
+ ra0 :: 0hBF843412600D6435
+ ra1 :: 0hBFE63416E4BA7360
+ ra2 :: 0hC0251E0441B0E726
+ ra3 :: 0hC04F300AE4CBA38D
+ ra4 :: 0hC0644CB184282266
+ ra5 :: 0hC067135CEBCCABB2
+ ra6 :: 0hC054526557E4D2F2
+ ra7 :: 0hC023A0EFC69AC25C
+ sa1 :: 0h4033A6B9BD707687
+ sa2 :: 0h4061350C526AE721
+ sa3 :: 0h407B290DD58A1A71
+ sa4 :: 0h40842B1921EC2868
+ sa5 :: 0h407AD02157700314
+ sa6 :: 0h405B28A3EE48AE2C
+ sa7 :: 0h401A47EF8E484A93
+ sa8 :: 0hBFAEEFF2EE749A62
+ // Coefficients for approximation to erfc in [1/.35, 28]
+ rb0 :: 0hBF84341239E86F4A
+ rb1 :: 0hBFE993BA70C285DE
+ rb2 :: 0hC031C209555F995A
+ rb3 :: 0hC064145D43C5ED98
+ rb4 :: 0hC083EC881375F228
+ rb5 :: 0hC09004616A2E5992
+ rb6 :: 0hC07E384E9BDC383F
+ sb1 :: 0h403E568B261D5190
+ sb2 :: 0h40745CAE221B9F0A
+ sb3 :: 0h409802EB189D5118
+ sb4 :: 0h40A8FFB7688C246A
+ sb5 :: 0h40A3F219CEDF3BE6
+ sb6 :: 0h407DA874E79FE763
+ sb7 :: 0hC03670E242712D62
+
+
+ VERY_TINY :: 0h0080000000000000
+ SMALL :: 1.0 / (1 << 28) // 2**-28
+
+ // special cases
+ switch {
+ case is_nan(x):
+ return nan_f64()
+ case is_inf(x, 1):
+ return 1
+ case is_inf(x, -1):
+ return -1
+ }
+ x := x
+ sign := false
+ if x < 0 {
+ x = -x
+ sign = true
+ }
+ if x < 0.84375 { // |x| < 0.84375
+ temp: f64
+ if x < SMALL { // |x| < 2**-28
+ if x < VERY_TINY {
+ temp = 0.125 * (8.0*x + efx8*x) // avoid underflow
+ } else {
+ temp = x + efx*x
+ }
+ } else {
+ z := x * x
+ r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
+ s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
+ y := r / s
+ temp = x + x*y
+ }
+ if sign {
+ return -temp
+ }
+ return temp
+ }
+ if x < 1.25 { // 0.84375 <= |x| < 1.25
+ s := x - 1
+ P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
+ Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
+ if sign {
+ return -erx - P/Q
+ }
+ return erx + P/Q
+ }
+ if x >= 6 { // inf > |x| >= 6
+ if sign {
+ return -1
+ }
+ return 1
+ }
+ s := 1 / (x * x)
+ R, S: f64
+ if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143
+ R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
+ S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
+ } else { // |x| >= 1 / 0.35 ~ 2.857143
+ R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
+ S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
+ }
+ z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
+ r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
+ if sign {
+ return r/x - 1
+ }
+ return 1 - r/x
+}
+
+
+erfc :: proc{
+ erfc_f16,
+ erfc_f16le,
+ erfc_f16be,
+ erfc_f32,
+ erfc_f32le,
+ erfc_f32be,
+ erfc_f64,
+}
+
+erfc_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erfc_f64(f64(x))) }
+erfc_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erfc_f64(f64(x))) }
+erfc_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erfc_f64(f64(x))) }
+erfc_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erfc_f64(f64(x))) }
+erfc_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erfc_f64(f64(x))) }
+erfc_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erfc_f64(f64(x))) }
+
+erfc_f64 :: proc "contextless" (x: f64) -> f64 {
+ erx :: 0h3FEB0AC160000000
+ // Coefficients for approximation to erf in [0, 0.84375]
+ efx :: 0h3FC06EBA8214DB69
+ efx8 :: 0h3FF06EBA8214DB69
+ pp0 :: 0h3FC06EBA8214DB68
+ pp1 :: 0hBFD4CD7D691CB913
+ pp2 :: 0hBF9D2A51DBD7194F
+ pp3 :: 0hBF77A291236668E4
+ pp4 :: 0hBEF8EAD6120016AC
+ qq1 :: 0h3FD97779CDDADC09
+ qq2 :: 0h3FB0A54C5536CEBA
+ qq3 :: 0h3F74D022C4D36B0F
+ qq4 :: 0h3F215DC9221C1A10
+ qq5 :: 0hBED09C4342A26120
+ // Coefficients for approximation to erf in [0.84375, 1.25]
+ pa0 :: 0hBF6359B8BEF77538
+ pa1 :: 0h3FDA8D00AD92B34D
+ pa2 :: 0hBFD7D240FBB8C3F1
+ pa3 :: 0h3FD45FCA805120E4
+ pa4 :: 0hBFBC63983D3E28EC
+ pa5 :: 0h3FA22A36599795EB
+ pa6 :: 0hBF61BF380A96073F
+ qa1 :: 0h3FBB3E6618EEE323
+ qa2 :: 0h3FE14AF092EB6F33
+ qa3 :: 0h3FB2635CD99FE9A7
+ qa4 :: 0h3FC02660E763351F
+ qa5 :: 0h3F8BEDC26B51DD1C
+ qa6 :: 0h3F888B545735151D
+ // Coefficients for approximation to erfc in [1.25, 1/0.35]
+ ra0 :: 0hBF843412600D6435
+ ra1 :: 0hBFE63416E4BA7360
+ ra2 :: 0hC0251E0441B0E726
+ ra3 :: 0hC04F300AE4CBA38D
+ ra4 :: 0hC0644CB184282266
+ ra5 :: 0hC067135CEBCCABB2
+ ra6 :: 0hC054526557E4D2F2
+ ra7 :: 0hC023A0EFC69AC25C
+ sa1 :: 0h4033A6B9BD707687
+ sa2 :: 0h4061350C526AE721
+ sa3 :: 0h407B290DD58A1A71
+ sa4 :: 0h40842B1921EC2868
+ sa5 :: 0h407AD02157700314
+ sa6 :: 0h405B28A3EE48AE2C
+ sa7 :: 0h401A47EF8E484A93
+ sa8 :: 0hBFAEEFF2EE749A62
+ // Coefficients for approximation to erfc in [1/.35, 28]
+ rb0 :: 0hBF84341239E86F4A
+ rb1 :: 0hBFE993BA70C285DE
+ rb2 :: 0hC031C209555F995A
+ rb3 :: 0hC064145D43C5ED98
+ rb4 :: 0hC083EC881375F228
+ rb5 :: 0hC09004616A2E5992
+ rb6 :: 0hC07E384E9BDC383F
+ sb1 :: 0h403E568B261D5190
+ sb2 :: 0h40745CAE221B9F0A
+ sb3 :: 0h409802EB189D5118
+ sb4 :: 0h40A8FFB7688C246A
+ sb5 :: 0h40A3F219CEDF3BE6
+ sb6 :: 0h407DA874E79FE763
+ sb7 :: 0hC03670E242712D62
+
+ TINY :: 1.0 / (1 << 56) // 2**-56
+ // special cases
+ switch {
+ case is_nan(x):
+ return nan_f64()
+ case is_inf(x, 1):
+ return 0
+ case is_inf(x, -1):
+ return 2
+ }
+ x := x
+ sign := false
+ if x < 0 {
+ x = -x
+ sign = true
+ }
+ if x < 0.84375 { // |x| < 0.84375
+ temp: f64
+ if x < TINY { // |x| < 2**-56
+ temp = x
+ } else {
+ z := x * x
+ r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
+ s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
+ y := r / s
+ if x < 0.25 { // |x| < 1/4
+ temp = x + x*y
+ } else {
+ temp = 0.5 + (x*y + (x - 0.5))
+ }
+ }
+ if sign {
+ return 1 + temp
+ }
+ return 1 - temp
+ }
+ if x < 1.25 { // 0.84375 <= |x| < 1.25
+ s := x - 1
+ P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
+ Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
+ if sign {
+ return 1 + erx + P/Q
+ }
+ return 1 - erx - P/Q
+
+ }
+ if x < 28 { // |x| < 28
+ s := 1 / (x * x)
+ R, S: f64
+ if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143
+ R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
+ S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
+ } else { // |x| >= 1 / 0.35 ~ 2.857143
+ if sign && x > 6 {
+ return 2 // x < -6
+ }
+ R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
+ S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
+ }
+ z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
+ r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
+ if sign {
+ return 2 - r/x
+ }
+ return r / x
+ }
+ if sign {
+ return 2
+ }
+ return 0
+}
\ No newline at end of file
diff --git a/core/math/math_gamma.odin b/core/math/math_gamma.odin
new file mode 100644
index 000000000..6b783cc25
--- /dev/null
+++ b/core/math/math_gamma.odin
@@ -0,0 +1,226 @@
+package math
+
+// The original C code, the long comment, and the constants
+// below are from http://netlib.sandia.gov/cephes/cprob/gamma.c.
+//
+// tgamma.c
+//
+// Gamma function
+//
+// SYNOPSIS:
+//
+// double x, y, tgamma();
+// extern int signgam;
+//
+// y = tgamma( x );
+//
+// DESCRIPTION:
+//
+// Returns gamma function of the argument. The result is
+// correctly signed, and the sign (+1 or -1) is also
+// returned in a global (extern) variable named signgam.
+// This variable is also filled in by the logarithmic gamma
+// function lgamma().
+//
+// Arguments |x| <= 34 are reduced by recurrence and the function
+// approximated by a rational function of degree 6/7 in the
+// interval (2,3). Large arguments are handled by Stirling's
+// formula. Large negative arguments are made positive using
+// a reflection formula.
+//
+// ACCURACY:
+//
+// Relative error:
+// arithmetic domain # trials peak rms
+// DEC -34, 34 10000 1.3e-16 2.5e-17
+// IEEE -170,-33 20000 2.3e-15 3.3e-16
+// IEEE -33, 33 20000 9.4e-16 2.2e-16
+// IEEE 33, 171.6 20000 2.3e-15 3.2e-16
+//
+// Error for arguments outside the test range will be larger
+// owing to error amplification by the exponential function.
+//
+// Cephes Math Library Release 2.8: June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+// Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+// The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+// Stephen L. Moshier
+// moshier@na-net.ornl.gov
+
+// Gamma function computed by Stirling's formula.
+// The pair of results must be multiplied together to get the actual answer.
+// The multiplication is left to the caller so that, if careful, the caller can avoid
+// infinity for 172 <= x <= 180.
+// The polynomial is valid for 33 <= x <= 172; larger values are only used
+// in reciprocal and produce denormalized floats. The lower precision there
+// masks any imprecision in the polynomial.
+@(private="file")
+stirling :: proc "contextless" (x: f64) -> (f64, f64) {
+ @(static) gamS := [?]f64{
+ +7.87311395793093628397e-04,
+ -2.29549961613378126380e-04,
+ -2.68132617805781232825e-03,
+ +3.47222221605458667310e-03,
+ +8.33333333333482257126e-02,
+ }
+
+ if x > 200 {
+ return inf_f64(1), 1
+ }
+ SQRT_TWO_PI :: 0h40040d931ff62706 // 2.506628274631000502417
+ MAX_STIRLING :: 143.01608
+ w := 1 / x
+ w = 1 + w*((((gamS[0]*w+gamS[1])*w+gamS[2])*w+gamS[3])*w+gamS[4])
+ y1 := exp(x)
+ y2 := 1.0
+ if x > MAX_STIRLING { // avoid pow() overflow
+ v := pow(x, 0.5*x-0.25)
+ y1, y2 = v, v/y1
+ } else {
+ y1 = pow(x, x-0.5) / y1
+ }
+ return y1, SQRT_TWO_PI * w * y2
+}
+
+gamma_f64 :: proc "contextless" (x: f64) -> f64 {
+ is_neg_int :: proc "contextless" (x: f64) -> bool {
+ if x < 0 {
+ _, xf := modf(x)
+ return xf == 0
+ }
+ return false
+ }
+
+ @(static) gamP := [?]f64{
+ 1.60119522476751861407e-04,
+ 1.19135147006586384913e-03,
+ 1.04213797561761569935e-02,
+ 4.76367800457137231464e-02,
+ 2.07448227648435975150e-01,
+ 4.94214826801497100753e-01,
+ 9.99999999999999996796e-01,
+ }
+ @(static) gamQ := [?]f64{
+ -2.31581873324120129819e-05,
+ +5.39605580493303397842e-04,
+ -4.45641913851797240494e-03,
+ +1.18139785222060435552e-02,
+ +3.58236398605498653373e-02,
+ -2.34591795718243348568e-01,
+ +7.14304917030273074085e-02,
+ +1.00000000000000000320e+00,
+ }
+
+
+ EULER :: 0.57721566490153286060651209008240243104215933593992 // A001620
+
+ switch {
+ case is_neg_int(x) || is_inf(x, -1) || is_nan(x):
+ return nan_f64()
+ case is_inf(x, 1):
+ return inf_f64(1)
+ case x == 0:
+ if signbit(x) {
+ return inf_f64(-1)
+ }
+ return inf_f64(1)
+ }
+
+ x := x
+ q := abs(x)
+ p := floor(q)
+ if q > 33 {
+ if x >= 0 {
+ y1, y2 := stirling(x)
+ return y1 * y2
+ }
+ // Note: x is negative but (checked above) not a negative integer,
+ // so x must be small enough to be in range for conversion to i64.
+ // If |x| were >= 2⁶³ it would have to be an integer.
+ signgam := 1
+ if ip := i64(p); ip&1 == 0 {
+ signgam = -1
+ }
+ z := q - p
+ if z > 0.5 {
+ p = p + 1
+ z = q - p
+ }
+ z = q * sin(PI*z)
+ if z == 0 {
+ return inf_f64(signgam)
+ }
+ sq1, sq2 := stirling(q)
+ absz := abs(z)
+ d := absz * sq1 * sq2
+ if is_inf(d, 0) {
+ z = PI / absz / sq1 / sq2
+ } else {
+ z = PI / d
+ }
+ return f64(signgam) * z
+ }
+
+ // Reduce argument
+ z := 1.0
+ for x >= 3 {
+ x = x - 1
+ z = z * x
+ }
+ for x < 0 {
+ if x > -1e-09 {
+ if x == 0 {
+ return inf_f64(1)
+ }
+ return z / ((1 + EULER*x) * x)
+ }
+ z = z / x
+ x = x + 1
+ }
+ for x < 2 {
+ if x < 1e-09 {
+ if x == 0 {
+ return inf_f64(1)
+ }
+ return z / ((1 + EULER*x) * x)
+ }
+ z = z / x
+ x = x + 1
+ }
+
+ if x == 2 {
+ return z
+ }
+
+ x = x - 2
+ p = (((((x*gamP[0]+gamP[1])*x+gamP[2])*x+gamP[3])*x+gamP[4])*x+gamP[5])*x + gamP[6]
+ q = ((((((x*gamQ[0]+gamQ[1])*x+gamQ[2])*x+gamQ[3])*x+gamQ[4])*x+gamQ[5])*x+gamQ[6])*x + gamQ[7]
+ return z * p / q
+}
+
+
+gamma_f16 :: proc "contextless" (x: f16) -> f16 { return f16(gamma_f64(f64(x))) }
+gamma_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(gamma_f64(f64(x))) }
+gamma_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(gamma_f64(f64(x))) }
+gamma_f32 :: proc "contextless" (x: f32) -> f32 { return f32(gamma_f64(f64(x))) }
+gamma_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(gamma_f64(f64(x))) }
+gamma_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(gamma_f64(f64(x))) }
+gamma_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(gamma_f64(f64(x))) }
+gamma_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(gamma_f64(f64(x))) }
+
+gamma :: proc{
+ gamma_f16, gamma_f16le, gamma_f16be,
+ gamma_f32, gamma_f32le, gamma_f32be,
+ gamma_f64, gamma_f64le, gamma_f64be,
+}
\ No newline at end of file
diff --git a/core/math/math_lgamma.odin b/core/math/math_lgamma.odin
new file mode 100644
index 000000000..98b2731c9
--- /dev/null
+++ b/core/math/math_lgamma.odin
@@ -0,0 +1,361 @@
+package math
+
+// The original C code and the long comment below are
+// from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and
+// came with this notice.
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+// __ieee754_lgamma_r(x, signgamp)
+// Reentrant version of the logarithm of the Gamma function
+// with user provided pointer for the sign of Gamma(x).
+//
+// Method:
+// 1. Argument Reduction for 0 < x <= 8
+// Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
+// reduce x to a number in [1.5,2.5] by
+// lgamma(1+s) = log(s) + lgamma(s)
+// for example,
+// lgamma(7.3) = log(6.3) + lgamma(6.3)
+// = log(6.3*5.3) + lgamma(5.3)
+// = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
+// 2. Polynomial approximation of lgamma around its
+// minimum (ymin=1.461632144968362245) to maintain monotonicity.
+// On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
+// Let z = x-ymin;
+// lgamma(x) = -1.214862905358496078218 + z**2*poly(z)
+// poly(z) is a 14 degree polynomial.
+// 2. Rational approximation in the primary interval [2,3]
+// We use the following approximation:
+// s = x-2.0;
+// lgamma(x) = 0.5*s + s*P(s)/Q(s)
+// with accuracy
+// |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
+// Our algorithms are based on the following observation
+//
+// zeta(2)-1 2 zeta(3)-1 3
+// lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
+// 2 3
+//
+// where Euler = 0.5772156649... is the Euler constant, which
+// is very close to 0.5.
+//
+// 3. For x>=8, we have
+// lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
+// (better formula:
+// lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
+// Let z = 1/x, then we approximation
+// f(z) = lgamma(x) - (x-0.5)(log(x)-1)
+// by
+// 3 5 11
+// w = w0 + w1*z + w2*z + w3*z + ... + w6*z
+// where
+// |w - f(z)| < 2**-58.74
+//
+// 4. For negative x, since (G is gamma function)
+// -x*G(-x)*G(x) = pi/sin(pi*x),
+// we have
+// G(x) = pi/(sin(pi*x)*(-x)*G(-x))
+// since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
+// Hence, for x<0, signgam = sign(sin(pi*x)) and
+// lgamma(x) = log(|Gamma(x)|)
+// = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
+// Note: one should avoid computing pi*(-x) directly in the
+// computation of sin(pi*(-x)).
+//
+// 5. Special Cases
+// lgamma(2+s) ~ s*(1-Euler) for tiny s
+// lgamma(1)=lgamma(2)=0
+// lgamma(x) ~ -log(x) for tiny x
+// lgamma(0) = lgamma(inf) = inf
+// lgamma(-integer) = +-inf
+//
+//
+
+
+lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
+ sin_pi :: proc "contextless" (x: f64) -> f64 {
+ if x < 0.25 {
+ return -sin(PI * x)
+ }
+ x := x
+
+ // argument reduction
+ z := floor(x)
+ n: int
+ if z != x { // inexact
+ x = mod(x, 2)
+ n = int(x * 4)
+ } else {
+ if x >= TWO_53 { // x must be even
+ x = 0
+ n = 0
+ } else {
+ if x < TWO_52 {
+ z = x + TWO_52 // exact
+ }
+ n = int(1 & transmute(u64)z)
+ x = f64(n)
+ n <<= 2
+ }
+ }
+ switch n {
+ case 0:
+ x = sin(PI * x)
+ case 1, 2:
+ x = cos(PI * (0.5 - x))
+ case 3, 4:
+ x = sin(PI * (1 - x))
+ case 5, 6:
+ x = -cos(PI * (x - 1.5))
+ case:
+ x = sin(PI * (x - 2))
+ }
+ return -x
+ }
+
+ @static lgamA := [?]f64{
+ 0h3FB3C467E37DB0C8,
+ 0h3FD4A34CC4A60FAD,
+ 0h3FB13E001A5562A7,
+ 0h3F951322AC92547B,
+ 0h3F7E404FB68FEFE8,
+ 0h3F67ADD8CCB7926B,
+ 0h3F538A94116F3F5D,
+ 0h3F40B6C689B99C00,
+ 0h3F2CF2ECED10E54D,
+ 0h3F1C5088987DFB07,
+ 0h3EFA7074428CFA52,
+ 0h3F07858E90A45837,
+ }
+ @static lgamR := [?]f64{
+ 1.0,
+ 0h3FF645A762C4AB74,
+ 0h3FE71A1893D3DCDC,
+ 0h3FC601EDCCFBDF27,
+ 0h3F9317EA742ED475,
+ 0h3F497DDACA41A95B,
+ 0h3EDEBAF7A5B38140,
+ }
+ @static lgamS := [?]f64{
+ 0hBFB3C467E37DB0C8,
+ 0h3FCB848B36E20878,
+ 0h3FD4D98F4F139F59,
+ 0h3FC2BB9CBEE5F2F7,
+ 0h3F9B481C7E939961,
+ 0h3F5E26B67368F239,
+ 0h3F00BFECDD17E945,
+ }
+ @static lgamT := [?]f64{
+ 0h3FDEF72BC8EE38A2,
+ 0hBFC2E4278DC6C509,
+ 0h3FB08B4294D5419B,
+ 0hBFA0C9A8DF35B713,
+ 0h3F9266E7970AF9EC,
+ 0hBF851F9FBA91EC6A,
+ 0h3F78FCE0E370E344,
+ 0hBF6E2EFFB3E914D7,
+ 0h3F6282D32E15C915,
+ 0hBF56FE8EBF2D1AF1,
+ 0h3F4CDF0CEF61A8E9,
+ 0hBF41A6109C73E0EC,
+ 0h3F34AF6D6C0EBBF7,
+ 0hBF347F24ECC38C38,
+ 0h3F35FD3EE8C2D3F4,
+ }
+ @static lgamU := [?]f64{
+ 0hBFB3C467E37DB0C8,
+ 0h3FE4401E8B005DFF,
+ 0h3FF7475CD119BD6F,
+ 0h3FEF497644EA8450,
+ 0h3FCD4EAEF6010924,
+ 0h3F8B678BBF2BAB09,
+ }
+ @static lgamV := [?]f64{
+ 1.0,
+ 0h4003A5D7C2BD619C,
+ 0h40010725A42B18F5,
+ 0h3FE89DFBE45050AF,
+ 0h3FBAAE55D6537C88,
+ 0h3F6A5ABB57D0CF61,
+ }
+ @static lgamW := [?]f64{
+ 0h3FDACFE390C97D69,
+ 0h3FB555555555553B,
+ 0hBF66C16C16B02E5C,
+ 0h3F4A019F98CF38B6,
+ 0hBF4380CB8C0FE741,
+ 0h3F4B67BA4CDAD5D1,
+ 0hBF5AB89D0B9E43E4,
+ }
+
+
+ Y_MIN :: 0h3ff762d86356be3f // 1.461632144968362245
+ TWO_52 :: 0h4330000000000000 // ~4.5036e+15
+ TWO_53 :: 0h4340000000000000 // ~9.0072e+15
+ TWO_58 :: 0h4390000000000000 // ~2.8823e+17
+ TINY :: 0h3b90000000000000 // ~8.47033e-22
+ Tc :: 0h3FF762D86356BE3F
+ Tf :: 0hBFBF19B9BCC38A42
+ Tt :: 0hBC50C7CAA48A971F
+
+ // special cases
+ sign = 1
+ switch {
+ case is_nan(x):
+ lgamma = x
+ return
+ case is_inf(x):
+ lgamma = x
+ return
+ case x == 0:
+ lgamma = inf_f64(1)
+ return
+ }
+
+ x := x
+ neg := false
+ if x < 0 {
+ x = -x
+ neg = true
+ }
+
+ if x < TINY { // if |x| < 2**-70, return -log(|x|)
+ if neg {
+ sign = -1
+ }
+ lgamma = -ln(x)
+ return
+ }
+ nadj: f64
+ if neg {
+ if x >= TWO_52 { // |x| >= 2**52, must be -integer
+ lgamma = inf_f64(1)
+ return
+ }
+ t := sin_pi(x)
+ if t == 0 {
+ lgamma = inf_f64(1) // -integer
+ return
+ }
+ nadj = ln(PI / abs(t*x))
+ if t < 0 {
+ sign = -1
+ }
+ }
+
+ switch {
+ case x == 1 || x == 2: // purge off 1 and 2
+ lgamma = 0
+ return
+ case x < 2: // use lgamma(x) = lgamma(x+1) - log(x)
+ y: f64
+ i: int
+ if x <= 0.9 {
+ lgamma = -ln(x)
+ switch {
+ case x >= (Y_MIN - 1 + 0.27): // 0.7316 <= x <= 0.9
+ y = 1 - x
+ i = 0
+ case x >= (Y_MIN - 1 - 0.27): // 0.2316 <= x < 0.7316
+ y = x - (Tc - 1)
+ i = 1
+ case: // 0 < x < 0.2316
+ y = x
+ i = 2
+ }
+ } else {
+ lgamma = 0
+ switch {
+ case x >= (Y_MIN + 0.27): // 1.7316 <= x < 2
+ y = 2 - x
+ i = 0
+ case x >= (Y_MIN - 0.27): // 1.2316 <= x < 1.7316
+ y = x - Tc
+ i = 1
+ case: // 0.9 < x < 1.2316
+ y = x - 1
+ i = 2
+ }
+ }
+ switch i {
+ case 0:
+ z := y * y
+ p1 := lgamA[0] + z*(lgamA[2]+z*(lgamA[4]+z*(lgamA[6]+z*(lgamA[8]+z*lgamA[10]))))
+ p2 := z * (lgamA[1] + z*(+lgamA[3]+z*(lgamA[5]+z*(lgamA[7]+z*(lgamA[9]+z*lgamA[11])))))
+ p := y*p1 + p2
+ lgamma += (p - 0.5*y)
+ case 1:
+ z := y * y
+ w := z * y
+ p1 := lgamT[0] + w*(lgamT[3]+w*(lgamT[6]+w*(lgamT[9]+w*lgamT[12]))) // parallel comp
+ p2 := lgamT[1] + w*(lgamT[4]+w*(lgamT[7]+w*(lgamT[10]+w*lgamT[13])))
+ p3 := lgamT[2] + w*(lgamT[5]+w*(lgamT[8]+w*(lgamT[11]+w*lgamT[14])))
+ p := z*p1 - (Tt - w*(p2+y*p3))
+ lgamma += (Tf + p)
+ case 2:
+ p1 := y * (lgamU[0] + y*(lgamU[1]+y*(lgamU[2]+y*(lgamU[3]+y*(lgamU[4]+y*lgamU[5])))))
+ p2 := 1 + y*(lgamV[1]+y*(lgamV[2]+y*(lgamV[3]+y*(lgamV[4]+y*lgamV[5]))))
+ lgamma += (-0.5*y + p1/p2)
+ }
+ case x < 8: // 2 <= x < 8
+ i := int(x)
+ y := x - f64(i)
+ p := y * (lgamS[0] + y*(lgamS[1]+y*(lgamS[2]+y*(lgamS[3]+y*(lgamS[4]+y*(lgamS[5]+y*lgamS[6]))))))
+ q := 1 + y*(lgamR[1]+y*(lgamR[2]+y*(lgamR[3]+y*(lgamR[4]+y*(lgamR[5]+y*lgamR[6])))))
+ lgamma = 0.5*y + p/q
+ z := 1.0 // lgamma(1+s) = ln(s) + lgamma(s)
+ switch i {
+ case 7:
+ z *= (y + 6)
+ fallthrough
+ case 6:
+ z *= (y + 5)
+ fallthrough
+ case 5:
+ z *= (y + 4)
+ fallthrough
+ case 4:
+ z *= (y + 3)
+ fallthrough
+ case 3:
+ z *= (y + 2)
+ lgamma += ln(z)
+ }
+ case x < TWO_58: // 8 <= x < 2**58
+ t := ln(x)
+ z := 1 / x
+ y := z * z
+ w := lgamW[0] + z*(lgamW[1]+y*(lgamW[2]+y*(lgamW[3]+y*(lgamW[4]+y*(lgamW[5]+y*lgamW[6])))))
+ lgamma = (x-0.5)*(t-1) + w
+ case: // 2**58 <= x <= Inf
+ lgamma = x * (ln(x) - 1)
+ }
+ if neg {
+ lgamma = nadj - lgamma
+ }
+ return
+}
+
+
+lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
+lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
+lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
+lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
+lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
+lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
+lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
+lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
+
+lgamma :: proc{
+ lgamma_f16, lgamma_f16le, lgamma_f16be,
+ lgamma_f32, lgamma_f32le, lgamma_f32be,
+ lgamma_f64, lgamma_f64le, lgamma_f64be,
+}
\ No newline at end of file
diff --git a/core/math/math_log1p.odin b/core/math/math_log1p.odin
new file mode 100644
index 000000000..a4a1aa2ae
--- /dev/null
+++ b/core/math/math_log1p.odin
@@ -0,0 +1,198 @@
+package math
+
+// The original C code, the long comment, and the constants
+// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c
+// and came with this notice. The go code is a simplified
+// version of the original C.
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+//
+// double log1p(double x)
+//
+// Method :
+// 1. Argument Reduction: find k and f such that
+// 1+x = 2**k * (1+f),
+// where sqrt(2)/2 < 1+f < sqrt(2) .
+//
+// Note. If k=0, then f=x is exact. However, if k!=0, then f
+// may not be representable exactly. In that case, a correction
+// term is need. Let u=1+x rounded. Let c = (1+x)-u, then
+// log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
+// and add back the correction term c/u.
+// (Note: when x > 2**53, one can simply return log(x))
+//
+// 2. Approximation of log1p(f).
+// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+// = 2s + s*R
+// We use a special Reme algorithm on [0,0.1716] to generate
+// a polynomial of degree 14 to approximate R The maximum error
+// of this polynomial approximation is bounded by 2**-58.45. In
+// other words,
+// 2 4 6 8 10 12 14
+// R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s
+// (the values of Lp1 to Lp7 are listed in the program)
+// and
+// | 2 14 | -58.45
+// | Lp1*s +...+Lp7*s - R(z) | <= 2
+// | |
+// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+// In order to guarantee error in log below 1ulp, we compute log
+// by
+// log1p(f) = f - (hfsq - s*(hfsq+R)).
+//
+// 3. Finally, log1p(x) = k*ln2 + log1p(f).
+// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
+// Here ln2 is split into two floating point number:
+// ln2_hi + ln2_lo,
+// where n*ln2_hi is always exact for |n| < 2000.
+//
+// Special cases:
+// log1p(x) is NaN with signal if x < -1 (including -INF) ;
+// log1p(+INF) is +INF; log1p(-1) is -INF with signal;
+// log1p(NaN) is that NaN with no signal.
+//
+// Accuracy:
+// according to an error analysis, the error is always less than
+// 1 ulp (unit in the last place).
+//
+// Constants:
+// The hexadecimal values are the intended ones for the following
+// constants. The decimal values may be used, provided that the
+// compiler will convert from decimal to binary accurately enough
+// to produce the hexadecimal values shown.
+//
+// Note: Assuming log() return accurate answer, the following
+// algorithm can be used to compute log1p(x) to within a few ULP:
+//
+// u = 1+x;
+// if(u==1.0) return x ; else
+// return log(u)*(x/(u-1.0));
+//
+// See HP-15C Advanced Functions Handbook, p.193.
+
+log1p :: proc {
+ log1p_f16,
+ log1p_f32,
+ log1p_f64,
+ log1p_f16le,
+ log1p_f16be,
+ log1p_f32le,
+ log1p_f32be,
+ log1p_f64le,
+ log1p_f64be,
+}
+log1p_f16 :: proc "contextless" (x: f16) -> f16 { return f16(log1p_f64(f64(x))) }
+log1p_f32 :: proc "contextless" (x: f32) -> f32 { return f32(log1p_f64(f64(x))) }
+log1p_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log1p_f64(f64(x))) }
+log1p_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log1p_f64(f64(x))) }
+log1p_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log1p_f64(f64(x))) }
+log1p_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log1p_f64(f64(x))) }
+log1p_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log1p_f64(f64(x))) }
+log1p_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log1p_f64(f64(x))) }
+
+log1p_f64 :: proc "contextless" (x: f64) -> f64 {
+ SQRT2_M1 :: 0h3fda827999fcef34 // sqrt(2)-1
+ SQRT2_HALF_M1 :: 0hbfd2bec333018866 // sqrt(2)/2-1
+ SMALL :: 0h3e20000000000000 // 2**-29
+ TINY :: 0h3c90000000000000 // 2**-54
+ TWO53 :: 0h4340000000000000 // 2**53
+ LN2HI :: 0h3fe62e42fee00000
+ LN2LO :: 0h3dea39ef35793c76
+ LP1 :: 0h3FE5555555555593
+ LP2 :: 0h3FD999999997FA04
+ LP3 :: 0h3FD2492494229359
+ LP4 :: 0h3FCC71C51D8E78AF
+ LP5 :: 0h3FC7466496CB03DE
+ LP6 :: 0h3FC39A09D078C69F
+ LP7 :: 0h3FC2F112DF3E5244
+
+ switch {
+ case x < -1 || is_nan(x):
+ return nan_f64()
+ case x == -1:
+ return inf_f64(-1)
+ case is_inf(x, 1):
+ return inf_f64(+1)
+ }
+ absx := abs(x)
+
+ f: f64
+ iu: u64
+ k := 1
+ if absx < SQRT2_M1 { // |x| < sqrt(2)-1
+ if absx < SMALL { // |x| < 2**-29
+ if absx < TINY { // |x| < 2**-54
+ return x
+ }
+ return x - x*x*0.5
+ }
+ if x > SQRT2_HALF_M1 { // sqrt(2)/2-1 < x
+ // (sqrt(2)/2-1) < x < (sqrt(2)-1)
+ k = 0
+ f = x
+ iu = 1
+ }
+ }
+ c: f64
+ if k != 0 {
+ u: f64
+ if absx < TWO53 { // 1<<53
+ u = 1.0 + x
+ iu = transmute(u64)u
+ k = int((iu >> 52) - 1023)
+ // correction term
+ if k > 0 {
+ c = 1.0 - (u - x)
+ } else {
+ c = x - (u - 1.0)
+ }
+ c /= u
+ } else {
+ u = x
+ iu = transmute(u64)u
+ k = int((iu >> 52) - 1023)
+ c = 0
+ }
+ iu &= 0x000fffffffffffff
+ if iu < 0x0006a09e667f3bcd { // mantissa of sqrt(2)
+ u = transmute(f64)(iu | 0x3ff0000000000000) // normalize u
+ } else {
+ k += 1
+ u = transmute(f64)(iu | 0x3fe0000000000000) // normalize u/2
+ iu = (0x0010000000000000 - iu) >> 2
+ }
+ f = u - 1.0 // sqrt(2)/2 < u < sqrt(2)
+ }
+ hfsq := 0.5 * f * f
+ s, R, z: f64
+ if iu == 0 { // |f| < 2**-20
+ if f == 0 {
+ if k == 0 {
+ return 0
+ }
+ c += f64(k) * LN2LO
+ return f64(k)*LN2HI + c
+ }
+ R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
+ if k == 0 {
+ return f - R
+ }
+ return f64(k)*LN2HI - ((R - (f64(k)*LN2LO + c)) - f)
+ }
+ s = f / (2.0 + f)
+ z = s * s
+ R = z * (LP1 + z*(LP2+z*(LP3+z*(LP4+z*(LP5+z*(LP6+z*LP7))))))
+ if k == 0 {
+ return f - (hfsq - s*(hfsq+R))
+ }
+ return f64(k)*LN2HI - ((hfsq - (s*(hfsq+R) + (f64(k)*LN2LO + c))) - f)
+}
diff --git a/core/math/noise/internal.odin b/core/math/noise/internal.odin
new file mode 100644
index 000000000..5837f9235
--- /dev/null
+++ b/core/math/noise/internal.odin
@@ -0,0 +1,734 @@
+/*
+ OpenSimplex2 noise implementation.
+
+ Ported from https://github.com/KdotJPG/OpenSimplex2.
+ Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+//+private
+package math_noise
+
+/*
+ Private implementation details follow.
+*/
+
+PRIME_X :: i64(0x5205402B9270C86F)
+PRIME_Y :: i64(0x598CD327003817B5)
+PRIME_Z :: i64(0x5BCC226E9FA0BACB)
+PRIME_W :: i64(0x56CC5227E58F554B)
+
+HASH_MULTIPLIER :: i64(0x53A3F72DEEC546F5)
+SEED_FLIP_3D :: i64(-0x52D547B2E96ED629)
+SEED_OFFSET_4D :: i64(0xE83DC3E0DA7164D)
+
+ROOT_2_OVER_2 :: f64(0.7071067811865476)
+SKEW_2D :: f64(0.366025403784439)
+UNSKEW_2D :: f64(-0.21132486540518713)
+ROOT_3_OVER_3 :: f64(0.577350269189626)
+
+FALLBACK_ROTATE_3D :: f64(2.0) / f64(3.0)
+ROTATE_3D_ORTHOGONALIZER :: f64(UNSKEW_2D)
+
+SKEW_4D :: f32(0hbe0d8369)
+UNSKEW_4D :: f32(0.309016994374947)
+LATTICE_STEP_4D :: f32(0.2)
+
+N_GRADS_2D_EXPONENT :: 7
+N_GRADS_3D_EXPONENT :: 8
+N_GRADS_4D_EXPONENT :: 9
+N_GRADS_2D :: 1 << N_GRADS_2D_EXPONENT
+N_GRADS_3D :: 1 << N_GRADS_3D_EXPONENT
+N_GRADS_4D :: 1 << N_GRADS_4D_EXPONENT
+
+NORMALIZER_2D :: f64(0.01001634121365712)
+NORMALIZER_3D :: f64(0.07969837668935331)
+NORMALIZER_4D :: f64(0.0220065933241897)
+RSQUARED_2D :: f32(0.5)
+RSQUARED_3D :: f32(0.6)
+RSQUARED_4D :: f32(0.6)
+
+GRADIENTS_2D := [N_GRADS_2D * 2]f32{
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+}
+
+GRADIENTS_3D := [N_GRADS_3D * 4]f32{
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+}
+
+GRADIENTS_4D := [N_GRADS_4D * 4]f32{
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+}
+
+/*
+ 2D Simplex noise base.
+*/
+_internal_noise_2d_unskewed_base :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+ // Get base points and offsets.
+ base := [2]i64{fast_floor(coord.x), fast_floor(coord.y)}
+ i := [2]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y))}
+
+ // Prime pre-multiplication for hash.
+ bp := base * [2]i64{PRIME_X, PRIME_Y}
+
+ // Unskew.
+ t := f32(i.x + i.y) * f32(UNSKEW_2D)
+ d0 := i + [2]f32{t, t}
+
+ // First vertex.
+ a0 := RSQUARED_2D - d0.x * d0.x - d0.y * d0.y
+ if a0 > 0 {
+ value = (a0 * a0) * (a0 * a0) * grad(seed, [2]i64{bp.x, bp.y}, d0)
+ }
+
+ // Second vertex.
+ a1 := f32(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + f32(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0
+ if a1 > 0 {
+ d1 := d0 - [2]f32{f32(1 + 2 * UNSKEW_2D), f32(1 + 2 * UNSKEW_2D)}
+ value += (a1 * a1) * (a1 * a1) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y + PRIME_Y}, d1)
+ }
+
+ // Third vertex.
+ if d0.y > d0.x {
+ d2 := d0 - [2]f32{f32(UNSKEW_2D), f32(UNSKEW_2D + 1)}
+ a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+ if(a2 > 0) {
+ value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x, bp.y + PRIME_Y}, d2)
+ }
+ } else {
+ d2 := d0 - [2]f32{f32(UNSKEW_2D + 1), f32(UNSKEW_2D)}
+ a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+ if(a2 > 0) {
+ value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y}, d2)
+ }
+ }
+
+ return
+}
+
+
+/*
+ Generate overlapping cubic lattices for 3D OpenSimplex2 noise.
+*/
+_internal_noise_3d_unrotated_base :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ seed := seed
+ // Get base points and offsets.
+ // xr, yr, zr := coord.x, coord.y, coord.z
+
+ rb := [3]i64{fast_round(coord.x), fast_round(coord.y), fast_round(coord.z)}
+ ri := [3]f32{f32(coord.x - f64(rb.x)), f32(coord.y - f64(rb.y)), f32(coord.z - f64(rb.z))}
+
+ // -1 if positive, 1 if negative.
+ i_sign := [3]i64{i64(-1.0 - ri.x) | 1, i64(-1.0 - ri.y) | 1, i64(-1.0 - ri.z) | 1}
+ f_sign := [3]f32{f32(i_sign.x), f32(i_sign.y), f32(i_sign.z)}
+
+ // Compute absolute values, using the above as a shortcut. This was faster in my tests for some reason.
+ a0 := f_sign * -ri
+
+ // Prime pre-multiplication for hash.
+ rbp := rb * [3]i64{PRIME_X, PRIME_Y, PRIME_Z}
+
+ // Loop: Pick an edge on each lattice copy.
+ a := (RSQUARED_3D - ri.x * ri.x) - (ri.y * ri.y + ri.z * ri.z)
+
+ l := 0
+ for {
+ defer l += 1
+
+ // Closest point on cube.
+ if a > 0 {
+ a2 := a * a; a4 := a2 * a2
+ value += a4 * grad(seed, rbp, ri)
+ }
+
+ // Second-closest point.
+ if a0.x >= a0.y && a0.x >= a0.z {
+ b := a + a0.x + a0.x
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x - i_sign.x * PRIME_X, rbp.y, rbp.z}, [3]f32{ri.x + f_sign.x, ri.y, ri.z})
+ }
+ } else if a0.y > a0.x && a0.y >= a0.z {
+ b := a + a0.y + a0.y
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x, rbp.y - i_sign.y * PRIME_Y, rbp.z}, [3]f32{ri.x, ri.y + f_sign.y, ri.z})
+ }
+ } else {
+ b := a + a0.z + a0.z
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x, rbp.y, rbp.z - i_sign.z * PRIME_Z}, [3]f32{ri.x, ri.y, ri.z + f_sign.z})
+ }
+ }
+
+ // Break from loop if we're done, skipping updates below.
+ if l == 1 {
+ break
+ }
+
+ // Update absolute value.
+ a0 = 0.5 - a0
+
+ // Update relative coordinate.
+ ri = a0 * f_sign
+
+ // Update falloff.
+ a += (0.75 - a0.x) - (a0.y + a0.z)
+
+ // Update prime for hash.
+ rbp += [3]i64{i_sign.x >> 1, i_sign.y >> 1, i_sign.z >> 1} & {PRIME_X, PRIME_Y, PRIME_Z}
+
+ // Update the reverse sign indicators.
+ i_sign = -i_sign
+ f_sign = -f_sign
+
+ // And finally update the seed for the other lattice copy.
+ seed ~= SEED_FLIP_3D
+ }
+
+ return value
+}
+
+/*
+ 4D OpenSimplex2 noise base.
+*/
+_internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ seed := seed
+
+ // Get base points and offsets
+ base := [4]i64{fast_floor(coord.x), fast_floor(coord.y), fast_floor(coord.z), fast_floor(coord.w)}
+ si := [4]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y)), f32(coord.z - f64(base.z)), f32(coord.w - f64(base.w))}
+
+ // Determine which lattice we can be confident has a contributing point its corresponding cell's base simplex.
+ // We only look at the spaces between the diagonal planes. This proved effective in all of my tests.
+ si_sum := (si.x + si.y) + (si.z + si.w)
+ starting_lattice := i64(si_sum * 1.25)
+
+ // Offset for seed based on first lattice copy.
+ seed += starting_lattice * SEED_OFFSET_4D
+
+ // Offset for lattice point relative positions (skewed)
+ starting_lattice_offset := f32(starting_lattice) * -LATTICE_STEP_4D
+ si += starting_lattice_offset
+
+ // Prep for vertex contributions.
+ ssi := (si_sum + starting_lattice_offset * 4) * UNSKEW_4D
+
+ // Prime pre-multiplication for hash.
+ svp := base * [4]i64{PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+
+ // Five points to add, total, from five copies of the A4 lattice.
+ for i : i64 = 0; ; i += 1 {
+
+ // Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
+ score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
+ if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
+ svp.x += PRIME_X
+ si.x -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
+ svp.y += PRIME_Y
+ si.y -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
+ svp.z += PRIME_Z
+ si.z -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
+ svp.w += PRIME_W
+ si.w -= 1
+ ssi -= UNSKEW_4D
+ }
+
+ // gradient contribution with falloff.
+ d := si + ssi
+ a := (d.x * d.x + d.y * d.y) + (d.z * d.z + d.w * d.w)
+
+ if a < RSQUARED_4D {
+ a -= RSQUARED_4D
+ a *= a; a4 := a * a
+ value += a4 * grad(seed, svp, d)
+ }
+
+ // Break from loop if we're done, skipping updates below.
+ if i == 4 {
+ break
+ }
+
+ // Update for next lattice copy shifted down by <-0.2, -0.2, -0.2, -0.2>.
+ si += LATTICE_STEP_4D
+ ssi += LATTICE_STEP_4D * 4 * UNSKEW_4D
+ seed -= SEED_OFFSET_4D
+
+ // Because we don't always start on the same lattice copy, there's a special reset case.
+ if i == starting_lattice {
+ svp -= {PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+ seed += SEED_OFFSET_4D * 5
+ }
+ }
+ return
+}
+
+/*
+ Utility functions
+*/
+@(optimization_mode="speed")
+grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
+ hash := seed ~ svp.x ~ svp.y
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_2D_EXPONENT + 1)
+
+ gi := hash & ((N_GRADS_2D - 1) << 1)
+ return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
+}
+
+@(optimization_mode="speed")
+grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
+ hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_3D_EXPONENT + 2)
+
+ gi := hash & ((N_GRADS_3D - 1) << 2)
+ return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
+}
+
+@(optimization_mode="speed")
+grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
+ hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_4D_EXPONENT + 2)
+
+ gi := hash & ((N_GRADS_4D - 1) << 2)
+ return (GRADIENTS_4D[gi] * delta.x + GRADIENTS_4D[gi | 1] * delta.y) + (GRADIENTS_4D[gi | 2] * delta.z + GRADIENTS_4D[gi | 3] * delta.w)
+}
+
+grad :: proc {grad_2d, grad_3d, grad_4d}
+
+@(optimization_mode="speed")
+fast_floor :: proc(x: f64) -> (floored: i64) {
+ xi := i64(x)
+ return x < f64(xi) ? xi - 1 : xi
+}
+
+@(optimization_mode="speed")
+fast_round :: proc(x: f64) -> (rounded: i64) {
+ return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
+}
\ No newline at end of file
diff --git a/core/math/noise/opensimplex2.odin b/core/math/noise/opensimplex2.odin
new file mode 100644
index 000000000..d90dafdf5
--- /dev/null
+++ b/core/math/noise/opensimplex2.odin
@@ -0,0 +1,171 @@
+/*
+ OpenSimplex2 noise implementation.
+
+ Ported from https://github.com/KdotJPG/OpenSimplex2.
+ Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+package math_noise
+
+/*
+ Input coordinate vectors
+*/
+Vec2 :: [2]f64
+Vec3 :: [3]f64
+Vec4 :: [4]f64
+
+/*
+ Noise Evaluators
+*/
+
+/*
+ 2D Simplex noise, standard lattice orientation.
+*/
+noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+ // Get points for A2* lattice
+ skew := SKEW_2D * (coord.x + coord.y)
+ skewed := coord + skew
+
+ return _internal_noise_2d_unskewed_base(seed, skewed)
+}
+
+/*
+ 2D Simplex noise, with Y pointing down the main diagonal.
+ Might be better for a 2D sandbox style game, where Y is vertical.
+ Probably slightly less optimal for heightmaps or continent maps,
+ unless your map is centered around an equator. It's a subtle
+ difference, but the option is here to make it an easy choice.
+*/
+noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+ // Skew transform and rotation baked into one.
+ xx := coord.x * ROOT_2_OVER_2
+ yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D))
+ return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx})
+}
+
+
+/*
+ 3D OpenSimplex2 noise, with better visual isotropy in (X, Y).
+ Recommended for 3D terrain and time-varied animations.
+ The Z coordinate should always be the "different" coordinate in whatever your use case is.
+ If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`.
+ If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`.
+ For a time varied animation, call `noise_3d_improve_xz(x, y, T)`.
+*/
+noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ /*
+ Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
+ and the planes formed by XY are moved far out of alignment with the cube faces.
+ Orthonormal rotation. Not a skew transform.
+ */
+ xy := coord.x + coord.y
+ s2 := xy * ROTATE_3D_ORTHOGONALIZER
+ zz := coord.z * ROOT_3_OVER_3
+
+ r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz}
+
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+ 3D OpenSimplex2 noise, with better visual isotropy in (X, Z).
+ Recommended for 3D terrain and time-varied animations.
+ The Y coordinate should always be the "different" coordinate in whatever your use case is.
+ If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`.
+ If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`.
+ For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`.
+*/
+noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ /*
+ Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
+ and the planes formed by XZ are moved far out of alignment with the cube faces.
+ Orthonormal rotation. Not a skew transform.
+ */
+ xz := coord.x + coord.z
+ s2 := xz * ROTATE_3D_ORTHOGONALIZER
+ yy := coord.y * ROOT_3_OVER_3
+
+ r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy}
+
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+ 3D OpenSimplex2 noise, fallback rotation option
+ Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate.
+ They have less diagonal bias. This function's best use is as a fallback.
+*/
+noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ /*
+ Re-orient the cubic lattices via rotation, to produce a familiar look.
+ Orthonormal rotation. Not a skew transform.
+ */
+ bias := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z)
+ biased := bias - coord
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, biased)
+}
+
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ in a space where Z is vertical.
+*/
+noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xy := coord.x + coord.y
+ s2 := xy * -0.21132486540518699998
+ zz := coord.z * 0.28867513459481294226
+ ww := coord.w * 0.2236067977499788
+
+ xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2)
+ zr : f64 = xy * -0.57735026918962599998 + (zz + ww)
+ wr : f64 = coord.z * -0.866025403784439 + ww
+
+ return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ in a space where Y is vertical.
+*/
+noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xz := coord.x + coord.z
+ s2 := xz * -0.21132486540518699998
+ yy := coord.y * 0.28867513459481294226
+ ww := coord.w * 0.2236067977499788
+
+ xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2)
+ yr := xz * -0.57735026918962599998 + (yy + ww)
+ wr := coord.y * -0.866025403784439 + ww
+
+ return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ where there isn't a clear distinction between horizontal and vertical
+*/
+noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xyz := coord.x + coord.y + coord.z
+ ww := coord.w * 0.2236067977499788
+ s2 := xyz * -0.16666666666666666 + ww
+
+ skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww}
+ return _internal_noise_4d_unskewed_base(seed, skewed)
+}
+
+/*
+ 4D OpenSimplex2 noise, fallback lattice orientation.
+*/
+noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ // Get points for A4 lattice
+ skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w)
+ return _internal_noise_4d_unskewed_base(seed, coord + skew)
+}
\ No newline at end of file
diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin
new file mode 100644
index 000000000..ada89afad
--- /dev/null
+++ b/core/math/rand/distributions.odin
@@ -0,0 +1,312 @@
+package rand
+
+import "core:math"
+
+float64_uniform :: float64_range
+float32_uniform :: float32_range
+
+// Triangular Distribution
+// See: http://wikipedia.org/wiki/Triangular_distribution
+float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
+ if hi-lo == 0 {
+ return lo
+ }
+ lo, hi := lo, hi
+ u := float64(r)
+ c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
+ if u > c {
+ u = 1-u
+ c = 1-c
+ lo, hi = hi, lo
+ }
+ return lo + (hi - lo) * math.sqrt(u * c)
+
+}
+// Triangular Distribution
+// See: http://wikipedia.org/wiki/Triangular_distribution
+float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
+ if hi-lo == 0 {
+ return lo
+ }
+ lo, hi := lo, hi
+ u := float32(r)
+ c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
+ if u > c {
+ u = 1-u
+ c = 1-c
+ lo, hi = hi, lo
+ }
+ return lo + (hi - lo) * math.sqrt(u * c)
+}
+
+
+// Normal/Gaussian Distribution
+float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
+ return norm_float64(r) * stddev + mean
+}
+// Normal/Gaussian Distribution
+float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_normal(f64(mean), f64(stddev), r))
+}
+
+
+// Log Normal Distribution
+float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
+ return math.exp(float64_normal(mean, stddev, r))
+}
+// Log Normal Distribution
+float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_log_normal(f64(mean), f64(stddev), r))
+}
+
+
+// Exponential Distribution
+// `lambda` is 1.0/(desired mean). It should be non-zero.
+// Return values range from
+// 0 to positive infinity if lambda > 0
+// negative infinity to 0 if lambda <= 0
+float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
+ return - math.ln(1 - float64(r)) / lambda
+}
+// Exponential Distribution
+// `lambda` is 1.0/(desired mean). It should be non-zero.
+// Return values range from
+// 0 to positive infinity if lambda > 0
+// negative infinity to 0 if lambda <= 0
+float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_exponential(f64(lambda), r))
+}
+
+
+// Gamma Distribution (NOT THE GAMMA FUNCTION)
+//
+// Required: alpha > 0 and beta > 0
+//
+// math.pow(x, alpha-1) * math.exp(-x / beta)
+// pdf(x) = --------------------------------------------
+// math.gamma(alpha) * math.pow(beta, alpha)
+//
+// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
+float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
+ if alpha <= 0 || beta <= 0 {
+ panic(#procedure + ": alpha and beta must be > 0.0")
+ }
+
+ LOG4 :: 1.3862943611198906188344642429163531361510002687205105082413600189
+ SG_MAGIC_CONST :: 2.5040773967762740733732583523868748412194809812852436493487
+
+ switch {
+ case alpha > 1:
+ // R.C.H. Cheng, "The generation of Gamma variables with non-integral shape parameters", Applied Statistics, (1977), 26, No. 1, p71-74
+
+ ainv := math.sqrt(2 * alpha - 1)
+ bbb := alpha - LOG4
+ ccc := alpha + ainv
+ for {
+ u1 := float64(r)
+ if !(1e-7 < u1 && u1 < 0.9999999) {
+ continue
+ }
+ u2 := 1 - float64(r)
+ v := math.ln(u1 / (1 - u1)) / ainv
+ x := alpha * math.exp(v)
+ z := u1 * u1 * u2
+ t := bbb + ccc*v - x
+ if t + SG_MAGIC_CONST - 4.5 * z >= 0 || t >= math.ln(z) {
+ return x * beta
+ }
+ }
+ case alpha == 1:
+ // float64_exponential(1/beta)
+ return -math.ln(1 - float64(r)) * beta
+ case:
+ // ALGORITHM GS of Statistical Computing - Kennedy & Gentle
+ x: f64
+ for {
+ u := float64(r)
+ b := (math.e + alpha) / math.e
+ p := b * u
+ if p <= 1 {
+ x = math.pow(p, 1/alpha)
+ } else {
+ x = -math.ln((b - p) / alpha)
+ }
+ u1 := float64(r)
+ if p > 1 {
+ if u1 <= math.pow(x, alpha-1) {
+ break
+ }
+ } else if u1 <= math.exp(-x) {
+ break
+ }
+ }
+ return x * beta
+ }
+}
+// Gamma Distribution (NOT THE GAMMA FUNCTION)
+//
+// Required: alpha > 0 and beta > 0
+//
+// math.pow(x, alpha-1) * math.exp(-x / beta)
+// pdf(x) = --------------------------------------------
+// math.gamma(alpha) * math.pow(beta, alpha)
+//
+// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
+float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_gamma(f64(alpha), f64(beta), r))
+}
+
+
+// Beta Distribution
+//
+// Required: alpha > 0 and beta > 0
+//
+// Return values range between 0 and 1
+float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
+ if alpha <= 0 || beta <= 0 {
+ panic(#procedure + ": alpha and beta must be > 0.0")
+ }
+ // Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
+ y := float64_gamma(alpha, 1.0, r)
+ if y != 0 {
+ return y / (y + float64_gamma(beta, 1.0, r))
+ }
+ return 0
+}
+// Beta Distribution
+//
+// Required: alpha > 0 and beta > 0
+//
+// Return values range between 0 and 1
+float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_beta(f64(alpha), f64(beta), r))
+}
+
+
+// Pareto distribution, `alpha` is the shape parameter.
+// https://wikipedia.org/wiki/Pareto_distribution
+float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
+ return math.pow(1 - float64(r), -1.0 / alpha)
+}
+// Pareto distribution, `alpha` is the shape parameter.
+// https://wikipedia.org/wiki/Pareto_distribution
+float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_pareto(f64(alpha), r))
+}
+
+
+// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
+float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
+ u := 1 - float64(r)
+ return alpha * math.pow(-math.ln(u), 1.0/beta)
+}
+// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
+float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_weibull(f64(alpha), f64(beta), r))
+}
+
+
+// Circular Data (von Mises) Distribution
+// `mean_angle` is the in mean angle between 0 and 2pi radians
+// `kappa` is the concentration parameter which must be >= 0
+// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
+float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
+ // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
+
+ mu := mean_angle
+ if kappa <= 1e-6 {
+ return math.TAU * float64(r)
+ }
+
+ s := 0.5 / kappa
+ t := s + math.sqrt(1 + s*s)
+ z: f64
+ for {
+ u1 := float64(r)
+ z = math.cos(math.TAU * 0.5 * u1)
+
+ d := z / (t + z)
+ u2 := float64(r)
+ if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
+ break
+ }
+ }
+
+ q := 1.0 / t
+ f := (q + z) / (1 + q*z)
+ u3 := float64(r)
+ if u3 > 0.5 {
+ return math.mod(mu + math.acos(f), math.TAU)
+ } else {
+ return math.mod(mu - math.acos(f), math.TAU)
+ }
+}
+// Circular Data (von Mises) Distribution
+// `mean_angle` is the in mean angle between 0 and 2pi radians
+// `kappa` is the concentration parameter which must be >= 0
+// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
+float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
+}
+
+
+// Cauchy-Lorentz Distribution
+// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
+ assert(gamma > 0)
+
+ // Calculated from the inverse CDF
+
+ return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
+}
+// Cauchy-Lorentz Distribution
+// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
+}
+
+
+// Log Cauchy-Lorentz Distribution
+// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
+ assert(gamma > 0)
+ return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
+}
+// Log Cauchy-Lorentz Distribution
+// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
+}
+
+
+// Laplace Distribution
+// `b` is the scale where `b` > 0
+float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
+ assert(b > 0)
+ p := float64(r)-0.5
+ return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
+}
+// Laplace Distribution
+// `b` is the scale where `b` > 0
+float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_laplace(f64(mean), f64(b), r))
+}
+
+
+// Gompertz Distribution
+// `eta` is the shape, `b` is the scale
+// Both `eta` and `b` must be > 0
+float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
+ if eta <= 0 || b <= 0 {
+ panic(#procedure + ": eta and b must be > 0.0")
+ }
+
+ p := float64(r)
+ return math.ln(1 - math.ln(1 - p)/eta)/b
+}
+// Gompertz Distribution
+// `eta` is the shape, `b` is the scale
+// Both `eta` and `b` must be > 0
+float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
+ return f32(float64_gompertz(f64(eta), f64(b), r))
+}
diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin
new file mode 100644
index 000000000..c0f92e99c
--- /dev/null
+++ b/core/math/rand/exp.odin
@@ -0,0 +1,214 @@
+package rand
+
+import "core:math"
+
+// exp_float64 returns a exponential distribution in the range (0, max(f64)],
+// with an exponential distribution who rate parameter is 1 (lambda) and whose mean
+// is 1 (1/lambda).
+//
+// To produce a distribution with a differetn rate parameter, divide the result by
+// the desired rate parameter
+//
+// "The Ziggurat Method for Generating Random Variables"
+// Authors: George Marsaglia, Wai Wan Tsang
+// Submitted: 2000-04-15. Published: 2000-10-02.
+// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
+// https://www.jstatsoft.org/article/view/v005i08 [web page]
+//
+exp_float64 :: proc(r: ^Rand = nil) -> f64 {
+ re :: 7.69711747013104972
+
+ @(static)
+ ke := [256]u32{
+ 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
+ 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
+ 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78,
+ 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651,
+ 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca,
+ 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8,
+ 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea,
+ 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba,
+ 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed,
+ 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662,
+ 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3,
+ 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace,
+ 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6,
+ 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7,
+ 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415,
+ 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4,
+ 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36,
+ 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46,
+ 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac,
+ 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245,
+ 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52,
+ 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06,
+ 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0,
+ 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9,
+ 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76,
+ 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516,
+ 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289,
+ 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed,
+ 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb,
+ 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e,
+ 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a,
+ 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1,
+ 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b,
+ 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621,
+ 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d,
+ 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3,
+ 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73,
+ 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88,
+ 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a,
+ 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb,
+ 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176,
+ 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be,
+ 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192,
+ 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed,
+ 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936,
+ 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b,
+ 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4,
+ 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1,
+ 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482,
+ 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023,
+ 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
+ 0xe6da6ecf,
+ }
+ @(static)
+ we := [256]f32{
+ 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
+ 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
+ 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11,
+ 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11,
+ 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10,
+ 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10,
+ 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10,
+ 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10,
+ 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10,
+ 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10,
+ 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10,
+ 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10,
+ 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10,
+ 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10,
+ 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10,
+ 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10,
+ 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10,
+ 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10,
+ 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10,
+ 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10,
+ 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10,
+ 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10,
+ 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10,
+ 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10,
+ 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10,
+ 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10,
+ 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10,
+ 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10,
+ 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10,
+ 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10,
+ 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10,
+ 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10,
+ 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10,
+ 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10,
+ 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10,
+ 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10,
+ 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10,
+ 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10,
+ 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10,
+ 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10,
+ 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10,
+ 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10,
+ 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10,
+ 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10,
+ 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10,
+ 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10,
+ 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10,
+ 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10,
+ 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10,
+ 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10,
+ 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10,
+ 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10,
+ 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10,
+ 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10,
+ 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10,
+ 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10,
+ 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10,
+ 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10,
+ 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10,
+ 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09,
+ 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09,
+ 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09,
+ 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
+ 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
+ }
+ @(static)
+ fe := [256]f32{
+ 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
+ 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
+ 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665,
+ 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967,
+ 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896,
+ 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092,
+ 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386,
+ 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495,
+ 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752,
+ 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325,
+ 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955,
+ 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694,
+ 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218,
+ 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763,
+ 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044,
+ 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796,
+ 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408,
+ 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928,
+ 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393,
+ 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625,
+ 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107,
+ 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878,
+ 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438,
+ 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682,
+ 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852,
+ 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479,
+ 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354,
+ 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494,
+ 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119,
+ 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624,
+ 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574,
+ 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672,
+ 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763,
+ 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816,
+ 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919,
+ 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274,
+ 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195,
+ 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106,
+ 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434,
+ 0.062193416, 0.060783047, 0.059384305, 0.057997175,
+ 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236,
+ 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623,
+ 0.043502413, 0.042254124, 0.041017443, 0.039792392,
+ 0.038578995, 0.037377283, 0.036187284, 0.035009038,
+ 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566,
+ 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421,
+ 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867,
+ 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392,
+ 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414,
+ 0.008780315, 0.007963077, 0.0071633533, 0.006381906,
+ 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648,
+ 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693,
+ 0.00045413437,
+ }
+
+ for {
+ j := uint32(r)
+ i := j & 0xFF
+ x := f64(j) * f64(we[i])
+ if j < ke[i] {
+ return x
+ }
+ if i == 0 {
+ return re - math.ln(float64(r))
+ }
+ if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
+ return x
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin
index 4a77543ba..a9edd0f19 100644
--- a/core/math/rand/normal.odin
+++ b/core/math/rand/normal.odin
@@ -2,6 +2,12 @@ package rand
import "core:math"
+
+// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
+// with a standard normal distribution with a mean of 0 and standard deviation of 1.
+//
+// sample = norm_float64() * std_dev + mean
+//
//
// Normal distribution
//
@@ -11,12 +17,6 @@ import "core:math"
// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
// https://www.jstatsoft.org/article/view/v005i08 [web page]
//
-
-// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
-// with a standard normal distribution with a mean of 0 and standard deviation of 1.
-//
-// sample = norm_float64() * std_dev + mean
-//
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899
@@ -49,7 +49,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
0x7ba90bdc, 0x7a722176, 0x77d664e5,
}
-
@(static)
wn := [128]f32{
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
@@ -85,7 +84,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
}
-
@(static)
fn := [128]f32{
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 9bd30c216..f7dfcb3b8 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -1,15 +1,16 @@
package rand
+import "core:intrinsics"
+
Rand :: struct {
state: u64,
inc: u64,
+ is_system: bool,
}
@(private)
-_GLOBAL_SEED_DATA := 1234567890
-@(private)
-global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)))
+global_rand := create(u64(intrinsics.read_cycle_counter()))
set_global_seed :: proc(seed: u64) {
init(&global_rand, seed)
@@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) {
_random(r)
}
+init_as_system :: proc(r: ^Rand) {
+ if !#defined(_system_random) {
+ panic(#procedure + " is not supported on this platform yet")
+ }
+ r.state = 0
+ r.inc = 0
+ r.is_system = true
+}
+
+@(private)
_random :: proc(r: ^Rand) -> u32 {
r := r
if r == nil {
@@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 {
// enforce the global random state if necessary with `nil`
r = &global_rand
}
+ when #defined(_system_random) {
+ if r.is_system {
+ return _system_random()
+ }
+ }
+
old_state := r.state
r.state = old_state * 6364136223846793005 + (r.inc|1)
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
@@ -70,7 +87,7 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
if n&(n-1) == 0 {
return int31(r) & (n-1)
}
- max := i32((1<<31) - 1 - (1<<31)&u32(n))
+ max := i32((1<<31) - 1 - (1<<31)%u32(n))
v := int31(r)
for v > max {
v = int31(r)
@@ -85,7 +102,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
if n&(n-1) == 0 {
return int63(r) & (n-1)
}
- max := i64((1<<63) - 1 - (1<<63)&u64(n))
+ max := i64((1<<63) - 1 - (1<<63)%u64(n))
v := int63(r)
for v > max {
v = int63(r)
@@ -100,7 +117,7 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
if n&(n-1) == 0 {
return int127(r) & (n-1)
}
- max := i128((1<<63) - 1 - (1<<63)&u128(n))
+ max := i128((1<<127) - 1 - (1<<127)%u128(n))
v := int127(r)
for v > max {
v = int127(r)
@@ -119,13 +136,14 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int {
}
}
+// Uniform random distribution [0, 1)
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) }
+// Uniform random distribution [0, 1)
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo }
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo }
-
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
pos := i8(0)
val := i64(0)
@@ -142,8 +160,8 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
}
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
-perm :: proc(n: int, r: ^Rand = nil) -> []int {
- m := make([]int, n)
+perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int {
+ m := make([]int, n, allocator)
for i := 0; i < n; i += 1 {
j := int_max(i+1, r)
m[i] = m[j]
diff --git a/core/math/rand/system_darwin.odin b/core/math/rand/system_darwin.odin
new file mode 100644
index 000000000..f51e4473e
--- /dev/null
+++ b/core/math/rand/system_darwin.odin
@@ -0,0 +1,21 @@
+package rand
+
+import "core:sys/darwin"
+
+_system_random :: proc() -> u32 {
+ for {
+ value: u32
+ ret := darwin.syscall_getentropy(([^]u8)(&value), 4)
+ if ret < 0 {
+ switch ret {
+ case -4: // EINTR
+ continue
+ case -78: // ENOSYS
+ panic("getentropy not available in kernel")
+ case:
+ panic("getentropy failed")
+ }
+ }
+ return value
+ }
+}
\ No newline at end of file
diff --git a/core/math/rand/system_linux.odin b/core/math/rand/system_linux.odin
new file mode 100644
index 000000000..bfdc8872b
--- /dev/null
+++ b/core/math/rand/system_linux.odin
@@ -0,0 +1,27 @@
+package rand
+
+import "core:sys/unix"
+
+_system_random :: proc() -> u32 {
+ for {
+ value: u32
+ ret := unix.sys_getrandom(([^]u8)(&value), 4, 0)
+ if ret < 0 {
+ switch ret {
+ case -4: // EINTR
+ // Call interupted by a signal handler, just retry the request.
+ continue
+ case -38: // ENOSYS
+ // The kernel is apparently prehistoric (< 3.17 circa 2014)
+ // and does not support getrandom.
+ panic("getrandom not available in kernel")
+ case:
+ // All other failures are things that should NEVER happen
+ // unless the kernel interface changes (ie: the Linux
+ // developers break userland).
+ panic("getrandom failed")
+ }
+ }
+ return value
+ }
+}
\ No newline at end of file
diff --git a/core/math/rand/system_windows.odin b/core/math/rand/system_windows.odin
new file mode 100644
index 000000000..ee9cd0294
--- /dev/null
+++ b/core/math/rand/system_windows.odin
@@ -0,0 +1,12 @@
+package rand
+
+import win32 "core:sys/windows"
+
+_system_random :: proc() -> u32 {
+ value: u32
+ status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
+ if status < 0 {
+ panic("BCryptGenRandom failed")
+ }
+ return value
+}
\ No newline at end of file
diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin
index bdd2899a9..7416ebdb7 100644
--- a/core/mem/alloc.odin
+++ b/core/mem/alloc.odin
@@ -55,6 +55,11 @@ Allocator :: struct {
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
+DEFAULT_PAGE_SIZE ::
+ 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 else
+ 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else
+ 4 * 1024
+
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 {
return nil
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index b8bd9a065..e2a0cc0fc 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -52,15 +52,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
switch mode {
case .Alloc:
- total_size := size + alignment
+ #no_bounds_check end := &arena.data[arena.offset]
+
+ ptr := align_forward(end, uintptr(alignment))
+
+ total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
if arena.offset + total_size > len(arena.data) {
return nil, .Out_Of_Memory
}
- #no_bounds_check end := &arena.data[arena.offset]
-
- ptr := align_forward(end, uintptr(alignment))
arena.offset += total_size
arena.peak_used = max(arena.peak_used, arena.offset)
zero(ptr, size)
@@ -662,6 +663,7 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_free_all(pool)
delete(unused_blocks)
delete(used_blocks)
+ delete(out_band_allocations)
zero(pool, size_of(pool^))
}
@@ -746,6 +748,8 @@ dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
free(a, block_allocator)
}
clear(&out_band_allocations)
+
+ bytes_left = 0 // Make new allocations call `cycle_new_block` again.
}
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
@@ -855,7 +859,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
result: []byte
err: Allocator_Error
- if mode == .Free && old_memory not_in data.allocation_map {
+ if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory,
location = loc,
@@ -883,6 +887,10 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
case .Free:
delete_key(&data.allocation_map, old_memory)
+ case .Free_All:
+ if data.clear_on_free_all {
+ clear_map(&data.allocation_map)
+ }
case .Resize:
if old_memory != result_ptr {
delete_key(&data.allocation_map, old_memory)
@@ -895,11 +903,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
location = loc,
}
- case .Free_All:
- if data.clear_on_free_all {
- clear_map(&data.allocation_map)
- }
-
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
diff --git a/core/mem/doc.odin b/core/mem/doc.odin
new file mode 100644
index 000000000..fe53dee83
--- /dev/null
+++ b/core/mem/doc.odin
@@ -0,0 +1,34 @@
+/*
+package mem implements various types of allocators.
+
+
+An example of how to use the `Tracking_Allocator` to track subsequent allocations
+in your program and report leaks and bad frees:
+
+```odin
+package foo
+
+import "core:mem"
+import "core:fmt"
+
+_main :: proc() {
+ do stuff
+}
+
+main :: proc() {
+ track: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&track, context.allocator)
+ context.allocator = mem.tracking_allocator(&track)
+
+ _main()
+
+ for _, leak in track.allocation_map {
+ fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
+ }
+ for bad_free in track.bad_free_array {
+ fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
+ }
+}
+```
+*/
+package mem
\ No newline at end of file
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 8eb877e75..7295a2afa 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -3,6 +3,12 @@ package mem
import "core:runtime"
import "core:intrinsics"
+Byte :: 1
+Kilobyte :: 1024 * Byte
+Megabyte :: 1024 * Kilobyte
+Gigabyte :: 1024 * Megabyte
+Terabyte :: 1024 * Gigabyte
+
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
return runtime.memset(data, i32(value), len)
}
@@ -16,14 +22,16 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
// memset_s call.
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
- intrinsics.atomic_fence() // Prevent reordering
+ intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
-zero_item :: proc "contextless" (item: $P/^$T) {
+zero_item :: proc "contextless" (item: $P/^$T) -> P {
intrinsics.mem_zero(item, size_of(T))
+ return item
}
-zero_slice :: proc "contextless" (data: $T/[]$E) {
+zero_slice :: proc "contextless" (data: $T/[]$E) -> T {
zero(raw_data(data), size_of(E)*len(data))
+ return data
}
@@ -101,6 +109,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
case ptr == nil:
return true
}
+ switch len {
+ case 1: return (^u8)(ptr)^ == 0
+ case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0
+ case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0
+ case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0
+ }
start := uintptr(ptr)
start_aligned := align_forward_uintptr(start, align_of(uintptr))
@@ -144,7 +158,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
return ([^]T)(ptr)[:len]
}
-byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
+byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
return ([^]u8)(data)[:max(len, 0)]
}
@@ -166,7 +180,7 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
s := transmute(Raw_Slice)slice
- return s.data, s.len
+ return (^T)(s.data), s.len
}
buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
@@ -192,11 +206,6 @@ any_to_bytes :: proc "contextless" (val: any) -> []byte {
}
-kilobytes :: proc "contextless" (x: int) -> int { return (x) * 1024 }
-megabytes :: proc "contextless" (x: int) -> int { return kilobytes(x) * 1024 }
-gigabytes :: proc "contextless" (x: int) -> int { return megabytes(x) * 1024 }
-terabytes :: proc "contextless" (x: int) -> int { return gigabytes(x) * 1024 }
-
is_power_of_two :: proc "contextless" (x: uintptr) -> bool {
if x <= 0 {
return false
diff --git a/core/mem/raw.odin b/core/mem/raw.odin
index 9eef4f6e3..2bce2d7aa 100644
--- a/core/mem/raw.odin
+++ b/core/mem/raw.odin
@@ -20,20 +20,12 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id}
}
-raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
- return (^E)(a)
-}
-raw_string_data :: proc "contextless" (s: $T/string) -> ^byte {
- return (transmute(Raw_String)s).data
-}
-raw_slice_data :: proc "contextless" (a: $T/[]$E) -> ^E {
- return cast(^E)(transmute(Raw_Slice)a).data
-}
-raw_dynamic_array_data :: proc "contextless" (a: $T/[dynamic]$E) -> ^E {
- return cast(^E)(transmute(Raw_Dynamic_Array)a).data
-}
-
-raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data}
+raw_array_data :: runtime.raw_array_data
+raw_simd_data :: runtime.raw_simd_data
+raw_string_data :: runtime.raw_string_data
+raw_slice_data :: runtime.raw_slice_data
+raw_dynamic_array_data :: runtime.raw_dynamic_array_data
+raw_data :: runtime.raw_data
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin
index 38c654254..21ab5ef21 100644
--- a/core/mem/virtual/virtual.odin
+++ b/core/mem/virtual/virtual.odin
@@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096)
Allocator_Error :: mem.Allocator_Error
-reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
-commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
return _commit(data, size)
}
-reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size) or_return
return
}
-decommit :: proc(data: rawptr, size: uint) {
+decommit :: proc "contextless" (data: rawptr, size: uint) {
_decommit(data, size)
}
-release :: proc(data: rawptr, size: uint) {
+release :: proc "contextless" (data: rawptr, size: uint) {
_release(data, size)
}
@@ -36,7 +36,7 @@ Protect_Flag :: enum u32 {
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
Protect_No_Access :: Protect_Flags{}
-protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
return _protect(data, size, flags)
}
@@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
pmblock := platform_memory_alloc(0, total_size) or_return
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
- commit(pmblock.block.base, committed) or_return
+ commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
+ assert(commit_err == nil)
+
// Should be zeroed
assert(pmblock.block.used == 0)
assert(pmblock.block.prev == nil)
- if (do_protection) {
+ if do_protection {
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
}
@@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
}
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
- calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
+ calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:])
mask := alignment-1
@@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
return alignment_offset
}
-
+ do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
+ if block.committed - block.used < size {
+ pmblock := (^Platform_Memory_Block)(block)
+ base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
+ platform_total_commit := base_offset + block.used + size
+
+ assert(pmblock.committed <= pmblock.reserved)
+ assert(pmblock.committed < platform_total_commit)
+
+ platform_memory_commit(pmblock, platform_total_commit) or_return
+
+ pmblock.committed = platform_total_commit
+ block.committed = pmblock.committed - base_offset
+ }
+ return nil
+ }
+
+
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
-
size := uint(min_size) + alignment_offset
-
+
if block.used + size > block.reserved {
err = .Out_Of_Memory
return
}
-
- ptr := block.base[block.used:]
- ptr = ptr[alignment_offset:]
-
+ assert(block.committed <= block.reserved)
+ do_commit_if_necessary(block, size) or_return
+
+ data = block.base[block.used+alignment_offset:][:min_size]
block.used += size
- assert(block.used <= block.reserved)
-
- return ptr[:min_size], nil
+ return
}
diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin
index c4dd564ee..2f6fbdd01 100644
--- a/core/mem/virtual/virtual_linux.odin
+++ b/core/mem/virtual/virtual_linux.odin
@@ -4,78 +4,69 @@ package mem_virtual
import "core:c"
import "core:intrinsics"
+import "core:sys/unix"
-when ODIN_ARCH == "amd64" {
- SYS_mmap :: 9
- SYS_mprotect :: 10
- SYS_munmap :: 11
- SYS_madvise :: 28
-
- PROT_NONE :: 0x0
- PROT_READ :: 0x1
- PROT_WRITE :: 0x2
- PROT_EXEC :: 0x4
- PROT_GROWSDOWN :: 0x01000000
- PROT_GROWSUP :: 0x02000000
+PROT_NONE :: 0x0
+PROT_READ :: 0x1
+PROT_WRITE :: 0x2
+PROT_EXEC :: 0x4
+PROT_GROWSDOWN :: 0x01000000
+PROT_GROWSUP :: 0x02000000
- MAP_FIXED :: 0x1
- MAP_PRIVATE :: 0x2
- MAP_SHARED :: 0x4
- MAP_ANONYMOUS :: 0x20
-
- MADV_NORMAL :: 0
- MADV_RANDOM :: 1
- MADV_SEQUENTIAL :: 2
- MADV_WILLNEED :: 3
- MADV_DONTNEED :: 4
- MADV_FREE :: 8
- MADV_REMOVE :: 9
- MADV_DONTFORK :: 10
- MADV_DOFORK :: 11
- MADV_MERGEABLE :: 12
- MADV_UNMERGEABLE :: 13
- MADV_HUGEPAGE :: 14
- MADV_NOHUGEPAGE :: 15
- MADV_DONTDUMP :: 16
- MADV_DODUMP :: 17
- MADV_WIPEONFORK :: 18
- MADV_KEEPONFORK :: 19
- MADV_HWPOISON :: 100
-} else {
- #panic("Unsupported architecture")
-}
+MAP_FIXED :: 0x1
+MAP_PRIVATE :: 0x2
+MAP_SHARED :: 0x4
+MAP_ANONYMOUS :: 0x20
-mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
- res := intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
- return rawptr(res)
+MADV_NORMAL :: 0
+MADV_RANDOM :: 1
+MADV_SEQUENTIAL :: 2
+MADV_WILLNEED :: 3
+MADV_DONTNEED :: 4
+MADV_FREE :: 8
+MADV_REMOVE :: 9
+MADV_DONTFORK :: 10
+MADV_DOFORK :: 11
+MADV_MERGEABLE :: 12
+MADV_UNMERGEABLE :: 13
+MADV_HUGEPAGE :: 14
+MADV_NOHUGEPAGE :: 15
+MADV_DONTDUMP :: 16
+MADV_DODUMP :: 17
+MADV_WIPEONFORK :: 18
+MADV_KEEPONFORK :: 19
+MADV_HWPOISON :: 100
+
+mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> int {
+ res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
+ return int(res)
}
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
- res := intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))
+ res := intrinsics.syscall(unix.SYS_munmap, uintptr(addr), uintptr(length))
return c.int(res)
}
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
- res := intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
+ res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
return c.int(res)
}
madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.int {
- res := intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
+ res := intrinsics.syscall(unix.SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
return c.int(res)
}
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
- MAP_FAILED := rawptr(~uintptr(0))
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
- if result == MAP_FAILED {
+ if result < 0 && result > -4096 {
return nil, .Out_Of_Memory
}
- return ([^]byte)(result)[:size], nil
+ return ([^]byte)(uintptr(result))[:size], nil
}
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := mprotect(data, size, PROT_READ|PROT_WRITE)
if result != 0 {
// TODO(bill): Handle error value correctly
@@ -83,14 +74,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
}
return nil
}
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
mprotect(data, size, PROT_NONE)
madvise(data, size, MADV_FREE)
}
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
munmap(data, size)
}
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: c.int
pflags = PROT_NONE
if .Read in flags { pflags |= PROT_READ }
diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin
index c4211ba5e..367346f63 100644
--- a/core/mem/virtual/virtual_platform.odin
+++ b/core/mem/virtual/virtual_platform.odin
@@ -1,15 +1,16 @@
//+private
package mem_virtual
-import sync "core:sync/sync2"
+import "core:sync"
Platform_Memory_Block :: struct {
- block: Memory_Block,
- reserved: uint,
+ block: Memory_Block,
+ committed: uint,
+ reserved: uint,
prev, next: ^Platform_Memory_Block,
}
-platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
+platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
to_commit, to_reserve := to_commit, to_reserve
to_reserve = max(to_commit, to_reserve)
@@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_
commit(raw_data(data), to_commit)
block = (^Platform_Memory_Block)(raw_data(data))
- block.reserved = to_reserve
+ block.committed = to_commit
+ block.reserved = to_reserve
return
}
-platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
if block != nil {
release(block, block.reserved)
}
@@ -52,3 +54,17 @@ platform_memory_init :: proc() {
global_platform_memory_block_sentinel_set = true
}
}
+
+platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
+ if to_commit < block.committed {
+ return nil
+ }
+ if to_commit > block.reserved {
+ return .Out_Of_Memory
+ }
+
+
+ commit(block, to_commit) or_return
+ block.committed = to_commit
+ return nil
+}
diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin
index 623e8d469..ef0bf6f1a 100644
--- a/core/mem/virtual/virtual_windows.odin
+++ b/core/mem/virtual/virtual_windows.odin
@@ -62,7 +62,7 @@ foreign Kernel32 {
}
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
if result == nil {
err = .Out_Of_Memory
@@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
return
}
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
if result == nil {
switch err := GetLastError(); err {
@@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
}
return nil
}
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, size, MEM_DECOMMIT)
}
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, 0, MEM_RELEASE)
}
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: u32
pflags = PAGE_NOACCESS
switch flags {
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 260979d89..f4aa67446 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -34,7 +34,7 @@ Node :: struct {
pos: tokenizer.Pos,
end: tokenizer.Pos,
state_flags: Node_State_Flags,
- derived: any,
+ derived: Any_Node,
}
Comment_Group :: struct {
@@ -88,9 +88,11 @@ File :: struct {
Expr :: struct {
using expr_base: Node,
+ derived_expr: Any_Expr,
}
Stmt :: struct {
using stmt_base: Node,
+ derived_stmt: Any_Stmt,
}
Decl :: struct {
using decl_base: Stmt,
@@ -151,6 +153,7 @@ Comp_Lit :: struct {
open: tokenizer.Pos,
elems: []^Expr,
close: tokenizer.Pos,
+ tag: ^Expr,
}
@@ -540,7 +543,7 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
return
}
for {
- e, ok := val.derived.(Paren_Expr)
+ e, ok := val.derived.(^Paren_Expr)
if !ok || e.expr == nil {
break
}
@@ -705,12 +708,19 @@ Struct_Type :: struct {
name_count: int,
}
+Union_Type_Kind :: enum u8 {
+ Normal,
+ maybe,
+ no_nil,
+ shared_nil,
+}
+
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
- is_maybe: bool,
+ kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,
@@ -756,4 +766,173 @@ Matrix_Type :: struct {
row_count: ^Expr,
column_count: ^Expr,
elem: ^Expr,
-}
\ No newline at end of file
+}
+
+
+Any_Node :: union {
+ ^Package,
+ ^File,
+ ^Comment_Group,
+
+ ^Bad_Expr,
+ ^Ident,
+ ^Implicit,
+ ^Undef,
+ ^Basic_Lit,
+ ^Basic_Directive,
+ ^Ellipsis,
+ ^Proc_Lit,
+ ^Comp_Lit,
+ ^Tag_Expr,
+ ^Unary_Expr,
+ ^Binary_Expr,
+ ^Paren_Expr,
+ ^Selector_Expr,
+ ^Implicit_Selector_Expr,
+ ^Selector_Call_Expr,
+ ^Index_Expr,
+ ^Deref_Expr,
+ ^Slice_Expr,
+ ^Matrix_Index_Expr,
+ ^Call_Expr,
+ ^Field_Value,
+ ^Ternary_If_Expr,
+ ^Ternary_When_Expr,
+ ^Or_Else_Expr,
+ ^Or_Return_Expr,
+ ^Type_Assertion,
+ ^Type_Cast,
+ ^Auto_Cast,
+ ^Inline_Asm_Expr,
+
+ ^Proc_Group,
+
+ ^Typeid_Type,
+ ^Helper_Type,
+ ^Distinct_Type,
+ ^Poly_Type,
+ ^Proc_Type,
+ ^Pointer_Type,
+ ^Multi_Pointer_Type,
+ ^Array_Type,
+ ^Dynamic_Array_Type,
+ ^Struct_Type,
+ ^Union_Type,
+ ^Enum_Type,
+ ^Bit_Set_Type,
+ ^Map_Type,
+ ^Relative_Type,
+ ^Matrix_Type,
+
+ ^Bad_Stmt,
+ ^Empty_Stmt,
+ ^Expr_Stmt,
+ ^Tag_Stmt,
+ ^Assign_Stmt,
+ ^Block_Stmt,
+ ^If_Stmt,
+ ^When_Stmt,
+ ^Return_Stmt,
+ ^Defer_Stmt,
+ ^For_Stmt,
+ ^Range_Stmt,
+ ^Inline_Range_Stmt,
+ ^Case_Clause,
+ ^Switch_Stmt,
+ ^Type_Switch_Stmt,
+ ^Branch_Stmt,
+ ^Using_Stmt,
+
+ ^Bad_Decl,
+ ^Value_Decl,
+ ^Package_Decl,
+ ^Import_Decl,
+ ^Foreign_Block_Decl,
+ ^Foreign_Import_Decl,
+
+ ^Attribute,
+ ^Field,
+ ^Field_List,
+}
+
+
+Any_Expr :: union {
+ ^Bad_Expr,
+ ^Ident,
+ ^Implicit,
+ ^Undef,
+ ^Basic_Lit,
+ ^Basic_Directive,
+ ^Ellipsis,
+ ^Proc_Lit,
+ ^Comp_Lit,
+ ^Tag_Expr,
+ ^Unary_Expr,
+ ^Binary_Expr,
+ ^Paren_Expr,
+ ^Selector_Expr,
+ ^Implicit_Selector_Expr,
+ ^Selector_Call_Expr,
+ ^Index_Expr,
+ ^Deref_Expr,
+ ^Slice_Expr,
+ ^Matrix_Index_Expr,
+ ^Call_Expr,
+ ^Field_Value,
+ ^Ternary_If_Expr,
+ ^Ternary_When_Expr,
+ ^Or_Else_Expr,
+ ^Or_Return_Expr,
+ ^Type_Assertion,
+ ^Type_Cast,
+ ^Auto_Cast,
+ ^Inline_Asm_Expr,
+
+ ^Proc_Group,
+
+ ^Typeid_Type,
+ ^Helper_Type,
+ ^Distinct_Type,
+ ^Poly_Type,
+ ^Proc_Type,
+ ^Pointer_Type,
+ ^Multi_Pointer_Type,
+ ^Array_Type,
+ ^Dynamic_Array_Type,
+ ^Struct_Type,
+ ^Union_Type,
+ ^Enum_Type,
+ ^Bit_Set_Type,
+ ^Map_Type,
+ ^Relative_Type,
+ ^Matrix_Type,
+}
+
+
+Any_Stmt :: union {
+ ^Bad_Stmt,
+ ^Empty_Stmt,
+ ^Expr_Stmt,
+ ^Tag_Stmt,
+ ^Assign_Stmt,
+ ^Block_Stmt,
+ ^If_Stmt,
+ ^When_Stmt,
+ ^Return_Stmt,
+ ^Defer_Stmt,
+ ^For_Stmt,
+ ^Range_Stmt,
+ ^Inline_Range_Stmt,
+ ^Case_Clause,
+ ^Switch_Stmt,
+ ^Type_Switch_Stmt,
+ ^Branch_Stmt,
+ ^Using_Stmt,
+
+ ^Bad_Decl,
+ ^Value_Decl,
+ ^Package_Decl,
+ ^Import_Decl,
+ ^Foreign_Block_Decl,
+ ^Foreign_Import_Decl,
+}
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 1e3058678..400c064f5 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -1,16 +1,25 @@
package odin_ast
+import "core:intrinsics"
import "core:mem"
import "core:fmt"
+import "core:reflect"
import "core:odin/tokenizer"
+_ :: intrinsics
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n, _ := mem.new(T)
n.pos = pos
n.end = end
- n.derived = n^
+ n.derived = n
base: ^Node = n // dummy check
_ = base // "Use" type to make -vet happy
+ when intrinsics.type_has_field(T, "derived_expr") {
+ n.derived_expr = n
+ }
+ when intrinsics.type_has_field(T, "derived_stmt") {
+ n.derived_stmt = n
+ }
return n
}
@@ -59,232 +68,257 @@ clone_node :: proc(node: ^Node) -> ^Node {
return nil
}
- size := size_of(Node)
+ size := size_of(Node)
align := align_of(Node)
- ti := type_info_of(node.derived.id)
+ ti := reflect.union_variant_type_info(node.derived)
if ti != nil {
- size = ti.size
- align = ti.align
+ elem := ti.variant.(reflect.Type_Info_Pointer).elem
+ size = elem.size
+ align = elem.align
}
- switch in node.derived {
- case Package, File:
+ #partial switch in node.derived {
+ case ^Package, ^File:
panic("Cannot clone this node type")
}
res := cast(^Node)mem.alloc(size, align)
src: rawptr = node
if node.derived != nil {
- src = node.derived.data
+ src = (^rawptr)(&node.derived)^
}
mem.copy(res, src, size)
- res.derived.data = rawptr(res)
- res.derived.id = node.derived.id
+ res_ptr_any: any
+ res_ptr_any.data = &res
+ res_ptr_any.id = ti.id
- switch r in &res.derived {
- case Bad_Expr:
- case Ident:
- case Implicit:
- case Undef:
- case Basic_Lit:
+ reflect.set_union_value(res.derived, res_ptr_any)
- case Ellipsis:
+ res_ptr := reflect.deref(res_ptr_any)
+
+ if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil {
+ reflect.set_union_value(de, res_ptr_any)
+ }
+ if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil {
+ reflect.set_union_value(ds, res_ptr_any)
+ }
+
+ if res.derived != nil do switch r in res.derived {
+ case ^Package, ^File:
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Basic_Directive:
+ case ^Comment_Group:
+
+ case ^Ellipsis:
r.expr = clone(r.expr)
- case Proc_Lit:
+ case ^Proc_Lit:
r.type = auto_cast clone(r.type)
r.body = clone(r.body)
- case Comp_Lit:
+ case ^Comp_Lit:
r.type = clone(r.type)
r.elems = clone(r.elems)
- case Tag_Expr:
+ case ^Tag_Expr:
r.expr = clone(r.expr)
- case Unary_Expr:
+ case ^Unary_Expr:
r.expr = clone(r.expr)
- case Binary_Expr:
+ case ^Binary_Expr:
r.left = clone(r.left)
r.right = clone(r.right)
- case Paren_Expr:
+ case ^Paren_Expr:
r.expr = clone(r.expr)
- case Selector_Expr:
+ case ^Selector_Expr:
r.expr = clone(r.expr)
r.field = auto_cast clone(r.field)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
r.field = auto_cast clone(r.field)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
r.expr = clone(r.expr)
r.call = auto_cast clone(r.call)
- case Index_Expr:
+ case ^Index_Expr:
r.expr = clone(r.expr)
r.index = clone(r.index)
- case Matrix_Index_Expr:
+ case ^Matrix_Index_Expr:
r.expr = clone(r.expr)
r.row_index = clone(r.row_index)
r.column_index = clone(r.column_index)
- case Deref_Expr:
+ case ^Deref_Expr:
r.expr = clone(r.expr)
- case Slice_Expr:
+ case ^Slice_Expr:
r.expr = clone(r.expr)
r.low = clone(r.low)
r.high = clone(r.high)
- case Call_Expr:
+ case ^Call_Expr:
r.expr = clone(r.expr)
r.args = clone(r.args)
- case Field_Value:
+ case ^Field_Value:
r.field = clone(r.field)
r.value = clone(r.value)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
r.x = clone(r.x)
r.y = clone(r.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
r.expr = clone(r.expr)
- case Type_Assertion:
+ case ^Type_Assertion:
r.expr = clone(r.expr)
r.type = clone(r.type)
- case Type_Cast:
+ case ^Type_Cast:
r.type = clone(r.type)
r.expr = clone(r.expr)
- case Auto_Cast:
+ case ^Auto_Cast:
r.expr = clone(r.expr)
- case Inline_Asm_Expr:
+ case ^Inline_Asm_Expr:
r.param_types = clone(r.param_types)
r.return_type = clone(r.return_type)
r.constraints_string = clone(r.constraints_string)
r.asm_string = clone(r.asm_string)
- case Bad_Stmt:
+ case ^Bad_Stmt:
// empty
- case Empty_Stmt:
+ case ^Empty_Stmt:
// empty
- case Expr_Stmt:
+ case ^Expr_Stmt:
r.expr = clone(r.expr)
- case Tag_Stmt:
+ case ^Tag_Stmt:
r.stmt = clone(r.stmt)
- case Assign_Stmt:
+ case ^Assign_Stmt:
r.lhs = clone(r.lhs)
r.rhs = clone(r.rhs)
- case Block_Stmt:
+ case ^Block_Stmt:
r.label = clone(r.label)
r.stmts = clone(r.stmts)
- case If_Stmt:
+ case ^If_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
- case When_Stmt:
+ case ^When_Stmt:
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
- case Return_Stmt:
+ case ^Return_Stmt:
r.results = clone(r.results)
- case Defer_Stmt:
+ case ^Defer_Stmt:
r.stmt = clone(r.stmt)
- case For_Stmt:
+ case ^For_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.post = clone(r.post)
r.body = clone(r.body)
- case Range_Stmt:
+ case ^Range_Stmt:
r.label = clone(r.label)
r.vals = clone(r.vals)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case Case_Clause:
+ case ^Inline_Range_Stmt:
+ r.label = clone(r.label)
+ r.val0 = clone(r.val0)
+ r.val1 = clone(r.val1)
+ r.expr = clone(r.expr)
+ r.body = clone(r.body)
+ case ^Case_Clause:
r.list = clone(r.list)
r.body = clone(r.body)
- case Switch_Stmt:
+ case ^Switch_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
r.label = clone(r.label)
r.tag = clone(r.tag)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case Branch_Stmt:
+ case ^Branch_Stmt:
r.label = auto_cast clone(r.label)
- case Using_Stmt:
+ case ^Using_Stmt:
r.list = clone(r.list)
- case Bad_Decl:
- case Value_Decl:
+ case ^Bad_Decl:
+ case ^Value_Decl:
r.attributes = clone(r.attributes)
r.names = clone(r.names)
r.type = clone(r.type)
r.values = clone(r.values)
- case Package_Decl:
- case Import_Decl:
- case Foreign_Block_Decl:
+ case ^Package_Decl:
+ case ^Import_Decl:
+ case ^Foreign_Block_Decl:
r.attributes = clone(r.attributes)
r.foreign_library = clone(r.foreign_library)
r.body = clone(r.body)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
r.name = auto_cast clone(r.name)
- case Proc_Group:
+ case ^Proc_Group:
r.args = clone(r.args)
- case Attribute:
+ case ^Attribute:
r.elems = clone(r.elems)
- case Field:
+ case ^Field:
r.names = clone(r.names)
r.type = clone(r.type)
r.default_value = clone(r.default_value)
- case Field_List:
+ case ^Field_List:
r.list = clone(r.list)
- case Typeid_Type:
+ case ^Typeid_Type:
r.specialization = clone(r.specialization)
- case Helper_Type:
+ case ^Helper_Type:
r.type = clone(r.type)
- case Distinct_Type:
+ case ^Distinct_Type:
r.type = clone(r.type)
- case Poly_Type:
+ case ^Poly_Type:
r.type = auto_cast clone(r.type)
r.specialization = clone(r.specialization)
- case Proc_Type:
+ case ^Proc_Type:
r.params = auto_cast clone(r.params)
r.results = auto_cast clone(r.results)
- case Pointer_Type:
+ case ^Pointer_Type:
r.elem = clone(r.elem)
- case Multi_Pointer_Type:
+ case ^Multi_Pointer_Type:
r.elem = clone(r.elem)
- case Array_Type:
+ case ^Array_Type:
r.len = clone(r.len)
r.elem = clone(r.elem)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
r.elem = clone(r.elem)
- case Struct_Type:
+ case ^Struct_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.fields = auto_cast clone(r.fields)
- case Union_Type:
+ case ^Union_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.variants = clone(r.variants)
- case Enum_Type:
+ case ^Enum_Type:
r.base_type = clone(r.base_type)
r.fields = clone(r.fields)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
r.elem = clone(r.elem)
r.underlying = clone(r.underlying)
- case Map_Type:
+ case ^Map_Type:
r.key = clone(r.key)
r.value = clone(r.value)
- case Matrix_Type:
+ case ^Matrix_Type:
r.row_count = clone(r.row_count)
r.column_count = clone(r.column_count)
r.elem = clone(r.elem)
+ case ^Relative_Type:
+ r.tag = clone(r.tag)
+ r.type = clone(r.type)
case:
- fmt.panicf("Unhandled node kind: %T", r)
+ fmt.panicf("Unhandled node kind: %v", r)
}
return res
diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin
index d0d17cc9e..b4eaf8140 100644
--- a/core/odin/ast/walk.odin
+++ b/core/odin/ast/walk.odin
@@ -52,71 +52,74 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
}
-
v := v
+ if v == nil || node == nil {
+ return
+ }
+
if v = v->visit(node); v == nil {
return
}
switch n in &node.derived {
- case File:
+ case ^File:
if n.docs != nil {
walk(v, n.docs)
}
walk_stmt_list(v, n.decls[:])
- case Package:
+ case ^Package:
for _, f in n.files {
walk(v, f)
}
- case Comment_Group:
+ case ^Comment_Group:
// empty
- case Bad_Expr:
- case Ident:
- case Implicit:
- case Undef:
- case Basic_Lit:
- case Basic_Directive:
- case Ellipsis:
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Basic_Directive:
+ case ^Ellipsis:
if n.expr != nil {
walk(v, n.expr)
}
- case Proc_Lit:
+ case ^Proc_Lit:
walk(v, n.type)
walk(v, n.body)
walk_expr_list(v, n.where_clauses)
- case Comp_Lit:
+ case ^Comp_Lit:
if n.type != nil {
walk(v, n.type)
}
walk_expr_list(v, n.elems)
- case Tag_Expr:
+ case ^Tag_Expr:
walk(v, n.expr)
- case Unary_Expr:
+ case ^Unary_Expr:
walk(v, n.expr)
- case Binary_Expr:
+ case ^Binary_Expr:
walk(v, n.left)
walk(v, n.right)
- case Paren_Expr:
+ case ^Paren_Expr:
walk(v, n.expr)
- case Selector_Expr:
+ case ^Selector_Expr:
walk(v, n.expr)
walk(v, n.field)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
walk(v, n.field)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
walk(v, n.expr)
walk(v, n.call)
- case Index_Expr:
+ case ^Index_Expr:
walk(v, n.expr)
walk(v, n.index)
- case Matrix_Index_Expr:
+ case ^Matrix_Index_Expr:
walk(v, n.expr)
walk(v, n.row_index)
walk(v, n.column_index)
- case Deref_Expr:
+ case ^Deref_Expr:
walk(v, n.expr)
- case Slice_Expr:
+ case ^Slice_Expr:
walk(v, n.expr)
if n.low != nil {
walk(v, n.low)
@@ -124,57 +127,57 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.high != nil {
walk(v, n.high)
}
- case Call_Expr:
+ case ^Call_Expr:
walk(v, n.expr)
walk_expr_list(v, n.args)
- case Field_Value:
+ case ^Field_Value:
walk(v, n.field)
walk(v, n.value)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
walk(v, n.x)
walk(v, n.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
walk(v, n.expr)
- case Type_Assertion:
+ case ^Type_Assertion:
walk(v, n.expr)
if n.type != nil {
walk(v, n.type)
}
- case Type_Cast:
+ case ^Type_Cast:
walk(v, n.type)
walk(v, n.expr)
- case Auto_Cast:
+ case ^Auto_Cast:
walk(v, n.expr)
- case Inline_Asm_Expr:
+ case ^Inline_Asm_Expr:
walk_expr_list(v, n.param_types)
walk(v, n.return_type)
walk(v, n.constraints_string)
walk(v, n.asm_string)
- case Bad_Stmt:
- case Empty_Stmt:
- case Expr_Stmt:
+ case ^Bad_Stmt:
+ case ^Empty_Stmt:
+ case ^Expr_Stmt:
walk(v, n.expr)
- case Tag_Stmt:
+ case ^Tag_Stmt:
walk(v, n.stmt)
- case Assign_Stmt:
+ case ^Assign_Stmt:
walk_expr_list(v, n.lhs)
walk_expr_list(v, n.rhs)
- case Block_Stmt:
+ case ^Block_Stmt:
if n.label != nil {
walk(v, n.label)
}
walk_stmt_list(v, n.stmts)
- case If_Stmt:
+ case ^If_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -186,17 +189,17 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.else_stmt != nil {
walk(v, n.else_stmt)
}
- case When_Stmt:
+ case ^When_Stmt:
walk(v, n.cond)
walk(v, n.body)
if n.else_stmt != nil {
walk(v, n.else_stmt)
}
- case Return_Stmt:
+ case ^Return_Stmt:
walk_expr_list(v, n.results)
- case Defer_Stmt:
+ case ^Defer_Stmt:
walk(v, n.stmt)
- case For_Stmt:
+ case ^For_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -210,7 +213,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.post)
}
walk(v, n.body)
- case Range_Stmt:
+ case ^Range_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -221,7 +224,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk(v, n.expr)
walk(v, n.body)
- case Inline_Range_Stmt:
+ case ^Inline_Range_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -233,10 +236,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk(v, n.expr)
walk(v, n.body)
- case Case_Clause:
+ case ^Case_Clause:
walk_expr_list(v, n.list)
walk_stmt_list(v, n.body)
- case Switch_Stmt:
+ case ^Switch_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -247,7 +250,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.cond)
}
walk(v, n.body)
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -258,16 +261,16 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.expr)
}
walk(v, n.body)
- case Branch_Stmt:
+ case ^Branch_Stmt:
if n.label != nil {
walk(v, n.label)
}
- case Using_Stmt:
+ case ^Using_Stmt:
walk_expr_list(v, n.list)
- case Bad_Decl:
- case Value_Decl:
+ case ^Bad_Decl:
+ case ^Value_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -280,21 +283,21 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.comment != nil {
walk(v, n.comment)
}
- case Package_Decl:
+ case ^Package_Decl:
if n.docs != nil {
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment)
}
- case Import_Decl:
+ case ^Import_Decl:
if n.docs != nil {
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment)
}
- case Foreign_Block_Decl:
+ case ^Foreign_Block_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -303,7 +306,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.foreign_library)
}
walk(v, n.body)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -313,11 +316,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.comment)
}
- case Proc_Group:
+ case ^Proc_Group:
walk_expr_list(v, n.args)
- case Attribute:
+ case ^Attribute:
walk_expr_list(v, n.elems)
- case Field:
+ case ^Field:
if n.docs != nil {
walk(v, n.docs)
}
@@ -331,31 +334,31 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.comment != nil {
walk(v, n.comment)
}
- case Field_List:
+ case ^Field_List:
for x in n.list {
walk(v, x)
}
- case Typeid_Type:
+ case ^Typeid_Type:
if n.specialization != nil {
walk(v, n.specialization)
}
- case Helper_Type:
+ case ^Helper_Type:
walk(v, n.type)
- case Distinct_Type:
+ case ^Distinct_Type:
walk(v, n.type)
- case Poly_Type:
+ case ^Poly_Type:
walk(v, n.type)
if n.specialization != nil {
walk(v, n.specialization)
}
- case Proc_Type:
+ case ^Proc_Type:
walk(v, n.params)
walk(v, n.results)
- case Pointer_Type:
+ case ^Pointer_Type:
walk(v, n.elem)
- case Multi_Pointer_Type:
+ case ^Multi_Pointer_Type:
walk(v, n.elem)
- case Array_Type:
+ case ^Array_Type:
if n.tag != nil {
walk(v, n.tag)
}
@@ -363,12 +366,12 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.len)
}
walk(v, n.elem)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
if n.tag != nil {
walk(v, n.tag)
}
walk(v, n.elem)
- case Struct_Type:
+ case ^Struct_Type:
if n.poly_params != nil {
walk(v, n.poly_params)
}
@@ -377,7 +380,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk_expr_list(v, n.where_clauses)
walk(v, n.fields)
- case Union_Type:
+ case ^Union_Type:
if n.poly_params != nil {
walk(v, n.poly_params)
}
@@ -386,23 +389,23 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk_expr_list(v, n.where_clauses)
walk_expr_list(v, n.variants)
- case Enum_Type:
+ case ^Enum_Type:
if n.base_type != nil {
walk(v, n.base_type)
}
walk_expr_list(v, n.fields)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
walk(v, n.elem)
if n.underlying != nil {
walk(v, n.underlying)
}
- case Map_Type:
+ case ^Map_Type:
walk(v, n.key)
walk(v, n.value)
- case Relative_Type:
+ case ^Relative_Type:
walk(v, n.tag)
walk(v, n.type)
- case Matrix_Type:
+ case ^Matrix_Type:
walk(v, n.row_count)
walk(v, n.column_count)
walk(v, n.elem)
diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin
index 8fa9f453c..62682004d 100644
--- a/core/odin/doc-format/doc_format.odin
+++ b/core/odin/doc-format/doc_format.odin
@@ -11,7 +11,7 @@ String :: distinct Array(byte)
Version_Type_Major :: 0
Version_Type_Minor :: 2
-Version_Type_Patch :: 0
+Version_Type_Patch :: 4
Version_Type :: struct {
major, minor, patch: u8,
@@ -77,9 +77,15 @@ Pkg :: struct {
flags: Pkg_Flags,
docs: String,
files: Array(File_Index),
- entities: Array(Entity_Index),
+ entries: Array(Scope_Entry),
}
+Scope_Entry :: struct {
+ name: String,
+ entity: Entity_Index,
+}
+
+
Entity_Kind :: enum u32le {
Invalid = 0,
Constant = 1,
@@ -89,6 +95,7 @@ Entity_Kind :: enum u32le {
Proc_Group = 5,
Import_Name = 6,
Library_Name = 7,
+ Builtin = 8,
}
Entity_Flag :: enum u32le {
@@ -105,8 +112,13 @@ Entity_Flag :: enum u32le {
Type_Alias = 20,
+ Builtin_Pkg_Builtin = 30,
+ Builtin_Pkg_Intrinsics = 31,
+
Var_Thread_Local = 40,
Var_Static = 41,
+
+ Private = 50,
}
Entity_Flags :: distinct bit_set[Entity_Flag; u64le]
@@ -122,6 +134,10 @@ Entity :: struct {
_: u32le, // reserved for init
comment: String,
docs: String,
+ // May be used by (Struct fields and procedure fields):
+ // .Variable
+ // .Constant
+ field_group_index: i32le,
// May used by:
// .Variable
@@ -242,6 +258,8 @@ Type :: struct {
polymorphic_params: Type_Index,
// Used By: .Struct, .Union
where_clauses: Array(String),
+ // Used By: .Struct
+ tags: Array(String),
}
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le]
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 7660005e0..52ecb4781 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -47,6 +47,8 @@ Parser :: struct {
fix_count: int,
fix_prev_pos: tokenizer.Pos,
+
+ peeking: bool,
}
MAX_FIX_COUNT :: 10
@@ -181,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
pd.name = pkg_name.text
pd.comment = p.line_comment
p.file.pkg_decl = pd
+ p.file.docs = docs
expect_semicolon(p, pd)
@@ -193,10 +196,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
for p.curr_tok.kind != .EOF {
stmt := parse_stmt(p)
if stmt != nil {
- if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+ if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
append(&p.file.decls, stmt)
- if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
- if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+ if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+ if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
error(p, stmt.pos, "procedure literal evaluated but not used")
}
}
@@ -209,7 +212,12 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0) -> (ok: bool) {
prev_parser := p^
- defer p^ = prev_parser
+ p.peeking = true
+
+ defer {
+ p^ = prev_parser
+ p.peeking = false
+ }
p.tok.err = nil
for i := 0; i <= lookahead; i += 1 {
@@ -222,7 +230,12 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
prev_parser := p^
- defer p^ = prev_parser
+ p.peeking = true
+
+ defer {
+ p^ = prev_parser
+ p.peeking = false
+ }
p.tok.err = nil
for i := 0; i <= lookahead; i += 1 {
@@ -305,7 +318,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro
append(&list, comment)
}
- if len(list) > 0 {
+ if len(list) > 0 && !p.peeking {
comments = ast.new(ast.Comment_Group, list[0].pos, end_pos(list[len(list)-1]))
comments.list = list[:]
append(&p.file.comments, comments)
@@ -416,9 +429,21 @@ expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
str := tokenizer.token_to_string(token)
error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str)
}
- return expect_token(p, .Close_Brace)
+ expect_brace := expect_token(p, .Close_Brace)
+
+ if expect_brace.kind != .Close_Brace {
+ for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) {
+ advance_token(p)
+ }
+ return p.curr_tok
+ }
+
+ return expect_brace
}
+is_non_inserted_semicolon :: proc(tok: tokenizer.Token) -> bool {
+ return tok.kind == .Semicolon && tok.text != "\n"
+}
is_blank_ident :: proc{
is_blank_ident_string,
@@ -435,7 +460,7 @@ is_blank_ident_token :: proc(tok: tokenizer.Token) -> bool {
return false
}
is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
- if ident, ok := node.derived.(ast.Ident); ok {
+ if ident, ok := node.derived.(^ast.Ident); ok {
return is_blank_ident(ident.name)
}
return true
@@ -478,34 +503,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return true
}
- switch n in node.derived {
- case ast.Empty_Stmt, ast.Block_Stmt:
+ #partial switch n in node.derived {
+ case ^ast.Empty_Stmt, ^ast.Block_Stmt:
return true
- case ast.If_Stmt, ast.When_Stmt,
- ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt,
- ast.Switch_Stmt, ast.Type_Switch_Stmt:
+ case ^ast.If_Stmt, ^ast.When_Stmt,
+ ^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt,
+ ^ast.Switch_Stmt, ^ast.Type_Switch_Stmt:
return true
- case ast.Helper_Type:
+ case ^ast.Helper_Type:
return is_semicolon_optional_for_node(p, n.type)
- case ast.Distinct_Type:
+ case ^ast.Distinct_Type:
return is_semicolon_optional_for_node(p, n.type)
- case ast.Pointer_Type:
+ case ^ast.Pointer_Type:
return is_semicolon_optional_for_node(p, n.elem)
- case ast.Struct_Type, ast.Union_Type, ast.Enum_Type:
+ case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type:
// Require semicolon within a procedure body
return p.curr_proc == nil
- case ast.Proc_Lit:
+ case ^ast.Proc_Lit:
return true
- case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl:
+ case ^ast.Package_Decl, ^ast.Import_Decl, ^ast.Foreign_Import_Decl:
return true
- case ast.Foreign_Block_Decl:
+ case ^ast.Foreign_Block_Decl:
return is_semicolon_optional_for_node(p, n.body)
- case ast.Value_Decl:
+ case ^ast.Value_Decl:
if n.is_mutable {
return false
}
@@ -617,10 +642,10 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt {
p.curr_tok.kind != .EOF {
stmt := parse_stmt(p)
if stmt != nil {
- if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+ if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
append(&list, stmt)
- if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
- if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+ if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+ if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
error(p, stmt.pos, "procedure literal evaluated but not used")
}
}
@@ -698,7 +723,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.
if stmt == nil {
return nil
}
- if es, ok := stmt.derived.(ast.Expr_Stmt); ok {
+ if es, ok := stmt.derived.(^ast.Expr_Stmt); ok {
return es.expr
}
error(p, stmt.pos, "expected %s, found a simple statement", kind)
@@ -840,7 +865,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_tok.kind != .Semicolon {
cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
- if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+ if as, ok := cond.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
is_range = true
}
}
@@ -876,12 +901,13 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token")
}
} else {
+ allow_token(p, .Semicolon)
body = parse_body(p)
}
if is_range {
- assign_stmt := cond.derived.(ast.Assign_Stmt)
+ assign_stmt := cond.derived.(^ast.Assign_Stmt)
vals := assign_stmt.lhs[:]
rhs: ^ast.Expr
@@ -962,7 +988,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
tag = as
} else {
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
- if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+ if as, ok := tag.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
is_type_switch = true
} else if parse_control_statement_semicolon_separator(p) {
init = tag
@@ -1049,14 +1075,14 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
skip_possible_newline(p)
decl := parse_stmt(p)
- switch d in &decl.derived {
- case ast.Value_Decl:
+ #partial switch d in decl.derived_stmt {
+ case ^ast.Value_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
- case ast.Foreign_Block_Decl:
+ case ^ast.Foreign_Block_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
- case ast.Foreign_Import_Decl:
+ case ^ast.Foreign_Import_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
case:
@@ -1070,11 +1096,11 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
decl := parse_stmt(p)
- switch in decl.derived {
- case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl:
+ #partial switch in decl.derived_stmt {
+ case ^ast.Empty_Stmt, ^ast.Bad_Stmt, ^ast.Bad_Decl:
// Ignore
return nil
- case ast.When_Stmt, ast.Value_Decl:
+ case ^ast.When_Stmt, ^ast.Value_Decl:
return decl
}
@@ -1278,13 +1304,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
case .Defer:
tok := advance_token(p)
stmt := parse_stmt(p)
- switch s in stmt.derived {
- case ast.Empty_Stmt:
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Empty_Stmt:
error(p, s.pos, "empty statement after defer (e.g. ';')")
- case ast.Defer_Stmt:
+ case ^ast.Defer_Stmt:
error(p, s.pos, "you cannot defer a defer statement")
stmt = s.stmt
- case ast.Return_Stmt:
+ case ^ast.Return_Stmt:
error(p, s.pos, "you cannot defer a return statement")
}
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
@@ -1299,7 +1325,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
results: [dynamic]^ast.Expr
- for p.curr_tok.kind != .Semicolon {
+ for p.curr_tok.kind != .Semicolon && p.curr_tok.kind != .Close_Brace {
result := parse_expr(p, false)
append(&results, result)
if p.curr_tok.kind != .Comma ||
@@ -1356,8 +1382,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
expect_token_after(p, .Colon, "identifier list")
decl := parse_value_decl(p, list, docs)
if decl != nil {
- switch d in &decl.derived {
- case ast.Value_Decl:
+ #partial switch d in decl.derived_stmt {
+ case ^ast.Value_Decl:
d.is_using = true
return decl
}
@@ -1388,9 +1414,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return stmt
case "partial":
stmt := parse_stmt(p)
- switch s in &stmt.derived {
- case ast.Switch_Stmt: s.partial = true
- case ast.Type_Switch_Stmt: s.partial = true
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Switch_Stmt: s.partial = true
+ case ^ast.Type_Switch_Stmt: s.partial = true
case: error(p, stmt.pos, "#partial can only be applied to a switch statement")
}
return stmt
@@ -1535,11 +1561,11 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt {
}
convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
- switch s in stmt.derived {
- case ast.Block_Stmt:
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Block_Stmt:
error(p, stmt.pos, "expected a normal statement rather than a block statement")
return stmt
- case ast.Empty_Stmt:
+ case ^ast.Empty_Stmt:
error(p, stmt.pos, "expected a non-empty statement")
}
@@ -1616,10 +1642,10 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
id: ^ast.Expr = ident.expr
- switch n in ident.expr.derived {
- case ast.Ident:
- case ast.Bad_Expr:
- case ast.Poly_Type:
+ #partial switch n in ident.expr.derived_expr {
+ case ^ast.Ident:
+ case ^ast.Bad_Expr:
+ case ^ast.Poly_Type:
if allow_poly_names {
if n.specialization == nil {
break
@@ -1781,21 +1807,21 @@ check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool {
return false
}
- _, first_is_polymorphic := names[0].derived.(ast.Poly_Type)
+ _, first_is_polymorphic := names[0].derived.(^ast.Poly_Type)
any_polymorphic_names := first_is_polymorphic
for i := 1; i < len(names); i += 1 {
name := names[i]
if first_is_polymorphic {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
any_polymorphic_names = true
} else {
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
return any_polymorphic_names
}
} else {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
any_polymorphic_names = true
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
return any_polymorphic_names
@@ -1860,7 +1886,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
if type == nil {
return false
}
- _, ok := type.derived.(ast.Ellipsis)
+ _, ok := type.derived.(^ast.Ellipsis)
return ok
}
@@ -1878,7 +1904,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
type = parse_var_type(p, allowed_flags)
tt := ast.unparen_expr(type)
if is_signature && !any_polymorphic_names {
- if ti, ok := tt.derived.(ast.Typeid_Type); ok && ti.specialization != nil {
+ if ti, ok := tt.derived.(^ast.Typeid_Type); ok && ti.specialization != nil {
error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names")
}
}
@@ -1954,7 +1980,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
p.curr_tok.kind != .EOF {
prefix_flags := parse_field_prefixes(p)
param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis})
- if _, ok := param.derived.(ast.Ellipsis); ok {
+ if _, ok := param.derived.(^ast.Ellipsis); ok {
if seen_ellipsis {
error(p, param.pos, "extra variadic parameter after ellipsis")
}
@@ -1981,8 +2007,8 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
names := make([]^ast.Expr, 1)
names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok))
- switch ident in &names[0].derived {
- case ast.Ident:
+ #partial switch ident in names[0].derived_expr {
+ case ^ast.Ident:
ident.name = tok.text
case:
unreachable()
@@ -2112,12 +2138,12 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
loop: for param in params.list {
if param.type != nil {
- if _, ok := param.type.derived.(ast.Poly_Type); ok {
+ if _, ok := param.type.derived.(^ast.Poly_Type); ok {
is_generic = true
break loop
}
for name in param.names {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
is_generic = true
break loop
}
@@ -2154,13 +2180,13 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
}
}
- switch e in &ast.unparen_expr(expr).derived {
- case ast.Proc_Lit:
+ #partial switch e in ast.unparen_expr(expr).derived_expr {
+ case ^ast.Proc_Lit:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
}
e.inlining = pi
- case ast.Call_Expr:
+ case ^ast.Call_Expr:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
}
@@ -2251,22 +2277,40 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
bd.name = name.text
original_type := parse_type(p)
type := ast.unparen_expr(original_type)
- switch t in &type.derived {
- case ast.Array_Type: t.tag = bd
- case ast.Dynamic_Array_Type: t.tag = bd
+ #partial switch t in type.derived_expr {
+ case ^ast.Array_Type: t.tag = bd
+ case ^ast.Dynamic_Array_Type: t.tag = bd
case:
error(p, original_type.pos, "expected an array type after #%s", name.text)
}
return original_type
case "partial":
+ tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
+ tag.tok = tok
+ tag.name = name.text
+ original_expr := parse_expr(p, lhs)
+ expr := ast.unparen_expr(original_expr)
+ #partial switch t in expr.derived_expr {
+ case ^ast.Comp_Lit:
+ t.tag = tag
+ case ^ast.Array_Type:
+ t.tag = tag
+ error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
+ case:
+ error(p, tok.pos, "expected a compound literal after #%s", name.text)
+
+ }
+ return original_expr
+
+ case "sparse":
tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
tag.tok = tok
tag.name = name.text
original_type := parse_type(p)
type := ast.unparen_expr(original_type)
- switch t in &type.derived {
- case ast.Array_Type:
+ #partial switch t in type.derived_expr {
+ case ^ast.Array_Type:
t.tag = tag
case:
error(p, tok.pos, "expected an enumerated array type after #%s", name.text)
@@ -2306,7 +2350,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return rt
case "force_inline", "force_no_inline":
- return parse_inlining_operand(p, lhs, tok)
+ return parse_inlining_operand(p, lhs, name)
case:
expr := parse_expr(p, lhs)
te := ast.new(ast.Tag_Expr, tok.pos, expr.pos)
@@ -2587,7 +2631,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
- is_maybe: bool
+ is_maybe: bool
+ is_no_nil: bool
+ is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2614,12 +2660,39 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_maybe = true
+ case "no_nil":
+ if is_no_nil {
+ error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+ }
+ is_no_nil = true
+ case "shared_nil":
+ if is_shared_nil {
+ error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+ }
+ is_shared_nil = true
case:
error(p, tag.pos, "invalid union tag '#%s", tag.text)
}
}
p.expr_level = prev_level
+ if is_no_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
+ }
+ if is_no_nil && is_shared_nil {
+ error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
+ }
+ if is_shared_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
+ }
+
+ union_kind := ast.Union_Type_Kind.Normal
+ switch {
+ case is_maybe: union_kind = .maybe
+ case is_no_nil: union_kind = .no_nil
+ case is_shared_nil: union_kind = .shared_nil
+ }
+
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2640,7 +2713,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
variants: [dynamic]^ast.Expr
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
type := parse_type(p)
- if _, ok := type.derived.(ast.Bad_Expr); !ok {
+ if _, ok := type.derived.(^ast.Bad_Expr); !ok {
append(&variants, type)
}
if !allow_token(p, .Comma) {
@@ -2650,13 +2723,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
+
+
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close))
ut.poly_params = poly_params
ut.variants = variants[:]
ut.align = align
ut.where_token = where_token
ut.where_clauses = where_clauses
- ut.is_maybe = is_maybe
+ ut.kind = union_kind
return ut
@@ -2814,18 +2889,19 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
if val == nil {
return false
}
- switch _ in val.derived {
- case ast.Bad_Expr,
- ast.Ident,
- ast.Selector_Expr,
- ast.Array_Type,
- ast.Struct_Type,
- ast.Union_Type,
- ast.Enum_Type,
- ast.Dynamic_Array_Type,
- ast.Map_Type,
- ast.Bit_Set_Type,
- ast.Call_Expr:
+ #partial switch _ in val.derived_expr {
+ case ^ast.Bad_Expr,
+ ^ast.Ident,
+ ^ast.Selector_Expr,
+ ^ast.Array_Type,
+ ^ast.Struct_Type,
+ ^ast.Union_Type,
+ ^ast.Enum_Type,
+ ^ast.Dynamic_Array_Type,
+ ^ast.Map_Type,
+ ^ast.Bit_Set_Type,
+ ^ast.Matrix_Type,
+ ^ast.Call_Expr:
return true
}
return false
@@ -2947,7 +3023,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
ce.close = close.pos
o := ast.unparen_expr(operand)
- if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
+ if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
sce.expr = o
sce.call = ce
@@ -3377,13 +3453,13 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
stmt := parse_stmt(p)
if stmt != nil {
- switch n in &stmt.derived {
- case ast.Block_Stmt: n.label = label
- case ast.If_Stmt: n.label = label
- case ast.For_Stmt: n.label = label
- case ast.Switch_Stmt: n.label = label
- case ast.Type_Switch_Stmt: n.label = label
- case ast.Range_Stmt: n.label = label
+ #partial switch n in stmt.derived_stmt {
+ case ^ast.Block_Stmt: n.label = label
+ case ^ast.If_Stmt: n.label = label
+ case ^ast.For_Stmt: n.label = label
+ case ^ast.Switch_Stmt: n.label = label
+ case ^ast.Type_Switch_Stmt: n.label = label
+ case ^ast.Range_Stmt: n.label = label
}
}
diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin
index abda44fa2..63a3b543d 100644
--- a/core/odin/printer/printer.odin
+++ b/core/odin/printer/printer.odin
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
fix_lines(p)
- builder := strings.make_builder(0, mem.megabytes(5), p.allocator)
+ builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator)
last_line := 0
diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin
index 023583bde..66166aa81 100644
--- a/core/odin/printer/visit.odin
+++ b/core/odin/printer/visit.odin
@@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
return 0
} else {
- builder := strings.make_builder(context.temp_allocator)
+ builder := strings.builder_make(context.temp_allocator)
c_len := len(comment.text)
trim_space := true
@@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
continue
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
append(&multilines, strings.to_string(builder))
- builder = strings.make_builder(context.temp_allocator)
+ builder = strings.builder_make(context.temp_allocator)
trim_space = true
i += 1
case c == '\n':
append(&multilines, strings.to_string(builder))
- builder = strings.make_builder(context.temp_allocator)
+ builder = strings.builder_make(context.temp_allocator)
trim_space = true
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
strings.write_string(&builder, "/*")
@@ -342,16 +342,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
return
}
- switch v in &decl.derived {
- case Expr_Stmt:
+ #partial switch v in decl.derived_stmt {
+ case ^Expr_Stmt:
move_line(p, decl.pos)
visit_expr(p, v.expr)
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case When_Stmt:
+ case ^When_Stmt:
visit_stmt(p, cast(^Stmt)decl)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -370,7 +370,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for path in v.fullpaths {
push_ident_token(p, path, 0)
}
- case Foreign_Block_Decl:
+ case ^Foreign_Block_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -383,7 +383,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
visit_expr(p, v.foreign_library)
visit_stmt(p, v.body)
- case Import_Decl:
+ case ^Import_Decl:
move_line(p, decl.pos)
if v.name.text != "" {
@@ -395,7 +395,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
push_ident_token(p, v.fullpath, 1)
}
- case Value_Decl:
+ case ^Value_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -446,10 +446,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
add_semicolon := true
for value in v.values {
- switch a in value.derived {
- case Union_Type, Enum_Type, Struct_Type:
+ #partial switch a in value.derived {
+ case ^Union_Type, ^Enum_Type, ^Struct_Type:
add_semicolon = false || called_in_stmt
- case Proc_Lit:
+ case ^Proc_Lit:
add_semicolon = false
}
}
@@ -516,23 +516,34 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
return
}
- switch v in stmt.derived {
- case Import_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Value_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Foreign_Import_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Foreign_Block_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- }
- switch v in stmt.derived {
- case Using_Stmt:
+ switch v in stmt.derived_stmt {
+ case ^Bad_Stmt:
+ case ^Bad_Decl:
+ case ^Package_Decl:
+
+ case ^Empty_Stmt:
+ push_generic_token(p, .Semicolon, 0)
+ case ^Tag_Stmt:
+ push_generic_token(p, .Hash, 1)
+ push_generic_token(p, v.op.kind, 1, v.op.text)
+ visit_stmt(p, v.stmt)
+
+
+ case ^Import_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Value_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Foreign_Import_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Foreign_Block_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+
+ case ^Using_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Using, 1)
@@ -542,7 +553,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Block_Stmt:
+ case ^Block_Stmt:
move_line(p, v.pos)
if v.pos.line == v.end.line {
@@ -572,7 +583,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_end_brace(p, v.end)
}
}
- case If_Stmt:
+ case ^If_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -595,7 +606,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
uses_do := false
- if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do {
+ if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
uses_do = true
}
@@ -626,7 +637,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
- case Switch_Stmt:
+ case ^Switch_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -654,7 +665,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.cond)
visit_stmt(p, v.body)
- case Case_Clause:
+ case ^Case_Clause:
move_line(p, v.pos)
if !p.config.indent_cases {
@@ -678,7 +689,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if !p.config.indent_cases {
indent(p)
}
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Switch_Stmt})
@@ -696,7 +707,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.tag)
visit_stmt(p, v.body)
- case Assign_Stmt:
+ case ^Assign_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Assign})
@@ -710,13 +721,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Expr_Stmt:
+ case ^Expr_Stmt:
move_line(p, v.pos)
visit_expr(p, v.expr)
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case For_Stmt:
+ case ^For_Stmt:
// this should be simplified
move_line(p, v.pos)
@@ -753,7 +764,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.body)
- case Inline_Range_Stmt:
+ case ^Inline_Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -779,7 +790,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
- case Range_Stmt:
+ case ^Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -805,7 +816,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
- case Return_Stmt:
+ case ^Return_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Return, 1)
@@ -817,7 +828,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Defer_Stmt:
+ case ^Defer_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Defer, 0)
@@ -826,7 +837,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case When_Stmt:
+ case ^When_Stmt:
move_line(p, v.pos)
push_generic_token(p, .When, 1)
visit_expr(p, v.cond)
@@ -846,7 +857,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
- case Branch_Stmt:
+ case ^Branch_Stmt:
move_line(p, v.pos)
push_generic_token(p, v.tok.kind, 0)
@@ -918,8 +929,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
set_source_position(p, expr.pos)
- switch v in expr.derived {
- case Inline_Asm_Expr:
+ switch v in expr.derived_expr {
+ case ^Bad_Expr:
+
+ case ^Tag_Expr:
+ push_generic_token(p, .Hash, 1)
+ push_generic_token(p, v.op.kind, 1, v.op.text)
+ visit_expr(p, v.expr)
+
+ case ^Inline_Asm_Expr:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
push_generic_token(p, .Open_Paren, 1)
@@ -936,42 +954,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Comma, 0)
visit_expr(p, v.constraints_string)
push_generic_token(p, .Close_Brace, 0)
- case Undef:
+ case ^Undef:
push_generic_token(p, .Undef, 1)
- case Auto_Cast:
+ case ^Auto_Cast:
push_generic_token(p, v.op.kind, 1)
visit_expr(p, v.expr)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.token.kind, 1)
visit_expr(p, v.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.token.kind, 1)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
visit_expr(p, v.call.expr)
push_generic_token(p, .Open_Paren, 1)
visit_exprs(p, v.call.args, {.Add_Comma})
push_generic_token(p, .Close_Paren, 0)
- case Ellipsis:
+ case ^Ellipsis:
push_generic_token(p, .Ellipsis, 1)
visit_expr(p, v.expr)
- case Relative_Type:
+ case ^Relative_Type:
visit_expr(p, v.tag)
visit_expr(p, v.type)
- case Slice_Expr:
+ case ^Slice_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.low)
@@ -981,37 +999,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.high)
}
push_generic_token(p, .Close_Bracket, 0)
- case Ident:
+ case ^Ident:
if .Enforce_Poly_Names in options {
push_generic_token(p, .Dollar, 1)
push_ident_token(p, v.name, 0)
} else {
push_ident_token(p, v.name, 1)
}
- case Deref_Expr:
+ case ^Deref_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
- case Type_Cast:
+ case ^Type_Cast:
push_generic_token(p, v.tok.kind, 1)
push_generic_token(p, .Open_Paren, 0)
visit_expr(p, v.type)
push_generic_token(p, .Close_Paren, 0)
merge_next_token(p)
visit_expr(p, v.expr)
- case Basic_Directive:
+ case ^Basic_Directive:
push_generic_token(p, v.tok.kind, 1)
push_ident_token(p, v.name, 0)
- case Distinct_Type:
+ case ^Distinct_Type:
push_generic_token(p, .Distinct, 1)
visit_expr(p, v.type)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Dynamic, 0)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
push_generic_token(p, .Bit_Set, 1)
push_generic_token(p, .Open_Bracket, 0)
@@ -1023,13 +1041,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
push_generic_token(p, .Close_Bracket, 0)
- case Union_Type:
+ case ^Union_Type:
push_generic_token(p, .Union, 1)
push_poly_params(p, v.poly_params)
- if v.is_maybe {
- push_ident_token(p, "#maybe", 1)
+ switch v.kind {
+ case .Normal:
+ case .maybe: push_ident_token(p, "#maybe", 1)
+ case .no_nil: push_ident_token(p, "#no_nil", 1)
+ case .shared_nil: push_ident_token(p, "#shared_nil", 1)
}
push_where_clauses(p, v.where_clauses)
@@ -1045,7 +1066,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
visit_end_brace(p, v.end)
}
- case Enum_Type:
+ case ^Enum_Type:
push_generic_token(p, .Enum, 1)
hint_current_line(p, {.Enum})
@@ -1068,7 +1089,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
- case Struct_Type:
+ case ^Struct_Type:
push_generic_token(p, .Struct, 1)
hint_current_line(p, {.Struct})
@@ -1103,7 +1124,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
- case Proc_Lit:
+ case ^Proc_Lit:
switch v.inlining {
case .None:
case .Inline:
@@ -1112,7 +1133,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_ident_token(p, "#force_no_inline", 0)
}
- visit_proc_type(p, v.type^, true)
+ visit_proc_type(p, v.type, true)
push_where_clauses(p, v.where_clauses)
@@ -1122,16 +1143,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} else {
push_generic_token(p, .Undef, 1)
}
- case Proc_Type:
+ case ^Proc_Type:
visit_proc_type(p, v)
- case Basic_Lit:
+ case ^Basic_Lit:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
- case Binary_Expr:
+ case ^Binary_Expr:
visit_binary_expr(p, v)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
push_generic_token(p, .Period, 1)
push_ident_token(p, v.field.name, 0)
- case Call_Expr:
+ case ^Call_Expr:
visit_expr(p, v.expr)
push_format_token(p,
@@ -1146,27 +1167,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
push_generic_token(p, .Close_Paren, 0)
- case Typeid_Type:
+ case ^Typeid_Type:
push_generic_token(p, .Typeid, 1)
if v.specialization != nil {
push_generic_token(p, .Quo, 0)
visit_expr(p, v.specialization)
}
- case Selector_Expr:
+ case ^Selector_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
visit_expr(p, v.field)
- case Paren_Expr:
+ case ^Paren_Expr:
push_generic_token(p, .Open_Paren, 1)
visit_expr(p, v.expr)
push_generic_token(p, .Close_Paren, 0)
- case Index_Expr:
+ case ^Index_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.index)
push_generic_token(p, .Close_Bracket, 0)
- case Proc_Group:
+ case ^Matrix_Index_Expr:
+ visit_expr(p, v.expr)
+ push_generic_token(p, .Open_Bracket, 0)
+ visit_expr(p, v.row_index)
+ push_generic_token(p, .Comma, 0)
+ visit_expr(p, v.column_index)
+ push_generic_token(p, .Close_Bracket, 0)
+ case ^Proc_Group:
push_generic_token(p, v.tok.kind, 1)
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1181,7 +1209,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
- case Comp_Lit:
+ case ^Comp_Lit:
if v.type != nil {
visit_expr(p, v.type)
}
@@ -1198,18 +1226,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
- case Unary_Expr:
+ case ^Unary_Expr:
push_generic_token(p, v.op.kind, 1)
merge_next_token(p)
visit_expr(p, v.expr)
- case Field_Value:
+ case ^Field_Value:
visit_expr(p, v.field)
push_generic_token(p, .Eq, 1)
visit_expr(p, v.value)
- case Type_Assertion:
+ case ^Type_Assertion:
visit_expr(p, v.expr)
- if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" {
+ if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
push_generic_token(p, .Period, 0)
visit_expr(p, v.type)
} else {
@@ -1219,13 +1247,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Paren, 0)
}
- case Pointer_Type:
+ case ^Pointer_Type:
push_generic_token(p, .Pointer, 1)
merge_next_token(p)
visit_expr(p, v.elem)
- case Implicit:
+ case ^Implicit:
push_generic_token(p, v.tok.kind, 1)
- case Poly_Type:
+ case ^Poly_Type:
push_generic_token(p, .Dollar, 1)
merge_next_token(p)
visit_expr(p, v.type)
@@ -1235,22 +1263,35 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
merge_next_token(p)
visit_expr(p, v.specialization)
}
- case Array_Type:
+ case ^Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
visit_expr(p, v.len)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
- case Map_Type:
+ case ^Map_Type:
push_generic_token(p, .Map, 1)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.key)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.value)
- case Helper_Type:
+ case ^Helper_Type:
visit_expr(p, v.type)
+ case ^Multi_Pointer_Type:
+ push_generic_token(p, .Open_Bracket, 1)
+ push_generic_token(p, .Pointer, 0)
+ push_generic_token(p, .Close_Bracket, 0)
+ visit_expr(p, v.elem)
+ case ^Matrix_Type:
+ push_generic_token(p, .Matrix, 1)
+ push_generic_token(p, .Open_Bracket, 0)
+ visit_expr(p, v.row_count)
+ push_generic_token(p, .Comma, 0)
+ visit_expr(p, v.column_count)
+ push_generic_token(p, .Close_Bracket, 0)
+ visit_expr(p, v.elem)
case:
panic(fmt.aprint(expr.derived))
}
@@ -1348,7 +1389,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt
}
}
-visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) {
+visit_proc_type :: proc(p: ^Printer, proc_type: ^ast.Proc_Type, is_proc_lit := false) {
if is_proc_lit {
push_format_token(p, Format_Token {
kind = .Proc,
@@ -1392,7 +1433,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
} else if len(proc_type.results.list) == 1 {
for name in proc_type.results.list[0].names {
- if ident, ok := name.derived.(ast.Ident); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
if ident.name != "_" {
use_parens = true
}
@@ -1410,19 +1451,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
}
}
-visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
+visit_binary_expr :: proc(p: ^Printer, binary: ^ast.Binary_Expr) {
move_line(p, binary.left.pos)
- if v, ok := binary.left.derived.(ast.Binary_Expr); ok {
+ if v, ok := binary.left.derived.(^ast.Binary_Expr); ok {
visit_binary_expr(p, v)
} else {
visit_expr(p, binary.left)
}
either_implicit_selector := false
- if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok {
+ if _, ok := binary.left.derived.(^ast.Implicit_Selector_Expr); ok {
either_implicit_selector = true
- } else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok {
+ } else if _, ok := binary.right.derived.(^ast.Implicit_Selector_Expr); ok {
either_implicit_selector = true
}
@@ -1439,7 +1480,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
move_line(p, binary.right.pos)
- if v, ok := binary.right.derived.(ast.Binary_Expr); ok {
+ if v, ok := binary.right.derived.(^ast.Binary_Expr); ok {
visit_binary_expr(p, v)
} else {
visit_expr(p, binary.right)
@@ -1499,7 +1540,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank :=
named := false
for name in field.names {
- if ident, ok := name.derived.(ast.Ident); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
//for some reason the parser uses _ to mean empty
if ident.name != "_" || !remove_blank {
named = true
diff --git a/core/os/dir_freebsd.odin b/core/os/dir_freebsd.odin
new file mode 100644
index 000000000..c664ffb34
--- /dev/null
+++ b/core/os/dir_freebsd.odin
@@ -0,0 +1,72 @@
+package os
+
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+ dirp: Dir
+ dirp, err = _fdopendir(fd)
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer _closedir(dirp)
+
+ dirpath: string
+ dirpath, err = absolute_path_from_handle(fd)
+
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer delete(dirpath)
+
+ n := n
+ size := n
+ if n <= 0 {
+ n = -1
+ size = 100
+ }
+
+ dfi := make([dynamic]File_Info, 0, size, allocator)
+
+ for {
+ entry: Dirent
+ end_of_stream: bool
+ entry, err, end_of_stream = _readdir(dirp)
+ if err != ERROR_NONE {
+ for fi_ in dfi {
+ file_info_delete(fi_, allocator)
+ }
+ delete(dfi)
+ return
+ } else if end_of_stream {
+ break
+ }
+
+ fi_: File_Info
+ filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+ if filename == "." || filename == ".." {
+ continue
+ }
+
+ fullpath := make([]byte, len(dirpath)+1+len(filename))
+ copy(fullpath, dirpath)
+ copy(fullpath[len(dirpath):], "/")
+ copy(fullpath[len(dirpath)+1:], filename)
+ defer delete(fullpath, context.temp_allocator)
+
+ fi_, err = stat(string(fullpath), allocator)
+ if err != ERROR_NONE {
+ for fi__ in dfi {
+ file_info_delete(fi__, allocator)
+ }
+ delete(dfi)
+ return
+ }
+
+ append(&dfi, fi_)
+ }
+
+ return dfi[:], ERROR_NONE
+}
diff --git a/core/os/dir_openbsd.odin b/core/os/dir_openbsd.odin
new file mode 100644
index 000000000..465fd35ae
--- /dev/null
+++ b/core/os/dir_openbsd.odin
@@ -0,0 +1,71 @@
+package os
+
+import "core:strings"
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+ dirp: Dir
+ dirp, err = _fdopendir(fd)
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer _closedir(dirp)
+
+ // XXX OpenBSD
+ dirpath: string
+ dirpath, err = absolute_path_from_handle(fd)
+
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer delete(dirpath)
+
+ n := n
+ size := n
+ if n <= 0 {
+ n = -1
+ size = 100
+ }
+
+ dfi := make([dynamic]File_Info, 0, size, allocator)
+
+ for {
+ entry: Dirent
+ end_of_stream: bool
+ entry, err, end_of_stream = _readdir(dirp)
+ if err != ERROR_NONE {
+ for fi_ in dfi {
+ file_info_delete(fi_, allocator)
+ }
+ delete(dfi)
+ return
+ } else if end_of_stream {
+ break
+ }
+
+ fi_: File_Info
+ filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+ if filename == "." || filename == ".." {
+ continue
+ }
+
+ fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
+ defer delete(fullpath, context.temp_allocator)
+
+ fi_, err = stat(fullpath, allocator)
+ if err != ERROR_NONE {
+ for fi__ in dfi {
+ file_info_delete(fi__, allocator)
+ }
+ delete(dfi)
+ return
+ }
+
+ append(&dfi, fi_)
+ }
+
+ return dfi[:], ERROR_NONE
+}
diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin
index ff7e53293..89a09d403 100644
--- a/core/os/dir_windows.odin
+++ b/core/os/dir_windows.odin
@@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
return
}
- path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
+ path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
+ defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin
index 74981bc6e..6e14127ed 100644
--- a/core/os/env_windows.odin
+++ b/core/os/env_windows.odin
@@ -11,24 +11,24 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return
}
wkey := win32.utf8_to_wstring(key)
- b := make([dynamic]u16, 100, context.temp_allocator)
- for {
- n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
- if n == 0 {
- err := win32.GetLastError()
- if err == u32(ERROR_ENVVAR_NOT_FOUND) {
- return "", false
- }
+ n := win32.GetEnvironmentVariableW(wkey, nil, 0)
+ if n == 0 {
+ err := win32.GetLastError()
+ if err == u32(ERROR_ENVVAR_NOT_FOUND) {
+ return "", false
}
-
- if n <= u32(len(b)) {
- value = win32.utf16_to_utf8(b[:n], allocator)
- found = true
- return
- }
-
- resize(&b, len(b)*2)
}
+ b := make([dynamic]u16, n, context.temp_allocator)
+ n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
+ if n == 0 {
+ err := win32.GetLastError()
+ if err == u32(ERROR_ENVVAR_NOT_FOUND) {
+ return "", false
+ }
+ }
+ value, _ = win32.utf16_to_utf8(b[:n], allocator)
+ found = true
+ return
}
@@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string {
if i <= from {
break
}
- append(&r, win32.utf16_to_utf8(envs[from:i], allocator))
+ append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
from = i + 1
}
}
diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin
index 4d740db7c..8019b0440 100644
--- a/core/os/file_windows.odin
+++ b/core/os/file_windows.odin
@@ -2,6 +2,7 @@ package os
import win32 "core:sys/windows"
import "core:intrinsics"
+import "core:unicode/utf16"
is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\'
@@ -19,13 +20,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
}
+ if mode&O_CREATE != 0 {
+ access |= win32.FILE_GENERIC_WRITE
+ }
if mode&O_APPEND != 0 {
access &~= win32.FILE_GENERIC_WRITE
access |= win32.FILE_APPEND_DATA
}
- if mode&O_CREATE != 0 {
- access |= win32.FILE_GENERIC_WRITE
- }
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
sa: ^win32.SECURITY_ATTRIBUTES = nil
@@ -96,26 +97,89 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return int(total_write), ERROR_NONE
}
+@(private="file")
+read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
+ if len(b) == 0 {
+ return 0, 0
+ }
+
+ BUF_SIZE :: 386
+ buf16: [BUF_SIZE]u16
+ buf8: [4*BUF_SIZE]u8
+
+ for n < len(b) && err == 0 {
+ min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
+ max_read := u32(min(BUF_SIZE, min_read))
+ if max_read == 0 {
+ break
+ }
+
+ single_read_length: u32
+ ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
+ if !ok {
+ err = Errno(win32.GetLastError())
+ }
+
+ buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
+ src := buf8[:buf8_len]
+
+ ctrl_z := false
+ for i := 0; i < len(src) && n+i < len(b); i += 1 {
+ x := src[i]
+ if x == 0x1a { // ctrl-z
+ ctrl_z = true
+ break
+ }
+ b[n] = x
+ n += 1
+ }
+ if ctrl_z || single_read_length < max_read {
+ break
+ }
+
+ // NOTE(bill): if the last two values were a newline, then it is expected that
+ // this is the end of the input
+ if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
+ break
+ }
+
+ }
+
+ return
+}
+
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
+
+ handle := win32.HANDLE(fd)
+
+ m: u32
+ is_console := win32.GetConsoleMode(handle, &m)
single_read_length: win32.DWORD
- total_read: i64
- length := i64(len(data))
+ total_read: int
+ length := len(data)
- for total_read < length {
- remaining := length - total_read
- to_read := min(win32.DWORD(remaining), MAX_RW)
+ to_read := min(win32.DWORD(length), MAX_RW)
- e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil)
- if single_read_length <= 0 || !e {
- err := Errno(win32.GetLastError())
+ e: win32.BOOL
+ if is_console {
+ n, err := read_console(handle, data[total_read:][:to_read])
+ total_read += n
+ if err != 0 {
return int(total_read), err
}
- total_read += i64(single_read_length)
+ } else {
+ e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
}
+ if single_read_length <= 0 || !e {
+ err := Errno(win32.GetLastError())
+ return int(total_read), err
+ }
+ total_read += int(single_read_length)
+
return int(total_read), ERROR_NONE
}
@@ -172,6 +236,8 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
Offset = u32(offset),
}
+ // TODO(bill): Determine the correct behaviour for consoles
+
h := win32.HANDLE(fd)
done: win32.DWORD
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
@@ -254,9 +320,6 @@ stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h))
- when size_of(uintptr) == 8 {
- win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0)
- }
return Handle(fd)
}
@@ -302,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
win32.ReleaseSRWLockExclusive(&cwd_lock)
- return win32.utf16_to_utf8(dir_buf_wstr, allocator)
+ return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
}
set_current_directory :: proc(path: string) -> (err: Errno) {
@@ -321,20 +384,33 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
-change_directory :: proc(path: string) -> Errno {
+change_directory :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- return Errno(win32.SetCurrentDirectoryW(wpath))
+
+ if !win32.SetCurrentDirectoryW(wpath) {
+ err = Errno(win32.GetLastError())
+ }
+ return
}
-make_directory :: proc(path: string, mode: u32) -> Errno {
+make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
+ // Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- return Errno(win32.CreateDirectoryW(wpath, nil))
+
+ if !win32.CreateDirectoryW(wpath, nil) {
+ err = Errno(win32.GetLastError())
+ }
+ return
}
-remove_directory :: proc(path: string) -> Errno {
+remove_directory :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- return Errno(win32.RemoveDirectoryW(wpath))
+
+ if !win32.RemoveDirectoryW(wpath) {
+ err = Errno(win32.GetLastError())
+ }
+ return
}
@@ -344,7 +420,7 @@ is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' {
return true
}
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
if len(path) > 2 {
switch path[0] {
case 'A'..='Z', 'a'..='z':
@@ -400,23 +476,31 @@ fix_long_path :: proc(path: string) -> string {
}
-link :: proc(old_name, new_name: string) -> Errno {
+link :: proc(old_name, new_name: string) -> (err: Errno) {
n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil))
}
-unlink :: proc(path: string) -> Errno {
+unlink :: proc(path: string) -> (err: Errno) {
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- return Errno(win32.DeleteFileW(wpath))
+
+ if !win32.DeleteFileW(wpath) {
+ err = Errno(win32.GetLastError())
+ }
+ return
}
-rename :: proc(old_path, new_path: string) -> Errno {
+rename :: proc(old_path, new_path: string) -> (err: Errno) {
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
- return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING))
+
+ if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
+ err = Errno(win32.GetLastError())
+ }
+ return
}
diff --git a/core/os/os.odin b/core/os/os.odin
index be1f5ad4e..5e71e720e 100644
--- a/core/os/os.odin
+++ b/core/os/os.odin
@@ -9,6 +9,10 @@ OS :: ODIN_OS
ARCH :: ODIN_ARCH
ENDIAN :: ODIN_ENDIAN
+SEEK_SET :: 0
+SEEK_CUR :: 1
+SEEK_END :: 2
+
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, transmute([]byte)str)
}
@@ -55,6 +59,25 @@ write_encoded_rune :: proc(fd: Handle, r: rune) {
write_byte(fd, '\'')
}
+read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) {
+ if len(buf) < min {
+ return 0, -1
+ }
+ for n < min && err == 0 {
+ nn: int
+ nn, err = read(fd, buf[n:])
+ n += nn
+ }
+ if n >= min {
+ err = 0
+ }
+ return
+}
+
+read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) {
+ return read_at_least(fd, buf, len(buf))
+}
+
file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0)
@@ -85,27 +108,27 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
context.allocator = allocator
- length: i64
- err: Errno
- if length, err = file_size(fd); err != 0 {
- return nil, false
- }
+ length: i64
+ err: Errno
+ if length, err = file_size(fd); err != 0 {
+ return nil, false
+ }
- if length <= 0 {
- return nil, true
- }
+ if length <= 0 {
+ return nil, true
+ }
- data = make([]byte, int(length), allocator)
- if data == nil {
- return nil, false
- }
+ data = make([]byte, int(length), allocator)
+ if data == nil {
+ return nil, false
+ }
- bytes_read, read_err := read(fd, data)
- if read_err != ERROR_NONE {
- delete(data)
- return nil, false
- }
- return data[:bytes_read], true
+ bytes_read, read_err := read_full(fd, data)
+ if read_err != ERROR_NONE {
+ delete(data)
+ return nil, false
+ }
+ return data[:bytes_read], true
}
read_entire_file :: proc {
@@ -120,7 +143,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
}
mode: int = 0
- when OS == "linux" || OS == "darwin" {
+ when OS == .Linux || OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
@@ -187,11 +210,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
}
- aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
+ aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
if p == nil {
return nil, nil
}
- return aligned_alloc(new_size, new_alignment, p)
+
+ new_memory = aligned_alloc(new_size, new_alignment, p) or_return
+
+ // NOTE: heap_resize does not zero the new memory, so we do it
+ if new_size > old_size {
+ new_region := mem.raw_data(new_memory[old_size:])
+ mem.zero(new_region, new_size - old_size)
+ }
+ return
}
switch mode {
diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin
new file mode 100644
index 000000000..1833ac4dc
--- /dev/null
+++ b/core/os/os2/env_linux.odin
@@ -0,0 +1,28 @@
+//+private
+package os2
+
+_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ //TODO
+ return
+}
+
+_set_env :: proc(key, value: string) -> bool {
+ //TODO
+ return false
+}
+
+_unset_env :: proc(key: string) -> bool {
+ //TODO
+ return false
+}
+
+_clear_env :: proc() {
+ //TODO
+}
+
+_environ :: proc(allocator := context.allocator) -> []string {
+ //TODO
+ return nil
+}
+
+
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin
index af04db858..f58922fac 100644
--- a/core/os/os2/env_windows.odin
+++ b/core/os/os2/env_windows.odin
@@ -1,32 +1,37 @@
//+private
package os2
-import "core:mem"
import win32 "core:sys/windows"
+import "core:runtime"
-_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
wkey := win32.utf8_to_wstring(key)
- b := make([dynamic]u16, 100, context.temp_allocator)
- for {
- n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
- if n == 0 {
- err := win32.GetLastError()
- if err == win32.ERROR_ENVVAR_NOT_FOUND {
- return "", false
- }
- }
- if n <= u32(len(b)) {
- value = win32.utf16_to_utf8(b[:n], allocator)
- found = true
- return
+ n := win32.GetEnvironmentVariableW(wkey, nil, 0)
+ if n == 0 {
+ err := win32.GetLastError()
+ if err == win32.ERROR_ENVVAR_NOT_FOUND {
+ return "", false
}
-
- resize(&b, len(b)*2)
+ return "", true
}
+ b := make([]u16, n+1, _temp_allocator())
+
+ n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
+ if n == 0 {
+ err := win32.GetLastError()
+ if err == win32.ERROR_ENVVAR_NOT_FOUND {
+ return "", false
+ }
+ return "", false
+ }
+
+ value = win32.utf16_to_utf8(b[:n], allocator) or_else ""
+ found = true
+ return
}
_set_env :: proc(key, value: string) -> bool {
@@ -42,7 +47,7 @@ _unset_env :: proc(key: string) -> bool {
}
_clear_env :: proc() {
- envs := environ(context.temp_allocator)
+ envs := environ(_temp_allocator())
for env in envs {
for j in 1.. []string {
+_environ :: proc(allocator: runtime.Allocator) -> []string {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
return nil
@@ -62,14 +67,13 @@ _environ :: proc(allocator := context.allocator) -> []string {
r := make([dynamic]string, 0, 50, allocator)
for from, i, p := 0, 0, envs; true; i += 1 {
- c := (^u16)(uintptr(p) + uintptr(i*2))^
+ c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
- w := mem.slice_ptr(mem.ptr_offset(p, from), i-from)
-
- append(&r, win32.utf16_to_utf8(w, allocator))
+ w := ([^]u16)(p)[from:i]
+ append(&r, win32.utf16_to_utf8(w, allocator) or_else "")
from = i + 1
}
}
diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin
index 69aeaee4b..2cff73ebd 100644
--- a/core/os/os2/errors.odin
+++ b/core/os/os2/errors.odin
@@ -1,9 +1,10 @@
package os2
import "core:io"
+import "core:runtime"
General_Error :: enum u32 {
- Invalid_Argument,
+ None,
Permission_Denied,
Exist,
@@ -11,83 +12,78 @@ General_Error :: enum u32 {
Closed,
Timeout,
+
+ Invalid_File,
+ Invalid_Dir,
+ Invalid_Path,
+
+ Unsupported,
}
-Platform_Error :: struct {
- err: i32,
-}
+Platform_Error :: enum i32 {None=0}
-Error :: union {
+Error :: union #shared_nil {
General_Error,
io.Error,
+ runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == size_of(u64))
-Path_Error :: struct {
- op: string,
- path: string,
- err: Error,
-}
-
-Link_Error :: struct {
- op: string,
- old: string,
- new: string,
- err: Error,
-}
-
-path_error_delete :: proc(perr: Maybe(Path_Error)) {
- if err, ok := perr.?; ok {
- context.allocator = error_allocator()
- delete(err.op)
- delete(err.path)
- }
-}
-
-link_error_delete :: proc(lerr: Maybe(Link_Error)) {
- if err, ok := lerr.?; ok {
- context.allocator = error_allocator()
- delete(err.op)
- delete(err.old)
- delete(err.new)
- }
-}
-
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
- return v.err, v.err != 0
+ return i32(v), i32(v) != 0
}
error_string :: proc(ferr: Error) -> string {
- switch ferr {
- case nil: return ""
- case .Invalid_Argument: return "invalid argument"
- case .Permission_Denied: return "permission denied"
- case .Exist: return "file already exists"
- case .Not_Exist: return "file does not exist"
- case .Closed: return "file already closed"
- case .Timeout: return "i/o timeout"
- case .EOF: return "eof"
- case .Unexpected_EOF: return "unexpected eof"
- case .Short_Write: return "short write"
- case .Invalid_Write: return "invalid write result"
- case .Short_Buffer: return "short buffer"
- case .No_Progress: return "multiple read calls return no data or error"
- case .Invalid_Whence: return "invalid whence"
- case .Invalid_Offset: return "invalid offset"
- case .Invalid_Unread: return "invalid unread"
- case .Negative_Read: return "negative read"
- case .Negative_Write: return "negative write"
- case .Negative_Count: return "negative count"
- case .Buffer_Full: return "buffer full"
+ if ferr == nil {
+ return ""
}
-
- if errno, ok := is_platform_error(ferr); ok {
- return _error_string(errno)
+ switch e in ferr {
+ case General_Error:
+ switch e {
+ case .None: return ""
+ case .Permission_Denied: return "permission denied"
+ case .Exist: return "file already exists"
+ case .Not_Exist: return "file does not exist"
+ case .Closed: return "file already closed"
+ case .Timeout: return "i/o timeout"
+ case .Invalid_File: return "invalid file"
+ case .Invalid_Dir: return "invalid directory"
+ case .Invalid_Path: return "invalid path"
+ case .Unsupported: return "unsupported"
+ }
+ case io.Error:
+ switch e {
+ case .None: return ""
+ case .EOF: return "eof"
+ case .Unexpected_EOF: return "unexpected eof"
+ case .Short_Write: return "short write"
+ case .Invalid_Write: return "invalid write result"
+ case .Short_Buffer: return "short buffer"
+ case .No_Progress: return "multiple read calls return no data or error"
+ case .Invalid_Whence: return "invalid whence"
+ case .Invalid_Offset: return "invalid offset"
+ case .Invalid_Unread: return "invalid unread"
+ case .Negative_Read: return "negative read"
+ case .Negative_Write: return "negative write"
+ case .Negative_Count: return "negative count"
+ case .Buffer_Full: return "buffer full"
+ case .Unknown, .Empty: //
+ }
+ case runtime.Allocator_Error:
+ switch e {
+ case .None: return ""
+ case .Out_Of_Memory: return "out of memory"
+ case .Invalid_Pointer: return "invalid allocator pointer"
+ case .Invalid_Argument: return "invalid allocator argument"
+ case .Mode_Not_Implemented: return "allocator mode not implemented"
+ }
+ case Platform_Error:
+ return _error_string(i32(e))
}
return "unknown error"
diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin
new file mode 100644
index 000000000..053256fbd
--- /dev/null
+++ b/core/os/os2/errors_linux.odin
@@ -0,0 +1,145 @@
+//+private
+package os2
+
+import "core:sys/unix"
+
+EPERM :: 1
+ENOENT :: 2
+ESRCH :: 3
+EINTR :: 4
+EIO :: 5
+ENXIO :: 6
+EBADF :: 9
+EAGAIN :: 11
+ENOMEM :: 12
+EACCES :: 13
+EFAULT :: 14
+EEXIST :: 17
+ENODEV :: 19
+ENOTDIR :: 20
+EISDIR :: 21
+EINVAL :: 22
+ENFILE :: 23
+EMFILE :: 24
+ETXTBSY :: 26
+EFBIG :: 27
+ENOSPC :: 28
+ESPIPE :: 29
+EROFS :: 30
+EPIPE :: 32
+ERANGE :: 34 /* Result too large */
+EDEADLK :: 35 /* Resource deadlock would occur */
+ENAMETOOLONG :: 36 /* File name too long */
+ENOLCK :: 37 /* No record locks available */
+ENOSYS :: 38 /* Invalid system call number */
+ENOTEMPTY :: 39 /* Directory not empty */
+ELOOP :: 40 /* Too many symbolic links encountered */
+EWOULDBLOCK :: EAGAIN /* Operation would block */
+ENOMSG :: 42 /* No message of desired type */
+EIDRM :: 43 /* Identifier removed */
+ECHRNG :: 44 /* Channel number out of range */
+EL2NSYNC :: 45 /* Level 2 not synchronized */
+EL3HLT :: 46 /* Level 3 halted */
+EL3RST :: 47 /* Level 3 reset */
+ELNRNG :: 48 /* Link number out of range */
+EUNATCH :: 49 /* Protocol driver not attached */
+ENOCSI :: 50 /* No CSI structure available */
+EL2HLT :: 51 /* Level 2 halted */
+EBADE :: 52 /* Invalid exchange */
+EBADR :: 53 /* Invalid request descriptor */
+EXFULL :: 54 /* Exchange full */
+ENOANO :: 55 /* No anode */
+EBADRQC :: 56 /* Invalid request code */
+EBADSLT :: 57 /* Invalid slot */
+EDEADLOCK :: EDEADLK
+EBFONT :: 59 /* Bad font file format */
+ENOSTR :: 60 /* Device not a stream */
+ENODATA :: 61 /* No data available */
+ETIME :: 62 /* Timer expired */
+ENOSR :: 63 /* Out of streams resources */
+ENONET :: 64 /* Machine is not on the network */
+ENOPKG :: 65 /* Package not installed */
+EREMOTE :: 66 /* Object is remote */
+ENOLINK :: 67 /* Link has been severed */
+EADV :: 68 /* Advertise error */
+ESRMNT :: 69 /* Srmount error */
+ECOMM :: 70 /* Communication error on send */
+EPROTO :: 71 /* Protocol error */
+EMULTIHOP :: 72 /* Multihop attempted */
+EDOTDOT :: 73 /* RFS specific error */
+EBADMSG :: 74 /* Not a data message */
+EOVERFLOW :: 75 /* Value too large for defined data type */
+ENOTUNIQ :: 76 /* Name not unique on network */
+EBADFD :: 77 /* File descriptor in bad state */
+EREMCHG :: 78 /* Remote address changed */
+ELIBACC :: 79 /* Can not access a needed shared library */
+ELIBBAD :: 80 /* Accessing a corrupted shared library */
+ELIBSCN :: 81 /* .lib section in a.out corrupted */
+ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
+ELIBEXEC :: 83 /* Cannot exec a shared library directly */
+EILSEQ :: 84 /* Illegal byte sequence */
+ERESTART :: 85 /* Interrupted system call should be restarted */
+ESTRPIPE :: 86 /* Streams pipe error */
+EUSERS :: 87 /* Too many users */
+ENOTSOCK :: 88 /* Socket operation on non-socket */
+EDESTADDRREQ :: 89 /* Destination address required */
+EMSGSIZE :: 90 /* Message too long */
+EPROTOTYPE :: 91 /* Protocol wrong type for socket */
+ENOPROTOOPT :: 92 /* Protocol not available */
+EPROTONOSUPPORT:: 93 /* Protocol not supported */
+ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
+EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
+EPFNOSUPPORT :: 96 /* Protocol family not supported */
+EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
+EADDRINUSE :: 98 /* Address already in use */
+EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
+ENETDOWN :: 100 /* Network is down */
+ENETUNREACH :: 101 /* Network is unreachable */
+ENETRESET :: 102 /* Network dropped connection because of reset */
+ECONNABORTED :: 103 /* Software caused connection abort */
+ECONNRESET :: 104 /* Connection reset by peer */
+ENOBUFS :: 105 /* No buffer space available */
+EISCONN :: 106 /* Transport endpoint is already connected */
+ENOTCONN :: 107 /* Transport endpoint is not connected */
+ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
+ETOOMANYREFS :: 109 /* Too many references: cannot splice */
+ETIMEDOUT :: 110 /* Connection timed out */
+ECONNREFUSED :: 111 /* Connection refused */
+EHOSTDOWN :: 112 /* Host is down */
+EHOSTUNREACH :: 113 /* No route to host */
+EALREADY :: 114 /* Operation already in progress */
+EINPROGRESS :: 115 /* Operation now in progress */
+ESTALE :: 116 /* Stale file handle */
+EUCLEAN :: 117 /* Structure needs cleaning */
+ENOTNAM :: 118 /* Not a XENIX named type file */
+ENAVAIL :: 119 /* No XENIX semaphores available */
+EISNAM :: 120 /* Is a named type file */
+EREMOTEIO :: 121 /* Remote I/O error */
+EDQUOT :: 122 /* Quota exceeded */
+ENOMEDIUM :: 123 /* No medium found */
+EMEDIUMTYPE :: 124 /* Wrong medium type */
+ECANCELED :: 125 /* Operation Canceled */
+ENOKEY :: 126 /* Required key not available */
+EKEYEXPIRED :: 127 /* Key has expired */
+EKEYREVOKED :: 128 /* Key has been revoked */
+EKEYREJECTED :: 129 /* Key was rejected by service */
+EOWNERDEAD :: 130 /* Owner died */
+ENOTRECOVERABLE:: 131 /* State not recoverable */
+ERFKILL :: 132 /* Operation not possible due to RF-kill */
+EHWPOISON :: 133 /* Memory page has hardware error */
+
+_get_platform_error :: proc(res: int) -> Error {
+ errno := unix.get_errno(res)
+ return Platform_Error(i32(errno))
+}
+
+_ok_or_error :: proc(res: int) -> Error {
+ return res >= 0 ? nil : _get_platform_error(res)
+}
+
+_error_string :: proc(errno: i32) -> string {
+ if errno == 0 {
+ return ""
+ }
+ return "Error"
+}
diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin
index f9dd3fdca..27c16e72e 100644
--- a/core/os/os2/errors_windows.odin
+++ b/core/os/os2/errors_windows.odin
@@ -12,3 +12,49 @@ _error_string :: proc(errno: i32) -> string {
// FormatMessageW
return ""
}
+
+_get_platform_error :: proc() -> Error {
+ err := win32.GetLastError()
+ if err == 0 {
+ return nil
+ }
+ switch err {
+ case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION:
+ return .Permission_Denied
+
+ case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS:
+ return .Exist
+
+ case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND:
+ return .Not_Exist
+
+ case win32.ERROR_NO_DATA:
+ return .Closed
+
+ case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT:
+ return .Timeout
+
+ case win32.ERROR_NOT_SUPPORTED:
+ return .Unsupported
+
+ case
+ win32.ERROR_BAD_ARGUMENTS,
+ win32.ERROR_INVALID_PARAMETER,
+ win32.ERROR_NOT_ENOUGH_MEMORY,
+ win32.ERROR_INVALID_HANDLE,
+ win32.ERROR_NO_MORE_FILES,
+ win32.ERROR_LOCK_VIOLATION,
+ win32.ERROR_HANDLE_EOF,
+ win32.ERROR_BROKEN_PIPE,
+ win32.ERROR_CALL_NOT_IMPLEMENTED,
+ win32.ERROR_INSUFFICIENT_BUFFER,
+ win32.ERROR_INVALID_NAME,
+ win32.ERROR_LOCK_FAILED,
+ win32.ERROR_ENVVAR_NOT_FOUND,
+ win32.ERROR_OPERATION_ABORTED,
+ win32.ERROR_IO_PENDING,
+ win32.ERROR_NO_UNICODE_TRANSLATION:
+ // fallthrough
+ }
+ return Platform_Error(err)
+}
\ No newline at end of file
diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin
index 5b995bd69..eb6d9e366 100644
--- a/core/os/os2/file.odin
+++ b/core/os/os2/file.odin
@@ -2,8 +2,11 @@ package os2
import "core:io"
import "core:time"
+import "core:runtime"
-Handle :: distinct uintptr
+File :: struct {
+ impl: _File,
+}
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
@@ -18,131 +21,170 @@ File_Mode_Device :: File_Mode(1<<18)
File_Mode_Char_Device :: File_Mode(1<<19)
File_Mode_Sym_Link :: File_Mode(1<<20)
+File_Mode_Perm :: File_Mode(0o777) // Unix permision bits
-O_RDONLY :: int( 0)
-O_WRONLY :: int( 1)
-O_RDWR :: int( 2)
-O_APPEND :: int( 4)
-O_CREATE :: int( 8)
-O_EXCL :: int(16)
-O_SYNC :: int(32)
-O_TRUNC :: int(64)
+File_Flags :: distinct bit_set[File_Flag; uint]
+File_Flag :: enum {
+ Read,
+ Write,
+ Append,
+ Create,
+ Excl,
+ Sync,
+ Trunc,
+ Sparse,
+ Close_On_Exec,
-
-
-stdin: Handle = 0 // OS-Specific
-stdout: Handle = 1 // OS-Specific
-stderr: Handle = 2 // OS-Specific
-
-
-create :: proc(name: string) -> (Handle, Error) {
- return _create(name)
+ Unbuffered_IO,
}
-open :: proc(name: string) -> (Handle, Error) {
- return _open(name)
+O_RDONLY :: File_Flags{.Read}
+O_WRONLY :: File_Flags{.Write}
+O_RDWR :: File_Flags{.Read, .Write}
+O_APPEND :: File_Flags{.Append}
+O_CREATE :: File_Flags{.Create}
+O_EXCL :: File_Flags{.Excl}
+O_SYNC :: File_Flags{.Sync}
+O_TRUNC :: File_Flags{.Trunc}
+O_SPARSE :: File_Flags{.Sparse}
+O_CLOEXEC :: File_Flags{.Close_On_Exec}
+
+
+
+stdin: ^File = nil // OS-Specific
+stdout: ^File = nil // OS-Specific
+stderr: ^File = nil // OS-Specific
+
+
+create :: proc(name: string) -> (^File, Error) {
+ return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
}
-open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
- return _open_file(name, flag, perm)
+open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) {
+ return _open(name, flags, perm)
}
-close :: proc(fd: Handle) -> Error {
- return _close(fd)
+new_file :: proc(handle: uintptr, name: string) -> ^File {
+ return _new_file(handle, name)
}
-name :: proc(fd: Handle, allocator := context.allocator) -> string {
- return _name(fd)
-}
-
-seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
- return _seek(fd, offset, whence)
-}
-
-read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
- return _read(fd, p)
-}
-
-read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
- return _read_at(fd, p, offset)
-}
-
-read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
- return _read_from(fd, r)
-}
-
-write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
- return _write(fd, p)
-}
-
-write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
- return _write_at(fd, p, offset)
-}
-
-write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
- return _write_to(fd, w)
-}
-
-file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
- return _file_size(fd)
+fd :: proc(f: ^File) -> uintptr {
+ return _fd(f)
}
-sync :: proc(fd: Handle) -> Error {
- return _sync(fd)
+close :: proc(f: ^File) -> Error {
+ return _close(f)
}
-flush :: proc(fd: Handle) -> Error {
- return _flush(fd)
+name :: proc(f: ^File) -> string {
+ return _name(f)
}
-truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
- return _truncate(fd, size)
+seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+ return _seek(f, offset, whence)
}
-remove :: proc(name: string) -> Maybe(Path_Error) {
+read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ return _read(f, p)
+}
+
+read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ return _read_at(f, p, offset)
+}
+
+read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
+ return _read_from(f, r)
+}
+
+write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ return _write(f, p)
+}
+
+write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ return _write_at(f, p, offset)
+}
+
+write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
+ return _write_to(f, w)
+}
+
+file_size :: proc(f: ^File) -> (n: i64, err: Error) {
+ return _file_size(f)
+}
+
+
+sync :: proc(f: ^File) -> Error {
+ return _sync(f)
+}
+
+flush :: proc(f: ^File) -> Error {
+ return _flush(f)
+}
+
+truncate :: proc(f: ^File, size: i64) -> Error {
+ return _truncate(f, size)
+}
+
+remove :: proc(name: string) -> Error {
return _remove(name)
}
-rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
+rename :: proc(old_path, new_path: string) -> Error {
return _rename(old_path, new_path)
}
-link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
+link :: proc(old_name, new_name: string) -> Error {
return _link(old_name, new_name)
}
-symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
+symlink :: proc(old_name, new_name: string) -> Error {
return _symlink(old_name, new_name)
}
-read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
- return _read_link(name)
+read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
+ return _read_link(name,allocator)
}
-chdir :: proc(fd: Handle) -> Error {
- return _chdir(fd)
+chdir :: proc(name: string) -> Error {
+ return _chdir(name)
}
-chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
- return _chmod(fd, mode)
+chmod :: proc(name: string, mode: File_Mode) -> Error {
+ return _chmod(name, mode)
}
-chown :: proc(fd: Handle, uid, gid: int) -> Error {
- return _chown(fd, uid, gid)
+chown :: proc(name: string, uid, gid: int) -> Error {
+ return _chown(name, uid, gid)
}
+fchdir :: proc(f: ^File) -> Error {
+ return _fchdir(f)
+}
+
+fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
+ return _fchmod(f, mode)
+}
+
+fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ return _fchown(f, uid, gid)
+}
+
+
lchown :: proc(name: string, uid, gid: int) -> Error {
return _lchown(name, uid, gid)
}
-chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
+chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
return _chtimes(name, atime, mtime)
}
+fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ return _fchtimes(f, atime, mtime)
+}
exists :: proc(path: string) -> bool {
return _exists(path)
@@ -156,3 +198,20 @@ is_dir :: proc(path: string) -> bool {
return _is_dir(path)
}
+
+copy_file :: proc(dst_path, src_path: string) -> Error {
+ src := open(src_path) or_return
+ defer close(src)
+
+ info := fstat(src, _file_allocator()) or_return
+ defer file_info_delete(info, _file_allocator())
+ if info.is_dir {
+ return .Invalid_File
+ }
+
+ dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return
+ defer close(dst)
+
+ _, err := io.copy(to_writer(dst), to_reader(src))
+ return err
+}
\ No newline at end of file
diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin
new file mode 100644
index 000000000..0f2e810f4
--- /dev/null
+++ b/core/os/os2/file_linux.odin
@@ -0,0 +1,422 @@
+//+private
+package os2
+
+import "core:io"
+import "core:time"
+import "core:strings"
+import "core:runtime"
+import "core:sys/unix"
+
+INVALID_HANDLE :: -1
+
+_O_RDONLY :: 0o0
+_O_WRONLY :: 0o1
+_O_RDWR :: 0o2
+_O_CREAT :: 0o100
+_O_EXCL :: 0o200
+_O_TRUNC :: 0o1000
+_O_APPEND :: 0o2000
+_O_NONBLOCK :: 0o4000
+_O_LARGEFILE :: 0o100000
+_O_DIRECTORY :: 0o200000
+_O_NOFOLLOW :: 0o400000
+_O_SYNC :: 0o4010000
+_O_CLOEXEC :: 0o2000000
+_O_PATH :: 0o10000000
+
+_AT_FDCWD :: -100
+
+_CSTRING_NAME_HEAP_THRESHOLD :: 512
+
+_File :: struct {
+ name: string,
+ fd: int,
+ allocator: runtime.Allocator,
+}
+
+_file_allocator :: proc() -> runtime.Allocator {
+ return heap_allocator()
+}
+
+_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+
+ flags_i: int
+ switch flags & O_RDONLY|O_WRONLY|O_RDWR {
+ case O_RDONLY: flags_i = _O_RDONLY
+ case O_WRONLY: flags_i = _O_WRONLY
+ case O_RDWR: flags_i = _O_RDWR
+ }
+
+ flags_i |= (_O_APPEND * int(.Append in flags))
+ flags_i |= (_O_CREAT * int(.Create in flags))
+ flags_i |= (_O_EXCL * int(.Excl in flags))
+ flags_i |= (_O_SYNC * int(.Sync in flags))
+ flags_i |= (_O_TRUNC * int(.Trunc in flags))
+ flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
+
+ fd := unix.sys_open(name_cstr, flags_i, int(perm))
+ if fd < 0 {
+ return nil, _get_platform_error(fd)
+ }
+
+ return _new_file(uintptr(fd), name), nil
+}
+
+_new_file :: proc(fd: uintptr, _: string) -> ^File {
+ file := new(File, _file_allocator())
+ file.impl.fd = int(fd)
+ file.impl.allocator = _file_allocator()
+ file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
+ return file
+}
+
+_destroy :: proc(f: ^File) -> Error {
+ if f == nil {
+ return nil
+ }
+ delete(f.impl.name, f.impl.allocator)
+ free(f, f.impl.allocator)
+ return nil
+}
+
+
+_close :: proc(f: ^File) -> Error {
+ res := unix.sys_close(f.impl.fd)
+ return _ok_or_error(res)
+}
+
+_fd :: proc(f: ^File) -> uintptr {
+ if f == nil {
+ return ~uintptr(0)
+ }
+ return uintptr(f.impl.fd)
+}
+
+_name :: proc(f: ^File) -> string {
+ return f.impl.name if f != nil else ""
+}
+
+_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+ res := unix.sys_lseek(f.impl.fd, offset, int(whence))
+ if res < 0 {
+ return -1, _get_platform_error(int(res))
+ }
+ return res, nil
+}
+
+_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ n = unix.sys_read(f.impl.fd, &p[0], len(p))
+ if n < 0 {
+ return -1, _get_platform_error(n)
+ }
+ return n, nil
+}
+
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ if offset < 0 {
+ return 0, .Invalid_Offset
+ }
+
+ b, offset := p, offset
+ for len(b) > 0 {
+ m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
+ if m < 0 {
+ return -1, _get_platform_error(m)
+ }
+ n += m
+ b = b[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
+ //TODO
+ return
+}
+
+_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ if len(p) == 0 {
+ return 0, nil
+ }
+ n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
+ if n < 0 {
+ return -1, _get_platform_error(n)
+ }
+ return int(n), nil
+}
+
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ if offset < 0 {
+ return 0, .Invalid_Offset
+ }
+
+ b, offset := p, offset
+ for len(b) > 0 {
+ m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
+ if m < 0 {
+ return -1, _get_platform_error(m)
+ }
+ n += m
+ b = b[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
+ //TODO
+ return
+}
+
+_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
+ s: _Stat = ---
+ res := unix.sys_fstat(f.impl.fd, &s)
+ if res < 0 {
+ return -1, _get_platform_error(res)
+ }
+ return s.size, nil
+}
+
+_sync :: proc(f: ^File) -> Error {
+ return _ok_or_error(unix.sys_fsync(f.impl.fd))
+}
+
+_flush :: proc(f: ^File) -> Error {
+ return _ok_or_error(unix.sys_fsync(f.impl.fd))
+}
+
+_truncate :: proc(f: ^File, size: i64) -> Error {
+ return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
+}
+
+_remove :: proc(name: string) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+
+ fd := unix.sys_open(name_cstr, int(File_Flags.Read))
+ if fd < 0 {
+ return _get_platform_error(fd)
+ }
+ defer unix.sys_close(fd)
+
+ if _is_dir_fd(fd) {
+ return _ok_or_error(unix.sys_rmdir(name_cstr))
+ }
+ return _ok_or_error(unix.sys_unlink(name_cstr))
+}
+
+_rename :: proc(old_name, new_name: string) -> Error {
+ old_name_cstr, old_allocated := _name_to_cstring(old_name)
+ new_name_cstr, new_allocated := _name_to_cstring(new_name)
+ defer if old_allocated {
+ delete(old_name_cstr)
+ }
+ defer if new_allocated {
+ delete(new_name_cstr)
+ }
+
+ return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
+}
+
+_link :: proc(old_name, new_name: string) -> Error {
+ old_name_cstr, old_allocated := _name_to_cstring(old_name)
+ new_name_cstr, new_allocated := _name_to_cstring(new_name)
+ defer if old_allocated {
+ delete(old_name_cstr)
+ }
+ defer if new_allocated {
+ delete(new_name_cstr)
+ }
+
+ return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
+}
+
+_symlink :: proc(old_name, new_name: string) -> Error {
+ old_name_cstr, old_allocated := _name_to_cstring(old_name)
+ new_name_cstr, new_allocated := _name_to_cstring(new_name)
+ defer if old_allocated {
+ delete(old_name_cstr)
+ }
+ defer if new_allocated {
+ delete(new_name_cstr)
+ }
+
+ return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
+}
+
+_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
+ bufsz : uint = 256
+ buf := make([]byte, bufsz, allocator)
+ for {
+ rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
+ if rc < 0 {
+ delete(buf)
+ return "", _get_platform_error(rc)
+ } else if rc == int(bufsz) {
+ bufsz *= 2
+ delete(buf)
+ buf = make([]byte, bufsz, allocator)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), nil
+ }
+ }
+}
+
+_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _read_link_cstr(name_cstr, allocator)
+}
+
+_unlink :: proc(name: string) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _ok_or_error(unix.sys_unlink(name_cstr))
+}
+
+_chdir :: proc(name: string) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _ok_or_error(unix.sys_chdir(name_cstr))
+}
+
+_fchdir :: proc(f: ^File) -> Error {
+ return _ok_or_error(unix.sys_fchdir(f.impl.fd))
+}
+
+_chmod :: proc(name: string, mode: File_Mode) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
+}
+
+_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
+ return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
+}
+
+// NOTE: will throw error without super user priviledges
+_chown :: proc(name: string, uid, gid: int) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
+}
+
+// NOTE: will throw error without super user priviledges
+_lchown :: proc(name: string, uid, gid: int) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
+}
+
+// NOTE: will throw error without super user priviledges
+_fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
+}
+
+_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ times := [2]Unix_File_Time {
+ { atime._nsec, 0 },
+ { mtime._nsec, 0 },
+ }
+ return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
+}
+
+_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ times := [2]Unix_File_Time {
+ { atime._nsec, 0 },
+ { mtime._nsec, 0 },
+ }
+ return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
+}
+
+_exists :: proc(name: string) -> bool {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ return unix.sys_access(name_cstr, F_OK) == 0
+}
+
+_is_file :: proc(name: string) -> bool {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ s: _Stat
+ res := unix.sys_stat(name_cstr, &s)
+ if res < 0 {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+_is_file_fd :: proc(fd: int) -> bool {
+ s: _Stat
+ res := unix.sys_fstat(fd, &s)
+ if res < 0 { // error
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+_is_dir :: proc(name: string) -> bool {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ s: _Stat
+ res := unix.sys_stat(name_cstr, &s)
+ if res < 0 {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+_is_dir_fd :: proc(fd: int) -> bool {
+ s: _Stat
+ res := unix.sys_fstat(fd, &s)
+ if res < 0 { // error
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly
+// defined as 512, however, it is well known that paths can exceed that limit.
+// So, in theory you could have a path larger than the entire temp_allocator's
+// buffer. Therefor, any large paths will use context.allocator.
+_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
+ if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
+ cname = strings.clone_to_cstring(name)
+ allocated = true
+ return
+ }
+ cname = strings.clone_to_cstring(name, context.temp_allocator)
+ return
+}
diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin
index 0962ed59c..5e3db9370 100644
--- a/core/os/os2/file_stream.odin
+++ b/core/os/os2/file_stream.odin
@@ -2,12 +2,20 @@ package os2
import "core:io"
-file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
- s.stream_data = rawptr(uintptr(fd))
+to_stream :: proc(f: ^File) -> (s: io.Stream) {
+ s.stream_data = f
s.stream_vtable = _file_stream_vtable
return
}
+to_writer :: proc(f: ^File) -> (s: io.Writer) {
+ return {to_stream(f)}
+}
+to_reader :: proc(f: ^File) -> (s: io.Reader) {
+ return {to_stream(f)}
+}
+
+
@(private)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
@@ -20,66 +28,66 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
@(private)
_file_stream_vtable := &io.Stream_VTable{
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = read(fd, p)
+ n, ferr = read(f, p)
err = error_to_io_error(ferr)
return
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = read_at(fd, p, offset)
+ n, ferr = read_at(f, p, offset)
err = error_to_io_error(ferr)
return
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = write_to(fd, w)
+ n, ferr = write_to(f, w)
err = error_to_io_error(ferr)
return
},
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = write(fd, p)
+ n, ferr = write(f, p)
err = error_to_io_error(ferr)
return
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = write_at(fd, p, offset)
+ n, ferr = write_at(f, p, offset)
err = error_to_io_error(ferr)
return
},
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
+ f := (^File)(s.stream_data)
ferr: Error
- n, ferr = read_from(fd, r)
+ n, ferr = read_from(f, r)
err = error_to_io_error(ferr)
return
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- fd := Handle(uintptr(s.stream_data))
- n, ferr := seek(fd, offset, Seek_From(whence))
+ f := (^File)(s.stream_data)
+ n, ferr := seek(f, offset, Seek_From(whence))
err := error_to_io_error(ferr)
return n, err
},
impl_size = proc(s: io.Stream) -> i64 {
- fd := Handle(uintptr(s.stream_data))
- sz, _ := file_size(fd)
+ f := (^File)(s.stream_data)
+ sz, _ := file_size(f)
return sz
},
impl_flush = proc(s: io.Stream) -> io.Error {
- fd := Handle(uintptr(s.stream_data))
- ferr := flush(fd)
+ f := (^File)(s.stream_data)
+ ferr := flush(f)
return error_to_io_error(ferr)
},
impl_close = proc(s: io.Stream) -> io.Error {
- fd := Handle(uintptr(s.stream_data))
- ferr := close(fd)
+ f := (^File)(s.stream_data)
+ ferr := close(f)
return error_to_io_error(ferr)
},
}
diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin
index 77f6545ac..9f9064244 100644
--- a/core/os/os2/file_util.odin
+++ b/core/os/os2/file_util.odin
@@ -4,25 +4,25 @@ import "core:mem"
import "core:strconv"
import "core:unicode/utf8"
-write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) {
- return write(fd, transmute([]byte)s)
+write_string :: proc(f: ^File, s: string) -> (n: int, err: Error) {
+ return write(f, transmute([]byte)s)
}
-write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) {
- return write(fd, []byte{b})
+write_byte :: proc(f: ^File, b: byte) -> (n: int, err: Error) {
+ return write(f, []byte{b})
}
-write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
+write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
if r < utf8.RUNE_SELF {
- return write_byte(fd, byte(r))
+ return write_byte(f, byte(r))
}
b: [4]byte
b, n = utf8.encode_rune(r)
- return write(fd, b[:n])
+ return write(f, b[:n])
}
-write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
+write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
n^ += m
if merr != nil {
@@ -32,49 +32,49 @@ write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
return false
}
- if wrap(write_byte(fd, '\''), &n, &err) { return }
+ if wrap(write_byte(f, '\''), &n, &err) { return }
switch r {
- case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return }
- case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return }
- case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return }
- case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return }
- case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return }
- case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return }
- case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return }
- case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return }
+ case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
+ case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
+ case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
+ case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
+ case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
+ case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
+ case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
+ case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
case:
if r < 32 {
- if wrap(write_string(fd, "\\x"), &n, &err) { return }
+ if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) {
- case 0: if wrap(write_string(fd, "00"), &n, &err) { return }
- case 1: if wrap(write_rune(fd, '0'), &n, &err) { return }
- case 2: if wrap(write_string(fd, s), &n, &err) { return }
+ case 0: if wrap(write_string(f, "00"), &n, &err) { return }
+ case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
+ case 2: if wrap(write_string(f, s), &n, &err) { return }
}
} else {
- if wrap(write_rune(fd, r), &n, &err) { return }
+ if wrap(write_rune(f, r), &n, &err) { return }
}
}
- _ = wrap(write_byte(fd, '\''), &n, &err)
+ _ = wrap(write_byte(f, '\''), &n, &err)
return
}
-write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
+write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len}
- return write(fd, s)
+ return write(f, s)
}
-read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
+read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
s := transmute([]byte)mem.Raw_Slice{data, len}
- return read(fd, s)
+ return read(f, s)
}
-read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) {
+read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
f, ferr := open(name)
if ferr != nil {
return nil, ferr
@@ -91,15 +91,17 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byt
// TODO(bill): Is this correct logic?
total: int
- data := make([]byte, size, allocator)
+ data = make([]byte, size, allocator) or_return
for {
- n, err := read(f, data[total:])
+ n: int
+ n, err = read(f, data[total:])
total += n
if err != nil {
if err == .EOF {
err = nil
}
- return data[:total], err
+ data = data[:total]
+ return
}
}
}
@@ -109,7 +111,7 @@ write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate
if truncate {
flags |= O_TRUNC
}
- f, err := open_file(name, flags, perm)
+ f, err := open(name, flags, perm)
if err != nil {
return err
}
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index cfc9feebf..7589ed799 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -2,135 +2,728 @@
package os2
import "core:io"
+import "core:mem"
+import "core:sync"
+import "core:runtime"
+import "core:strings"
import "core:time"
+import "core:unicode/utf16"
+import win32 "core:sys/windows"
-_create :: proc(name: string) -> (Handle, Error) {
- return 0, nil
+INVALID_HANDLE :: ~uintptr(0)
+
+S_IWRITE :: 0o200
+_ERROR_BAD_NETPATH :: 53
+MAX_RW :: 1<<30
+
+_file_allocator :: proc() -> runtime.Allocator {
+ return heap_allocator()
}
-_open :: proc(name: string) -> (Handle, Error) {
- return 0, nil
-}
-
-_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
- return 0, nil
-}
-
-_close :: proc(fd: Handle) -> Error {
- return nil
-}
-
-_name :: proc(fd: Handle, allocator := context.allocator) -> string {
- return ""
-}
-
-_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
- return
-}
-
-_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
- return
-}
-
-_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
- return
-}
-
-_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
- return
-}
-
-_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
- return
-}
-
-_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
- return
-}
-
-_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
- return
-}
-
-_file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
- return
+_temp_allocator :: proc() -> runtime.Allocator {
+ // TODO(bill): make this not depend on the context allocator
+ return context.temp_allocator
}
-_sync :: proc(fd: Handle) -> Error {
- return nil
+_File_Kind :: enum u8 {
+ File,
+ Console,
+ Pipe,
}
-_flush :: proc(fd: Handle) -> Error {
- return nil
+_File :: struct {
+ fd: rawptr,
+ name: string,
+ wname: win32.wstring,
+ kind: _File_Kind,
+
+ allocator: runtime.Allocator,
+
+ rw_mutex: sync.RW_Mutex, // read write calls
+ p_mutex: sync.Mutex, // pread pwrite calls
}
-_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
- return nil
+_handle :: proc(f: ^File) -> win32.HANDLE {
+ return win32.HANDLE(_fd(f))
}
-_remove :: proc(name: string) -> Maybe(Path_Error) {
- return nil
+_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) {
+ if len(name) == 0 {
+ err = .Not_Exist
+ return
+ }
+
+ path := _fix_long_path(name)
+ access: u32
+ switch flags & {.Read, .Write} {
+ case {.Read}: access = win32.FILE_GENERIC_READ
+ case {.Write}: access = win32.FILE_GENERIC_WRITE
+ case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
+ }
+
+ if .Create in flags {
+ access |= win32.FILE_GENERIC_WRITE
+ }
+ if .Append in flags {
+ access &~= win32.FILE_GENERIC_WRITE
+ access |= win32.FILE_APPEND_DATA
+ }
+ share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
+ sa: ^win32.SECURITY_ATTRIBUTES
+ if .Close_On_Exec not_in flags {
+ sa = &win32.SECURITY_ATTRIBUTES{}
+ sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
+ sa.bInheritHandle = true
+ }
+
+ create_mode: u32 = win32.OPEN_EXISTING
+ switch {
+ case flags & {.Create, .Excl} == {.Create, .Excl}:
+ create_mode = win32.CREATE_NEW
+ case flags & {.Create, .Trunc} == {.Create, .Trunc}:
+ create_mode = win32.CREATE_ALWAYS
+ case flags & {.Create} == {.Create}:
+ create_mode = win32.OPEN_ALWAYS
+ case flags & {.Trunc} == {.Trunc}:
+ create_mode = win32.TRUNCATE_EXISTING
+ }
+
+ attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
+ if perm & S_IWRITE == 0 {
+ attrs = win32.FILE_ATTRIBUTE_READONLY
+ if create_mode == win32.CREATE_ALWAYS {
+ // NOTE(bill): Open has just asked to create a file in read-only mode.
+ // If the file already exists, to make it akin to a *nix open call,
+ // the call preserves the existing permissions.
+ h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
+ if h == win32.INVALID_HANDLE {
+ switch e := win32.GetLastError(); e {
+ case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
+ // file does not exist, create the file
+ case 0:
+ return uintptr(h), nil
+ case:
+ return 0, Platform_Error(e)
+ }
+ }
+ }
+ }
+ h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil)
+ if h == win32.INVALID_HANDLE {
+ return 0, _get_platform_error()
+ }
+ return uintptr(h), nil
}
-_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
+
+_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
+ flags := flags if flags != nil else {.Read}
+ handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return
+ return _new_file(handle, name), nil
+}
+
+_new_file :: proc(handle: uintptr, name: string) -> ^File {
+ if handle == INVALID_HANDLE {
+ return nil
+ }
+ f := new(File, _file_allocator())
+
+ f.impl.allocator = _file_allocator()
+ f.impl.fd = rawptr(fd)
+ f.impl.name = strings.clone(name, f.impl.allocator)
+ f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
+
+ handle := _handle(f)
+ kind := _File_Kind.File
+ if m: u32; win32.GetConsoleMode(handle, &m) {
+ kind = .Console
+ }
+ if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
+ kind = .Pipe
+ }
+ f.impl.kind = kind
+
+ return f
+}
+
+_fd :: proc(f: ^File) -> uintptr {
+ if f == nil {
+ return INVALID_HANDLE
+ }
+ return uintptr(f.impl.fd)
+}
+
+_destroy :: proc(f: ^File) -> Error {
+ if f == nil {
+ return nil
+ }
+
+ a := f.impl.allocator
+ free(f.impl.wname, a)
+ delete(f.impl.name, a)
+ free(f, a)
return nil
}
-_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
+_close :: proc(f: ^File) -> Error {
+ if f == nil {
+ return nil
+ }
+ if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
+ return .Closed
+ }
+ return _destroy(f)
+}
+
+_name :: proc(f: ^File) -> string {
+ return f.impl.name if f != nil else ""
+}
+
+_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+ handle := _handle(f)
+ if handle == win32.INVALID_HANDLE {
+ return 0, .Invalid_File
+ }
+ if f.impl.kind == .Pipe {
+ return 0, .Invalid_File
+ }
+
+ sync.guard(&f.impl.rw_mutex)
+
+ w: u32
+ switch whence {
+ case .Start: w = win32.FILE_BEGIN
+ case .Current: w = win32.FILE_CURRENT
+ case .End: w = win32.FILE_END
+ }
+ hi := i32(offset>>32)
+ lo := i32(offset)
+
+ dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
+ if dw_ptr == win32.INVALID_SET_FILE_POINTER {
+ return 0, _get_platform_error()
+ }
+ return i64(hi)<<32 + i64(dw_ptr), nil
+}
+
+_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
+
+ // TODO(bill): should this be moved to `_File` instead?
+ BUF_SIZE :: 386
+ buf16: [BUF_SIZE]u16
+ buf8: [4*BUF_SIZE]u8
+
+ for n < len(b) && err == nil {
+ min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
+ max_read := u32(min(BUF_SIZE, min_read))
+ if max_read == 0 {
+ break
+ }
+
+ single_read_length: u32
+ ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
+ if !ok {
+ err = _get_platform_error()
+ }
+
+ buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
+ src := buf8[:buf8_len]
+
+ ctrl_z := false
+ for i := 0; i < len(src) && n+i < len(b); i += 1 {
+ x := src[i]
+ if x == 0x1a { // ctrl-z
+ ctrl_z = true
+ break
+ }
+ b[n] = x
+ n += 1
+ }
+ if ctrl_z || single_read_length < max_read {
+ break
+ }
+
+ // NOTE(bill): if the last two values were a newline, then it is expected that
+ // this is the end of the input
+ if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
+ break
+ }
+ }
+
+ return
+ }
+
+ handle := _handle(f)
+
+ single_read_length: win32.DWORD
+ total_read: int
+ length := len(p)
+
+ sync.shared_guard(&f.impl.rw_mutex) // multiple readers
+
+ if sync.guard(&f.impl.p_mutex) {
+ to_read := min(win32.DWORD(length), MAX_RW)
+ ok: win32.BOOL
+ if f.impl.kind == .Console {
+ n, err := read_console(handle, p[total_read:][:to_read])
+ total_read += n
+ if err != nil {
+ return int(total_read), err
+ }
+ } else {
+ ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
+ }
+
+ if single_read_length > 0 && ok {
+ total_read += int(single_read_length)
+ } else {
+ err = _get_platform_error()
+ }
+ }
+
+ return int(total_read), nil
+}
+
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+ buf := data
+ if len(buf) > MAX_RW {
+ buf = buf[:MAX_RW]
+
+ }
+ curr_offset := seek(f, offset, .Current) or_return
+ defer seek(f, curr_offset, .Start)
+
+ o := win32.OVERLAPPED{
+ OffsetHigh = u32(offset>>32),
+ Offset = u32(offset),
+ }
+
+ // TODO(bill): Determine the correct behaviour for consoles
+
+ h := _handle(f)
+ done: win32.DWORD
+ if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
+ err = _get_platform_error()
+ done = 0
+ }
+ n = int(done)
+ return
+ }
+
+ sync.guard(&f.impl.p_mutex)
+
+ p, offset := p, offset
+ for len(p) > 0 {
+ m := pread(f, p, offset) or_return
+ n += m
+ p = p[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
+ // TODO(bill)
+ return
+}
+
+_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+ if len(p) == 0 {
+ return
+ }
+
+ single_write_length: win32.DWORD
+ total_write: i64
+ length := i64(len(p))
+
+ handle := _handle(f)
+
+ sync.guard(&f.impl.rw_mutex)
+ for total_write < length {
+ remaining := length - total_write
+ to_write := win32.DWORD(min(i32(remaining), MAX_RW))
+
+ e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
+ if single_write_length <= 0 || !e {
+ n = int(total_write)
+ err = _get_platform_error()
+ return
+ }
+ total_write += i64(single_write_length)
+ }
+ return int(total_write), nil
+}
+
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+ pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+ buf := data
+ if len(buf) > MAX_RW {
+ buf = buf[:MAX_RW]
+
+ }
+ curr_offset := seek(f, offset, .Current) or_return
+ defer seek(f, curr_offset, .Start)
+
+ o := win32.OVERLAPPED{
+ OffsetHigh = u32(offset>>32),
+ Offset = u32(offset),
+ }
+
+ h := _handle(f)
+ done: win32.DWORD
+ if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
+ err = _get_platform_error()
+ done = 0
+ }
+ n = int(done)
+ return
+ }
+
+ sync.guard(&f.impl.p_mutex)
+ p, offset := p, offset
+ for len(p) > 0 {
+ m := pwrite(f, p, offset) or_return
+ n += m
+ p = p[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
+ // TODO(bill)
+ return
+}
+
+_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
+ length: win32.LARGE_INTEGER
+ handle := _handle(f)
+ if !win32.GetFileSizeEx(handle, &length) {
+ err = _get_platform_error()
+ }
+ n = i64(length)
+ return
+}
+
+
+_sync :: proc(f: ^File) -> Error {
+ return _flush(f)
+}
+
+_flush :: proc(f: ^File) -> Error {
+ handle := _handle(f)
+ if !win32.FlushFileBuffers(handle) {
+ return _get_platform_error()
+ }
return nil
}
-_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
+_truncate :: proc(f: ^File, size: i64) -> Error {
+ if f == nil {
+ return nil
+ }
+ curr_off := seek(f, 0, .Current) or_return
+ defer seek(f, curr_off, .Start)
+ seek(f, size, .Start) or_return
+ handle := _handle(f)
+ if !win32.SetEndOfFile(handle) {
+ return _get_platform_error()
+ }
return nil
}
-_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
+_remove :: proc(name: string) -> Error {
+ p := _fix_long_path(name)
+ err, err1: Error
+ if !win32.DeleteFileW(p) {
+ err = _get_platform_error()
+ }
+ if err == nil {
+ return nil
+ }
+ if !win32.RemoveDirectoryW(p) {
+ err1 = _get_platform_error()
+ }
+ if err1 == nil {
+ return nil
+ }
+
+ if err != err1 {
+ a := win32.GetFileAttributesW(p)
+ if a == ~u32(0) {
+ err = _get_platform_error()
+ } else {
+ if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ err = err1
+ } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
+ if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
+ err = nil
+ if !win32.DeleteFileW(p) {
+ err = _get_platform_error()
+ }
+ }
+ }
+ }
+ }
+
+ return err
+}
+
+_rename :: proc(old_path, new_path: string) -> Error {
+ from := _fix_long_path(old_path)
+ to := _fix_long_path(new_path)
+ if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
+ return nil
+ }
+ return _get_platform_error()
+
+}
+
+
+_link :: proc(old_name, new_name: string) -> Error {
+ o := _fix_long_path(old_name)
+ n := _fix_long_path(new_name)
+ if win32.CreateHardLinkW(n, o, nil) {
+ return nil
+ }
+ return _get_platform_error()
+}
+
+_symlink :: proc(old_name, new_name: string) -> Error {
+ return .Unsupported
+}
+
+_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
+ attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
+ attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
+ handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
+ if handle == win32.INVALID_HANDLE {
+ return nil, _get_platform_error()
+ }
+ return
+
+}
+
+_normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
+ has_prefix :: proc(p: []u16, str: string) -> bool {
+ if len(p) < len(str) {
+ return false
+ }
+ // assume ascii
+ for i in 0.. bool {
+ return has_prefix(p, `\??\`)
+ }
+
+ if !has_unc_prefix(p) {
+ return win32.utf16_to_utf8(p, allocator)
+ }
+
+ ws := p[4:]
+ switch {
+ case len(ws) >= 2 && ws[1] == ':':
+ return win32.utf16_to_utf8(ws, allocator)
+ case has_prefix(ws, `UNC\`):
+ ws[3] = '\\' // override data in buffer
+ return win32.utf16_to_utf8(ws[3:], allocator)
+ }
+
+
+ handle := _open_sym_link(raw_data(p)) or_return
+ defer win32.CloseHandle(handle)
+
+ n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
+ if n == 0 {
+ return "", _get_platform_error()
+ }
+ buf := make([]u16, n+1, _temp_allocator())
+ n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
+ if n == 0 {
+ return "", _get_platform_error()
+ }
+
+ ws = buf[:n]
+ if has_unc_prefix(ws) {
+ ws = ws[4:]
+ if len(ws) > 3 && has_prefix(ws, `UNC`) {
+ ws[2] = '\\'
+ return win32.utf16_to_utf8(ws[2:], allocator)
+ }
+ return win32.utf16_to_utf8(ws, allocator)
+ }
+ return "", .Invalid_Path
+}
+
+_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
+
+ @thread_local
+ rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
+
+ p := _fix_long_path(name)
+ handle := _open_sym_link(p) or_return
+ defer win32.CloseHandle(handle)
+
+ bytes_returned: u32
+ if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
+ err = _get_platform_error()
+ return
+ }
+ mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
+
+
+ rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
+ switch rdb.ReparseTag {
+ case win32.IO_REPARSE_TAG_SYMLINK:
+ rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
+ pb := win32.wstring(&rb.PathBuffer)
+ pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
+ p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
+ if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
+ return win32.utf16_to_utf8(p, allocator)
+ }
+ return _normalize_link_path(p, allocator)
+
+ case win32.IO_REPARSE_TAG_MOUNT_POINT:
+ rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
+ pb := win32.wstring(&rb.PathBuffer)
+ pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
+ p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
+ return _normalize_link_path(p, allocator)
+ }
+ // Path wasn't a symlink/junction but another reparse point kind
return "", nil
}
-_chdir :: proc(fd: Handle) -> Error {
+_fchdir :: proc(f: ^File) -> Error {
+ if f == nil {
+ return nil
+ }
+ if !win32.SetCurrentDirectoryW(f.impl.wname) {
+ return _get_platform_error()
+ }
return nil
}
-_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
+_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
+ if f == nil {
+ return nil
+ }
+ d: win32.BY_HANDLE_FILE_INFORMATION
+ if !win32.GetFileInformationByHandle(_handle(f), &d) {
+ return _get_platform_error()
+ }
+ attrs := d.dwFileAttributes
+ if mode & S_IWRITE != 0 {
+ attrs &~= win32.FILE_ATTRIBUTE_READONLY
+ } else {
+ attrs |= win32.FILE_ATTRIBUTE_READONLY
+ }
+
+ info: win32.FILE_BASIC_INFO
+ info.FileAttributes = attrs
+ if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
+ return _get_platform_error()
+ }
return nil
}
-_chown :: proc(fd: Handle, uid, gid: int) -> Error {
+_fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_chdir :: proc(name: string) -> Error {
+ p := _fix_long_path(name)
+ if !win32.SetCurrentDirectoryW(p) {
+ return _get_platform_error()
+ }
return nil
}
+_chmod :: proc(name: string, mode: File_Mode) -> Error {
+ f := open(name, {.Write}) or_return
+ defer close(f)
+ return _fchmod(f, mode)
+}
+
+_chown :: proc(name: string, uid, gid: int) -> Error {
+ return .Unsupported
+}
_lchown :: proc(name: string, uid, gid: int) -> Error {
- return nil
+ return .Unsupported
}
-_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
+_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
+ f := open(name, {.Write}) or_return
+ defer close(f)
+ return _fchtimes(f, atime, mtime)
+}
+_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ if f == nil {
+ return nil
+ }
+ d: win32.BY_HANDLE_FILE_INFORMATION
+ if !win32.GetFileInformationByHandle(_handle(f), &d) {
+ return _get_platform_error()
+ }
+
+ to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER {
+ // a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
+ return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000)
+ }
+
+ atime, mtime := atime, mtime
+ if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
+ atime = mtime
+ }
+
+ info: win32.FILE_BASIC_INFO
+ info.LastAccessTime = to_windows_time(atime)
+ info.LastWriteTime = to_windows_time(mtime)
+ if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
+ return _get_platform_error()
+ }
return nil
}
+
_exists :: proc(path: string) -> bool {
- return false
+ wpath := _fix_long_path(path)
+ attribs := win32.GetFileAttributesW(wpath)
+ return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
}
_is_file :: proc(path: string) -> bool {
+ wpath := _fix_long_path(path)
+ attribs := win32.GetFileAttributesW(wpath)
+ if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
+ return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
+ }
return false
}
_is_dir :: proc(path: string) -> bool {
+ wpath := _fix_long_path(path)
+ attribs := win32.GetFileAttributesW(wpath)
+ if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
+ return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
+ }
return false
}
-
-
-_path_error_delete :: proc(perr: Maybe(Path_Error)) {
-
-}
-
-_link_error_delete :: proc(lerr: Maybe(Link_Error)) {
-
-}
diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin
new file mode 100644
index 000000000..5fdad5329
--- /dev/null
+++ b/core/os/os2/heap_linux.odin
@@ -0,0 +1,722 @@
+//+private
+package os2
+
+import "core:sys/unix"
+import "core:sync"
+import "core:mem"
+
+// NOTEs
+//
+// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region
+// consists of a Region_Header and the memory that will be divided into allocations to
+// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes.
+// Allocation_Headers are used to navigate the memory in the region. The "next" member of
+// the Allocation_Header points to the next header, and the space between the headers
+// can be used to send to the user. This space between is referred to as "blocks" in the
+// code. The indexes in the header refer to these blocks instead of bytes. This allows us
+// to index all the memory in the region with a u16.
+//
+// When an allocation request is made, it will use the first free block that can contain
+// the entire block. If there is an excess number of blocks (as specified by the constant
+// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list.
+//
+// To keep the implementation simple, there can never exist 2 free blocks adjacent to each
+// other. Any freeing will result in attempting to merge the blocks before and after the
+// newly free'd blocks.
+//
+// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation
+// getting its own individual mmap. Individual mmaps will still get an Allocation_Header
+// that contains the size with the last bit set to 1 to indicate it is indeed a direct
+// mmap allocation.
+
+// Why not brk?
+// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation
+// does *not* utilize the brk system call to avoid possible conflicts with foreign C
+// code. Just because we aren't directly using libc, there is nothing stopping the user
+// from doing it.
+
+// What's with all the #no_bounds_check?
+// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere
+// until that region is written to by *you*. So, when a new region is created, we call mmap
+// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the
+// region itself is never formally initialized by the compiler as this would result in writing
+// zeros to memory that we can already assume are 0. This would also have the effect of
+// actually commiting this data to memory whether it gets used or not.
+
+
+//
+// Some variables to play with
+//
+
+// Minimum blocks used for any one allocation
+MINIMUM_BLOCK_COUNT :: 2
+
+// Number of extra blocks beyond the requested amount where we would segment.
+// E.g. (blocks) |H0123456| 7 available
+// |H01H0123| Ask for 2, now 4 available
+BLOCK_SEGMENT_THRESHOLD :: 4
+
+// Anything above this threshold will get its own memory map. Since regions
+// are indexed by 16 bit integers, this value should not surpass max(u16) * 6
+DIRECT_MMAP_THRESHOLD_USER :: int(max(u16))
+
+// The point at which we convert direct mmap to region. This should be a decent
+// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions.
+MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4
+
+// free_list is dynamic and is initialized in the begining of the region memory
+// when the region is initialized. Once resized, it can be moved anywhere.
+FREE_LIST_DEFAULT_CAP :: 32
+
+
+//
+// Other constants that should not be touched
+//
+
+// This universally seems to be 4096 outside of uncommon archs.
+PAGE_SIZE :: 4096
+
+// just rounding up to nearest PAGE_SIZE
+DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE
+
+// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well
+// as end right on a page boundary as to not waste space.
+SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE)
+
+// size of user memory blocks
+BLOCK_SIZE :: size_of(Allocation_Header)
+
+// number of allocation sections (call them blocks) of the region used for allocations
+BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE)
+
+// minimum amount of space that can used by any individual allocation (includes header)
+MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE
+
+// This is used as a boolean value for Region_Header.local_addr.
+CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
+
+FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
+
+MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
+MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
+
+
+@thread_local _local_region: ^Region
+global_regions: ^Region
+
+
+// There is no way of correctly setting the last bit of free_idx or
+// the last bit of requested, so we can safely use it as a flag to
+// determine if we are interacting with a direct mmap.
+REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF
+IS_DIRECT_MMAP :: 0x8000000000000000
+
+// Special free_idx value that does not index the free_list.
+NOT_FREE :: 0x7FFF
+Allocation_Header :: struct #raw_union {
+ using _: struct {
+ // Block indicies
+ idx: u16,
+ prev: u16,
+ next: u16,
+ free_idx: u16,
+ },
+ requested: u64,
+}
+
+Region_Header :: struct #align 16 {
+ next_region: ^Region, // points to next region in global_heap (linked list)
+ local_addr: ^^Region, // tracks region ownership via address of _local_region
+ reset_addr: ^^Region, // tracks old local addr for reset
+ free_list: []u16,
+ free_list_len: u16,
+ free_blocks: u16, // number of free blocks in region (includes headers)
+ last_used: u16, // farthest back block that has been used (need zeroing?)
+ _reserved: u16,
+}
+
+Region :: struct {
+ hdr: Region_Header,
+ memory: [BLOCKS_PER_REGION]Allocation_Header,
+}
+
+_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
+ size, alignment: int,
+ old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
+ //
+ // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
+ // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
+ // padding. We also store the original pointer returned by heap_alloc right before
+ // the pointer we return to the user.
+ //
+
+ aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
+ a := max(alignment, align_of(rawptr))
+ space := size + a - 1
+
+ allocated_mem: rawptr
+ if old_ptr != nil {
+ original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
+ allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
+ } else {
+ allocated_mem = heap_alloc(space+size_of(rawptr))
+ }
+ aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
+
+ ptr := uintptr(aligned_mem)
+ aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
+ diff := int(aligned_ptr - ptr)
+ if (size + diff) > space {
+ return nil, .Out_Of_Memory
+ }
+
+ aligned_mem = rawptr(aligned_ptr)
+ mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
+
+ return mem.byte_slice(aligned_mem, size), nil
+ }
+
+ aligned_free :: proc(p: rawptr) {
+ if p != nil {
+ heap_free(mem.ptr_offset((^rawptr)(p), -1)^)
+ }
+ }
+
+ aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
+ if p == nil {
+ return nil, nil
+ }
+
+ return aligned_alloc(new_size, new_alignment, p)
+ }
+
+ switch mode {
+ case .Alloc:
+ return aligned_alloc(size, alignment)
+
+ case .Free:
+ aligned_free(old_memory)
+
+ case .Free_All:
+ return nil, .Mode_Not_Implemented
+
+ case .Resize:
+ if old_memory == nil {
+ return aligned_alloc(size, alignment)
+ }
+ return aligned_resize(old_memory, old_size, size, alignment)
+
+ case .Query_Features:
+ set := (^mem.Allocator_Mode_Set)(old_memory)
+ if set != nil {
+ set^ = {.Alloc, .Free, .Resize, .Query_Features}
+ }
+ return nil, nil
+
+ case .Query_Info:
+ return nil, .Mode_Not_Implemented
+ }
+
+ return nil, nil
+}
+
+heap_alloc :: proc(size: int) -> rawptr {
+ if size >= DIRECT_MMAP_THRESHOLD {
+ return _direct_mmap_alloc(size)
+ }
+
+ // atomically check if the local region has been stolen
+ if _local_region != nil {
+ res := sync.atomic_compare_exchange_strong_explicit(
+ &_local_region.hdr.local_addr,
+ &_local_region,
+ CURRENTLY_ACTIVE,
+ .Acquire,
+ .Relaxed,
+ )
+ if res != &_local_region {
+ // At this point, the region has been stolen and res contains the unexpected value
+ expected := res
+ if res != CURRENTLY_ACTIVE {
+ expected = res
+ res = sync.atomic_compare_exchange_strong_explicit(
+ &_local_region.hdr.local_addr,
+ expected,
+ CURRENTLY_ACTIVE,
+ .Acquire,
+ .Relaxed,
+ )
+ }
+ if res != expected {
+ _local_region = nil
+ }
+ }
+ }
+
+ size := size
+ size = _round_up_to_nearest(size, BLOCK_SIZE)
+ blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE))
+
+ // retrieve a region if new thread or stolen
+ if _local_region == nil {
+ _local_region, _ = _region_retrieve_with_space(blocks_needed)
+ if _local_region == nil {
+ return nil
+ }
+ }
+ defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+
+ // At this point we have a usable region. Let's find the user some memory
+ idx: u16
+ local_region_idx := _region_get_local_idx()
+ back_idx := -1
+ infinite: for {
+ for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 {
+ idx = _local_region.hdr.free_list[i]
+ #no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed {
+ break infinite
+ }
+ }
+ sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+ _local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx)
+ }
+ user_ptr, used := _region_get_block(_local_region, idx, blocks_needed)
+ _local_region.hdr.free_blocks -= (used + 1)
+
+ // If this memory was ever used before, it now needs to be zero'd.
+ if idx < _local_region.hdr.last_used {
+ mem.zero(user_ptr, int(used) * BLOCK_SIZE)
+ } else {
+ _local_region.hdr.last_used = idx + used
+ }
+
+ return user_ptr
+}
+
+heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check {
+ alloc := _get_allocation_header(old_memory)
+ if alloc.requested & IS_DIRECT_MMAP > 0 {
+ return _direct_mmap_resize(alloc, new_size)
+ }
+
+ if new_size > DIRECT_MMAP_THRESHOLD {
+ return _direct_mmap_from_region(alloc, new_size)
+ }
+
+ return _region_resize(alloc, new_size)
+}
+
+heap_free :: proc(memory: rawptr) {
+ alloc := _get_allocation_header(memory)
+ if alloc.requested & IS_DIRECT_MMAP == IS_DIRECT_MMAP {
+ _direct_mmap_free(alloc)
+ return
+ }
+
+ assert(alloc.free_idx == NOT_FREE)
+
+ _region_find_and_assign_local(alloc)
+ _region_local_free(alloc)
+ sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+}
+
+//
+// Regions
+//
+_new_region :: proc() -> ^Region #no_bounds_check {
+ res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
+ if res < 0 {
+ return nil
+ }
+ new_region := (^Region)(uintptr(res))
+
+ new_region.hdr.local_addr = CURRENTLY_ACTIVE
+ new_region.hdr.reset_addr = &_local_region
+
+ free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK)
+ _region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK)
+
+ // + 2 to account for free_list's allocation header
+ first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2
+
+ // first allocation header (this is a free list)
+ new_region.memory[0].next = u16(first_user_block)
+ new_region.memory[0].free_idx = NOT_FREE
+ new_region.memory[first_user_block].idx = u16(first_user_block)
+ new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1
+
+ // add the first user block to the free list
+ new_region.hdr.free_list[0] = u16(first_user_block)
+ new_region.hdr.free_list_len = 1
+ new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1
+
+ for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region);
+ r != nil;
+ r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {}
+
+ return new_region
+}
+
+_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check {
+ assert(alloc.free_idx == NOT_FREE)
+
+ old_memory := mem.ptr_offset(alloc, 1)
+
+ old_block_count := _get_block_count(alloc^)
+ new_block_count := u16(
+ max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE),
+ )
+ if new_block_count < old_block_count {
+ if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT {
+ _region_find_and_assign_local(alloc)
+ _region_segment(_local_region, alloc, new_block_count, alloc.free_idx)
+ new_block_count = _get_block_count(alloc^)
+ sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+ }
+ // need to zero anything within the new block that that lies beyond new_size
+ extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size
+ extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE)
+ mem.zero(extra_bytes_ptr, extra_bytes)
+ return old_memory
+ }
+
+ if !alloc_is_free_list {
+ _region_find_and_assign_local(alloc)
+ }
+ defer if !alloc_is_free_list {
+ sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+ }
+
+ // First, let's see if we can grow in place.
+ if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
+ next_alloc := _local_region.memory[alloc.next]
+ total_available := old_block_count + _get_block_count(next_alloc) + 1
+ if total_available >= new_block_count {
+ alloc.next = next_alloc.next
+ _local_region.memory[alloc.next].prev = alloc.idx
+ if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD {
+ _region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx)
+ } else {
+ _region_free_list_remove(_local_region, next_alloc.free_idx)
+ }
+ mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE)
+ _local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used)
+ _local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count)
+ if alloc_is_free_list {
+ _region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^))
+ }
+ return old_memory
+ }
+ }
+
+ // If we made it this far, we need to resize, copy, zero and free.
+ region_iter := _local_region
+ local_region_idx := _region_get_local_idx()
+ back_idx := -1
+ idx: u16
+ infinite: for {
+ for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
+ idx = region_iter.hdr.free_list[i]
+ if _get_block_count(region_iter.memory[idx]) >= new_block_count {
+ break infinite
+ }
+ }
+ if region_iter != _local_region {
+ sync.atomic_store_explicit(
+ ®ion_iter.hdr.local_addr,
+ region_iter.hdr.reset_addr,
+ .Release,
+ )
+ }
+ region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx)
+ }
+ if region_iter != _local_region {
+ sync.atomic_store_explicit(
+ ®ion_iter.hdr.local_addr,
+ region_iter.hdr.reset_addr,
+ .Release,
+ )
+ }
+
+ // copy from old memory
+ new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count)
+ mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE))
+
+ // zero any new memory
+ addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count)
+ new_blocks := used_blocks - old_block_count
+ mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE)
+
+ region_iter.hdr.free_blocks -= (used_blocks + 1)
+
+ // Set free_list before freeing.
+ if alloc_is_free_list {
+ _region_assign_free_list(_local_region, new_memory, used_blocks)
+ }
+
+ // free old memory
+ _region_local_free(alloc)
+ return new_memory
+}
+
+_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check {
+ alloc := alloc
+ add_to_free_list := true
+
+ _local_region.hdr.free_blocks += _get_block_count(alloc^) + 1
+
+ // try to merge with prev
+ if alloc.idx > 0 && _local_region.memory[alloc.prev].free_idx != NOT_FREE {
+ _local_region.memory[alloc.prev].next = alloc.next
+ _local_region.memory[alloc.next].prev = alloc.prev
+ alloc = &_local_region.memory[alloc.prev]
+ add_to_free_list = false
+ }
+
+ // try to merge with next
+ if alloc.next < BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
+ old_next := alloc.next
+ alloc.next = _local_region.memory[old_next].next
+ _local_region.memory[alloc.next].prev = alloc.idx
+
+ if add_to_free_list {
+ _local_region.hdr.free_list[_local_region.memory[old_next].free_idx] = alloc.idx
+ alloc.free_idx = _local_region.memory[old_next].free_idx
+ } else {
+ // NOTE: We have aleady merged with prev, and now merged with next.
+ // Now, we are actually going to remove from the free_list.
+ _region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx)
+ }
+ add_to_free_list = false
+ }
+
+ // This is the only place where anything is appended to the free list.
+ if add_to_free_list {
+ fl := _local_region.hdr.free_list
+ alloc.free_idx = _local_region.hdr.free_list_len
+ fl[alloc.free_idx] = alloc.idx
+ _local_region.hdr.free_list_len += 1
+ if int(_local_region.hdr.free_list_len) == len(fl) {
+ free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list))
+ _region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true)
+ }
+ }
+}
+
+_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) {
+ raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list
+ raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK
+ raw_free_list.data = memory
+ region.hdr.free_list = transmute([]u16)(raw_free_list)
+}
+
+_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) {
+ r: ^Region
+ idx: int
+ for r = global_regions; r != nil; r = r.hdr.next_region {
+ if idx == local_idx || idx < back_idx || r.hdr.free_blocks < blocks {
+ idx += 1
+ continue
+ }
+ idx += 1
+ local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr)
+ if local_addr != CURRENTLY_ACTIVE {
+ res := sync.atomic_compare_exchange_strong_explicit(
+ &r.hdr.local_addr,
+ local_addr,
+ CURRENTLY_ACTIVE,
+ .Acquire,
+ .Relaxed,
+ )
+ if res == local_addr {
+ r.hdr.reset_addr = local_addr
+ return r, idx
+ }
+ }
+ }
+
+ return _new_region(), idx
+}
+
+_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region {
+ r: ^Region
+ for r = global_regions; r != nil; r = r.hdr.next_region {
+ if _region_contains_mem(r, addr) {
+ return r
+ }
+ }
+ unreachable()
+}
+
+_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check {
+ alloc := ®ion.memory[idx]
+
+ assert(alloc.free_idx != NOT_FREE)
+ assert(alloc.next > 0)
+
+ block_count := _get_block_count(alloc^)
+ if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD {
+ _region_segment(region, alloc, blocks_needed, alloc.free_idx)
+ } else {
+ _region_free_list_remove(region, alloc.free_idx)
+ }
+
+ alloc.free_idx = NOT_FREE
+ return mem.ptr_offset(alloc, 1), _get_block_count(alloc^)
+}
+
+_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check {
+ old_next := alloc.next
+ alloc.next = alloc.idx + blocks + 1
+ region.memory[old_next].prev = alloc.next
+
+ // Initialize alloc.next allocation header here.
+ region.memory[alloc.next].prev = alloc.idx
+ region.memory[alloc.next].next = old_next
+ region.memory[alloc.next].idx = alloc.next
+ region.memory[alloc.next].free_idx = new_free_idx
+
+ // Replace our original spot in the free_list with new segment.
+ region.hdr.free_list[new_free_idx] = alloc.next
+}
+
+_region_get_local_idx :: proc() -> int {
+ idx: int
+ for r := global_regions; r != nil; r = r.hdr.next_region {
+ if r == _local_region {
+ return idx
+ }
+ idx += 1
+ }
+
+ return -1
+}
+
+_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) {
+ // Find the region that contains this memory
+ if !_region_contains_mem(_local_region, alloc) {
+ _local_region = _region_retrieve_from_addr(alloc)
+ }
+
+ // At this point, _local_region is set correctly. Spin until acquired
+ res: ^^Region
+ for res != &_local_region {
+ res = sync.atomic_compare_exchange_strong_explicit(
+ &_local_region.hdr.local_addr,
+ &_local_region,
+ CURRENTLY_ACTIVE,
+ .Acquire,
+ .Relaxed,
+ )
+ }
+}
+
+_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check {
+ if r == nil {
+ return false
+ }
+ mem_int := uintptr(memory)
+ return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1])
+}
+
+_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check {
+ // pop, swap and update allocation hdr
+ if n := region.hdr.free_list_len - 1; free_idx != n {
+ region.hdr.free_list[free_idx] = region.hdr.free_list[n]
+ alloc_idx := region.hdr.free_list[free_idx]
+ region.memory[alloc_idx].free_idx = free_idx
+ }
+ region.hdr.free_list_len -= 1
+}
+
+//
+// Direct mmap
+//
+_direct_mmap_alloc :: proc(size: int) -> rawptr {
+ mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
+ new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
+ if new_allocation < 0 && new_allocation > -4096 {
+ return nil
+ }
+
+ alloc := (^Allocation_Header)(uintptr(new_allocation))
+ alloc.requested = u64(size) // NOTE: requested = requested size
+ alloc.requested += IS_DIRECT_MMAP
+ return rawptr(mem.ptr_offset(alloc, 1))
+}
+
+_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
+ old_requested := int(alloc.requested & REQUESTED_MASK)
+ old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE)
+ new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE)
+ if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD {
+ return _direct_mmap_to_region(alloc, new_size)
+ } else if old_requested == new_size {
+ return mem.ptr_offset(alloc, 1)
+ }
+
+ new_allocation := unix.sys_mremap(
+ alloc,
+ uint(old_mmap_size),
+ uint(new_mmap_size),
+ unix.MREMAP_MAYMOVE,
+ )
+ if new_allocation < 0 && new_allocation > -4096 {
+ return nil
+ }
+
+ new_header := (^Allocation_Header)(uintptr(new_allocation))
+ new_header.requested = u64(new_size)
+ new_header.requested += IS_DIRECT_MMAP
+
+ if new_mmap_size > old_mmap_size {
+ // new section may not be pointer aligned, so cast to ^u8
+ new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE)
+ mem.zero(new_section, new_mmap_size - old_mmap_size)
+ }
+ return mem.ptr_offset(new_header, 1)
+
+}
+
+_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
+ new_memory := _direct_mmap_alloc(new_size)
+ if new_memory != nil {
+ old_memory := mem.ptr_offset(alloc, 1)
+ mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE)
+ }
+ _region_find_and_assign_local(alloc)
+ _region_local_free(alloc)
+ sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
+ return new_memory
+}
+
+_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
+ new_memory := heap_alloc(new_size)
+ if new_memory != nil {
+ mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size)
+ _direct_mmap_free(alloc)
+ }
+ return new_memory
+}
+
+_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
+ requested := int(alloc.requested & REQUESTED_MASK)
+ mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
+ unix.sys_munmap(alloc, uint(mmap_size))
+}
+
+//
+// Util
+//
+
+_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 {
+ return alloc.next - alloc.idx - 1
+}
+
+_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header {
+ return mem.ptr_offset((^Allocation_Header)(raw_mem), -1)
+}
+
+_round_up_to_nearest :: #force_inline proc(size, round: int) -> int {
+ return (size-1) + round - (size-1) % round
+}
diff --git a/core/os/os2/heap_windows.odin b/core/os/os2/heap_windows.odin
index 85ea2c56f..90f0ae110 100644
--- a/core/os/os2/heap_windows.odin
+++ b/core/os/os2/heap_windows.odin
@@ -99,7 +99,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return nil, nil
case .Query_Info:
- return nil, nil
+ return nil, .Mode_Not_Implemented
}
return nil, nil
diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin
index ee7d6e6f2..c27015862 100644
--- a/core/os/os2/path.odin
+++ b/core/os/os2/path.odin
@@ -1,5 +1,7 @@
package os2
+import "core:runtime"
+
Path_Separator :: _Path_Separator // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
@@ -7,21 +9,21 @@ is_path_separator :: proc(c: byte) -> bool {
return _is_path_separator(c)
}
-mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
+mkdir :: proc(name: string, perm: File_Mode) -> Error {
return _mkdir(name, perm)
}
-mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
+mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
return _mkdir_all(path, perm)
}
-remove_all :: proc(path: string) -> Maybe(Path_Error) {
+remove_all :: proc(path: string) -> Error {
return _remove_all(path)
}
-getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
+getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _getwd(allocator)
}
setwd :: proc(dir: string) -> (err: Error) {
diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin
new file mode 100644
index 000000000..2f59d1f13
--- /dev/null
+++ b/core/os/os2/path_linux.odin
@@ -0,0 +1,247 @@
+//+private
+package os2
+
+import "core:strings"
+import "core:strconv"
+import "core:runtime"
+import "core:sys/unix"
+
+_Path_Separator :: '/'
+_Path_List_Separator :: ':'
+
+_S_IFMT :: 0o170000 // Type of file mask
+_S_IFIFO :: 0o010000 // Named pipe (fifo)
+_S_IFCHR :: 0o020000 // Character special
+_S_IFDIR :: 0o040000 // Directory
+_S_IFBLK :: 0o060000 // Block special
+_S_IFREG :: 0o100000 // Regular
+_S_IFLNK :: 0o120000 // Symbolic link
+_S_IFSOCK :: 0o140000 // Socket
+
+_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
+
+_is_path_separator :: proc(c: byte) -> bool {
+ return c == '/'
+}
+
+_mkdir :: proc(path: string, perm: File_Mode) -> Error {
+ // NOTE: These modes would require sys_mknod, however, that would require
+ // additional arguments to this function.
+ if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
+ return .Invalid_Argument
+ }
+
+ path_cstr, allocated := _name_to_cstring(path)
+ defer if allocated {
+ delete(path_cstr)
+ }
+ return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777)))
+}
+
+_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
+ _mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
+ if len(path) == 0 {
+ return _ok_or_error(unix.sys_close(dfd))
+ }
+ i: int
+ for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
+ path[i] = 0
+ new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
+ switch new_dfd {
+ case -ENOENT:
+ if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 {
+ return _get_platform_error(res)
+ }
+ has_created^ = true
+ if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
+ return _get_platform_error(new_dfd)
+ }
+ fallthrough
+ case 0:
+ if res := unix.sys_close(dfd); res < 0 {
+ return _get_platform_error(res)
+ }
+ // skip consecutive '/'
+ for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
+ return _mkdirat(new_dfd, path[i:], perm, has_created)
+ case:
+ return _get_platform_error(new_dfd)
+ }
+ unreachable()
+ }
+
+ if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
+ return .Invalid_Argument
+ }
+
+ // need something we can edit, and use to generate cstrings
+ allocated: bool
+ path_bytes: []u8
+ if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
+ allocated = true
+ path_bytes = make([]u8, len(path) + 1)
+ } else {
+ path_bytes = make([]u8, len(path) + 1, context.temp_allocator)
+ }
+ defer if allocated {
+ delete(path_bytes)
+ }
+
+ // NULL terminate the byte slice to make it a valid cstring
+ copy(path_bytes, path)
+ path_bytes[len(path)] = 0
+
+ dfd: int
+ if path_bytes[0] == '/' {
+ dfd = unix.sys_open("/", _OPENDIR_FLAGS)
+ path_bytes = path_bytes[1:]
+ } else {
+ dfd = unix.sys_open(".", _OPENDIR_FLAGS)
+ }
+ if dfd < 0 {
+ return _get_platform_error(dfd)
+ }
+
+ has_created: bool
+ _mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
+ if has_created {
+ return nil
+ }
+ return .Exist
+ //return has_created ? nil : .Exist
+}
+
+dirent64 :: struct {
+ d_ino: u64,
+ d_off: u64,
+ d_reclen: u16,
+ d_type: u8,
+ d_name: [1]u8,
+}
+
+_remove_all :: proc(path: string) -> Error {
+ DT_DIR :: 4
+
+ _remove_all_dir :: proc(dfd: int) -> Error {
+ n := 64
+ buf := make([]u8, n)
+ defer delete(buf)
+
+ loop: for {
+ getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
+ switch getdents_res {
+ case -EINVAL:
+ delete(buf)
+ n *= 2
+ buf = make([]u8, n)
+ continue loop
+ case -4096..<0:
+ return _get_platform_error(getdents_res)
+ case 0:
+ break loop
+ }
+
+ d: ^dirent64
+
+ for i := 0; i < getdents_res; i += int(d.d_reclen) {
+ d = (^dirent64)(rawptr(&buf[i]))
+ d_name_cstr := cstring(&d.d_name[0])
+
+ buf_len := uintptr(d.d_reclen) - offset_of(d.d_name)
+
+ /* check for current directory (.) */
+ #no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 {
+ continue
+ }
+
+ /* check for parent directory (..) */
+ #no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 {
+ continue
+ }
+
+ unlink_res: int
+
+ switch d.d_type {
+ case DT_DIR:
+ new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
+ if new_dfd < 0 {
+ return _get_platform_error(new_dfd)
+ }
+ defer unix.sys_close(new_dfd)
+ _remove_all_dir(new_dfd) or_return
+ unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
+ case:
+ unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
+ }
+
+ if unlink_res < 0 {
+ return _get_platform_error(unlink_res)
+ }
+ }
+ }
+ return nil
+ }
+
+ path_cstr, allocated := _name_to_cstring(path)
+ defer if allocated {
+ delete(path_cstr)
+ }
+
+ fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
+ switch fd {
+ case -ENOTDIR:
+ return _ok_or_error(unix.sys_unlink(path_cstr))
+ case -4096..<0:
+ return _get_platform_error(fd)
+ }
+
+ defer unix.sys_close(fd)
+ _remove_all_dir(fd) or_return
+ return _ok_or_error(unix.sys_rmdir(path_cstr))
+}
+
+_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
+ // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
+ // an authoritative value for it across all systems.
+ // The largest value I could find was 4096, so might as well use the page size.
+ // NOTE(jason): Avoiding libc, so just use 4096 directly
+ PATH_MAX :: 4096
+ buf := make([dynamic]u8, PATH_MAX, allocator)
+ for {
+ #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
+ }
+ if res != -ERANGE {
+ return "", _get_platform_error(res)
+ }
+ resize(&buf, len(buf)+PATH_MAX)
+ }
+ unreachable()
+}
+
+_setwd :: proc(dir: string) -> Error {
+ dir_cstr, allocated := _name_to_cstring(dir)
+ defer if allocated {
+ delete(dir_cstr)
+ }
+ return _ok_or_error(unix.sys_chdir(dir_cstr))
+}
+
+_get_full_path :: proc(fd: int, allocator := context.allocator) -> string {
+ PROC_FD_PATH :: "/proc/self/fd/"
+
+ buf: [32]u8
+ copy(buf[:], PROC_FD_PATH)
+
+ strconv.itoa(buf[len(PROC_FD_PATH):], fd)
+
+ fullpath: string
+ err: Error
+ if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
+ return ""
+ }
+ return fullpath
+}
+
diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin
index 607f56968..2dc667822 100644
--- a/core/os/os2/path_windows.odin
+++ b/core/os/os2/path_windows.odin
@@ -1,6 +1,10 @@
//+private
package os2
+import win32 "core:sys/windows"
+import "core:runtime"
+import "core:strings"
+
_Path_Separator :: '\\'
_Path_List_Separator :: ';'
@@ -8,24 +12,151 @@ _is_path_separator :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
}
-_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
+_mkdir :: proc(name: string, perm: File_Mode) -> Error {
+ if !win32.CreateDirectoryW(_fix_long_path(name), nil) {
+ return _get_platform_error()
+ }
return nil
}
-_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
- // TODO(bill): _mkdir_all for windows
+_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
+ fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
+ if len(p) == len(`\\?\c:`) {
+ if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
+ s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return
+ allocated = true
+ return
+ }
+ }
+ return p, false, nil
+ }
+
+ dir, err := stat(path, _temp_allocator())
+ if err == nil {
+ if dir.is_dir {
+ return nil
+ }
+ return .Exist
+ }
+
+ i := len(path)
+ for i > 0 && is_path_separator(path[i-1]) {
+ i -= 1
+ }
+
+ j := i
+ for j > 0 && !is_path_separator(path[j-1]) {
+ j -= 1
+ }
+
+ if j > 1 {
+ new_path, allocated := fix_root_directory(path[:j-1]) or_return
+ defer if allocated {
+ delete(new_path, _file_allocator())
+ }
+ mkdir_all(new_path, perm) or_return
+ }
+
+ err = mkdir(path, perm)
+ if err != nil {
+ dir1, err1 := lstat(path, _temp_allocator())
+ if err1 == nil && dir1.is_dir {
+ return nil
+ }
+ return err
+ }
return nil
}
-_remove_all :: proc(path: string) -> Maybe(Path_Error) {
+_remove_all :: proc(path: string) -> Error {
// TODO(bill): _remove_all for windows
return nil
}
-_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
+_getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ // TODO(bill)
return "", nil
}
_setwd :: proc(dir: string) -> (err: Error) {
+ // TODO(bill)
return nil
}
+
+
+can_use_long_paths: bool
+
+@(init)
+init_long_path_support :: proc() {
+ // TODO(bill): init_long_path_support
+ // ADD THIS SHIT
+ // registry_path := win32.L(`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled`)
+ can_use_long_paths = false
+}
+
+
+_fix_long_path_slice :: proc(path: string) -> []u16 {
+ return win32.utf8_to_utf16(_fix_long_path_internal(path))
+}
+
+_fix_long_path :: proc(path: string) -> win32.wstring {
+ return win32.utf8_to_wstring(_fix_long_path_internal(path))
+}
+
+
+_fix_long_path_internal :: proc(path: string) -> string {
+ if can_use_long_paths {
+ return path
+ }
+
+ // When using win32 to create a directory, the path
+ // cannot be too long that you cannot append an 8.3
+ // file name, because MAX_PATH is 260, 260-12 = 248
+ if len(path) < 248 {
+ return path
+ }
+
+ // UNC paths do not need to be modified
+ if len(path) >= 2 && path[:2] == `\\` {
+ return path
+ }
+
+ if !_is_abs(path) { // relative path
+ return path
+ }
+
+ PREFIX :: `\\?`
+ path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator())
+ copy(path_buf, PREFIX)
+ n := len(path)
+ r, w := 0, len(PREFIX)
+ for r < n {
+ switch {
+ case is_path_separator(path[r]):
+ r += 1
+ case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
+ // \.\
+ r += 1
+ case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
+ // Skip \..\ paths
+ return path
+ case:
+ path_buf[w] = '\\'
+ w += 1
+ for r < n && !is_path_separator(path[r]) {
+ path_buf[w] = path[r]
+ r += 1
+ w += 1
+ }
+ }
+ }
+
+ // Root directories require a trailing \
+ if w == len(`\\?\c:`) {
+ path_buf[w] = '\\'
+ w += 1
+ }
+
+ return string(path_buf[:w])
+
+}
diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin
index c38f03f03..62f7ddf10 100644
--- a/core/os/os2/pipe.odin
+++ b/core/os/os2/pipe.odin
@@ -1,5 +1,5 @@
package os2
-pipe :: proc() -> (r, w: Handle, err: Error) {
+pipe :: proc() -> (r, w: ^File, err: Error) {
return _pipe()
}
diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin
new file mode 100644
index 000000000..b66ff9663
--- /dev/null
+++ b/core/os/os2/pipe_linux.odin
@@ -0,0 +1,7 @@
+//+private
+package os2
+
+_pipe :: proc() -> (r, w: ^File, err: Error) {
+ return nil, nil, nil
+}
+
diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin
index 5570ca282..bab8b44f5 100644
--- a/core/os/os2/pipe_windows.odin
+++ b/core/os/os2/pipe_windows.odin
@@ -3,11 +3,11 @@ package os2
import win32 "core:sys/windows"
-_pipe :: proc() -> (r, w: Handle, err: Error) {
+_pipe :: proc() -> (r, w: ^File, err: Error) {
p: [2]win32.HANDLE
if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
- return 0, 0, Platform_Error{i32(win32.GetLastError())}
+ return nil, nil, _get_platform_error()
}
- return Handle(p[0]), Handle(p[1]), nil
+ return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
}
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 028951fe3..db47e2f5b 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -1,6 +1,6 @@
package os2
-import sync "core:sync/sync2"
+import "core:sync"
import "core:time"
import "core:runtime"
@@ -46,7 +46,7 @@ Process :: struct {
Process_Attributes :: struct {
dir: string,
env: []string,
- files: []Handle,
+ files: []^File,
sys: ^Process_Attributes_OS_Specific,
}
diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin
index 19f1453ff..24a01fb0a 100644
--- a/core/os/os2/stat.odin
+++ b/core/os/os2/stat.odin
@@ -1,6 +1,7 @@
package os2
import "core:time"
+import "core:runtime"
File_Info :: struct {
fullpath: string,
@@ -13,26 +14,26 @@ File_Info :: struct {
access_time: time.Time,
}
-file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) {
+file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
for i := len(infos)-1; i >= 0; i -= 1 {
file_info_delete(infos[i], allocator)
}
delete(infos, allocator)
}
-file_info_delete :: proc(fi: File_Info, allocator := context.allocator) {
+file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
delete(fi.fullpath, allocator)
}
-fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
- return _fstat(fd, allocator)
+fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
+ return _fstat(f, allocator)
}
-stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
+stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return _stat(name, allocator)
}
-lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
+lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return _lstat(name, allocator)
}
diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin
new file mode 100644
index 000000000..b627cef15
--- /dev/null
+++ b/core/os/os2/stat_linux.odin
@@ -0,0 +1,152 @@
+//+private
+package os2
+
+import "core:time"
+import "core:runtime"
+import "core:sys/unix"
+import "core:path/filepath"
+
+// File type
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+
+// File mode
+// Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+ // Read, write, execute/search by group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
+
+ // Read, write, execute/search by others
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISVTX :: 0o1000 // Directory restrcted delete
+
+
+S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+
+F_OK :: 0 // Test for file existance
+X_OK :: 1 // Test for execute permission
+W_OK :: 2 // Test for write permission
+R_OK :: 4 // Test for read permission
+
+@private
+Unix_File_Time :: struct {
+ seconds: i64,
+ nanoseconds: i64,
+}
+
+@private
+_Stat :: struct {
+ device_id: u64, // ID of device containing file
+ serial: u64, // File serial number
+ nlink: u64, // Number of hard links
+ mode: u32, // Mode of the file
+ uid: u32, // User ID of the file's owner
+ gid: u32, // Group ID of the file's group
+ _padding: i32, // 32 bits of padding
+ rdev: u64, // Device ID, if device
+ size: i64, // Size of the file, in bytes
+ block_size: i64, // Optimal bllocksize for I/O
+ blocks: i64, // Number of 512-byte blocks allocated
+
+ last_access: Unix_File_Time, // Time of last access
+ modified: Unix_File_Time, // Time of last modification
+ status_change: Unix_File_Time, // Time of last status change
+
+ _reserve1,
+ _reserve2,
+ _reserve3: i64,
+}
+
+
+_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
+ return _fstat_internal(f.impl.fd, allocator)
+}
+
+_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
+ s: _Stat
+ result := unix.sys_fstat(fd, &s)
+ if result < 0 {
+ return {}, _get_platform_error(result)
+ }
+
+ // TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
+ fi := File_Info {
+ fullpath = _get_full_path(fd, allocator),
+ name = "",
+ size = s.size,
+ mode = 0,
+ is_dir = S_ISDIR(s.mode),
+ modification_time = time.Time {s.modified.seconds},
+ access_time = time.Time {s.last_access.seconds},
+ creation_time = time.Time{0}, // regular stat does not provide this
+ }
+
+ fi.name = filepath.base(fi.fullpath)
+ return fi, nil
+}
+
+// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
+_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+
+ fd := unix.sys_open(name_cstr, _O_RDONLY)
+ if fd < 0 {
+ return {}, _get_platform_error(fd)
+ }
+ defer unix.sys_close(fd)
+ return _fstat_internal(fd, allocator)
+}
+
+_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
+ if fd < 0 {
+ return {}, _get_platform_error(fd)
+ }
+ defer unix.sys_close(fd)
+ return _fstat_internal(fd, allocator)
+}
+
+_same_file :: proc(fi1, fi2: File_Info) -> bool {
+ return fi1.fullpath == fi2.fullpath
+}
+
+_stat_internal :: proc(name: string) -> (s: _Stat, res: int) {
+ name_cstr, allocated := _name_to_cstring(name)
+ defer if allocated {
+ delete(name_cstr)
+ }
+ res = unix.sys_stat(name_cstr, &s)
+ return
+}
diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin
index f46a9435c..5de5269d7 100644
--- a/core/os/os2/stat_windows.odin
+++ b/core/os/os2/stat_windows.odin
@@ -1,21 +1,22 @@
//+private
package os2
+import "core:runtime"
import "core:time"
+import "core:strings"
import win32 "core:sys/windows"
-_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
- if fd == 0 {
- return {}, Path_Error{err = .Invalid_Argument}
+_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
+ if f == nil || f.impl.fd == nil {
+ return {}, nil
}
- context.allocator = allocator
- path, err := _cleanpath_from_handle(fd)
+ path, err := _cleanpath_from_handle(f, allocator)
if err != nil {
return {}, err
}
- h := win32.HANDLE(fd)
+ h := _handle(f)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi: File_Info
@@ -25,13 +26,13 @@ _fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Maybe(
return fi, nil
}
- return _file_info_from_get_file_information_by_handle(path, h)
+ return _file_info_from_get_file_information_by_handle(path, h, allocator)
}
-_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
- return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS)
+_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
+ return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator)
}
-_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Maybe(Path_Error)) {
- return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT)
+_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
+ return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator)
}
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
@@ -39,50 +40,38 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
-_stat_errno :: proc(errno: win32.DWORD) -> Path_Error {
- return Path_Error{err = Platform_Error{i32(errno)}}
-}
-
-full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Maybe(Path_Error)) {
- context.allocator = allocator
-
+full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
name := name
if name == "" {
name = "."
}
- p := win32.utf8_to_utf16(name, context.temp_allocator)
- buf := make([dynamic]u16, 100)
- for {
- n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
- if n == 0 {
- delete(buf)
- return "", _stat_errno(win32.GetLastError())
- }
- if n <= u32(len(buf)) {
- return win32.utf16_to_utf8(buf[:n]), nil
- }
- resize(&buf, len(buf)*2)
- }
+ p := win32.utf8_to_utf16(name, _temp_allocator())
- return
+ n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
+ if n == 0 {
+ return "", _get_platform_error()
+ }
+ buf := make([]u16, n+1, _temp_allocator())
+ n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
+ if n == 0 {
+ return "", _get_platform_error()
+ }
+ return win32.utf16_to_utf8(buf[:n], allocator)
}
-internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Maybe(Path_Error)) {
+internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
if len(name) == 0 {
- return {}, Path_Error{err = .Not_Exist}
+ return {}, .Not_Exist
}
- context.allocator = allocator
-
-
- wname := win32.utf8_to_wstring(_fix_long_path(name), context.temp_allocator)
+ wname := _fix_long_path(name)
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
- return _file_info_from_win32_file_attribute_data(&fa, name)
+ return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
}
err := 0 if ok else win32.GetLastError()
@@ -91,21 +80,21 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
fd: win32.WIN32_FIND_DATAW
sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE {
- e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}
+ e = _get_platform_error()
return
}
win32.FindClose(sh)
- return _file_info_from_win32_find_data(&fd, name)
+ return _file_info_from_win32_find_data(&fd, name, allocator)
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE {
- e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}}
+ e = _get_platform_error()
return
}
defer win32.CloseHandle(h)
- return _file_info_from_get_file_information_by_handle(name, h)
+ return _file_info_from_get_file_information_by_handle(name, h, allocator)
}
@@ -130,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
}
-_cleanpath_from_handle :: proc(fd: Handle) -> (string, Maybe(Path_Error)) {
- if fd == 0 {
- return "", Path_Error{err = .Invalid_Argument}
+_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
+ if f == nil || f.impl.fd == nil {
+ return "", nil
}
- h := win32.HANDLE(fd)
+ h := _handle(f)
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch err {
- case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER:
- return "", _stat_errno(err)
- case win32.ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
+ n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
+ if n == 0 {
+ return "", _get_platform_error()
}
- return _cleanpath_from_buf(buf), nil
+ buf := make([]u16, max(n, 260)+1, _temp_allocator())
+ n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
+ return _cleanpath_from_buf(buf[:n], allocator)
}
-_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Maybe(Path_Error)) {
- if fd == 0 {
- return nil, Path_Error{err = .Invalid_Argument}
+_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
+ if f == nil || f.impl.fd == nil {
+ return nil, nil
}
- h := win32.HANDLE(fd)
+ h := _handle(f)
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch err {
- case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER:
- return nil, _stat_errno(err)
- case win32.ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
+ n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
+ if n == 0 {
+ return nil, _get_platform_error()
}
- return _cleanpath_strip_prefix(buf), nil
+ buf := make([]u16, max(n, 260)+1, _temp_allocator())
+ n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
+ return _cleanpath_strip_prefix(buf[:n]), nil
}
-_cleanpath_from_buf :: proc(buf: []u16) -> string {
+_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
buf := buf
buf = _cleanpath_strip_prefix(buf)
- return win32.utf16_to_utf8(buf, context.allocator)
+ return win32.utf16_to_utf8(buf, allocator)
}
@@ -221,15 +194,15 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
-_file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
- if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
+_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
+ if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444
} else {
mode |= 0o666
}
is_sym := false
- if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
+ if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
is_sym = false
} else {
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
@@ -238,7 +211,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
if is_sym {
mode |= File_Mode_Sym_Link
} else {
- if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
mode |= 0o111 | File_Mode_Dir
}
@@ -251,7 +224,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
}
-_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) {
+_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
@@ -261,14 +234,14 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
- fi.fullpath, e = full_path_from_name(name)
+ fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath)
return
}
-_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Maybe(Path_Error)) {
+_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
@@ -278,17 +251,17 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
- fi.fullpath, e = full_path_from_name(name)
+ fi.fullpath, e = full_path_from_name(name, allocator)
fi.name = basename(fi.fullpath)
return
}
-_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Maybe(Path_Error)) {
+_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) {
d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) {
- return {}, _stat_errno(win32.GetLastError())
+ return {}, _get_platform_error()
}
@@ -296,7 +269,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := win32.GetLastError()
if err != win32.ERROR_INVALID_PARAMETER {
- return {}, _stat_errno(err)
+ return {}, Platform_Error(err)
}
// Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0
@@ -318,58 +291,83 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
return fi, nil
}
-_is_abs :: proc(path: string) -> bool {
- if len(path) > 0 && path[0] == '/' {
- return true
+
+
+reserved_names := [?]string{
+ "CON", "PRN", "AUX", "NUL",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+}
+
+_is_reserved_name :: proc(path: string) -> bool {
+ if len(path) == 0 {
+ return false
}
- if len(path) > 2 {
- switch path[0] {
- case 'A'..='Z', 'a'..='z':
- return path[1] == ':' && is_path_separator(path[2])
+ for reserved in reserved_names {
+ if strings.equal_fold(path, reserved) {
+ return true
}
}
return false
}
-_fix_long_path :: proc(path: string) -> string {
- if len(path) < 248 {
- return path
- }
+_is_UNC :: proc(path: string) -> bool {
+ return _volume_name_len(path) > 2
+}
- if len(path) >= 2 && path[:2] == `\\` {
- return path
- }
- if !_is_abs(path) {
- return path
- }
+_volume_name_len :: proc(path: string) -> int {
+ if ODIN_OS == .Windows {
+ if len(path) < 2 {
+ return 0
+ }
+ c := path[0]
+ if path[1] == ':' {
+ switch c {
+ case 'a'..='z', 'A'..='Z':
+ return 2
+ }
+ }
- prefix :: `\\?`
-
- path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
- copy(path_buf, prefix)
- n := len(path)
- r, w := 0, len(prefix)
- for r < n {
- switch {
- case is_path_separator(path[r]):
- r += 1
- case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
- r += 1
- case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
- return path
- case:
- path_buf[w] = '\\'
- w += 1
- for ; r < n && !is_path_separator(path[r]); r += 1 {
- path_buf[w] = path[r]
- w += 1
+ // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) &&
+ !_is_path_separator(path[2]) && path[2] != '.' {
+ for n := 3; n < l-1; n += 1 {
+ if _is_path_separator(path[n]) {
+ n += 1
+ if !_is_path_separator(path[n]) {
+ if path[n] == '.' {
+ break
+ }
+ }
+ for ; n < l; n += 1 {
+ if _is_path_separator(path[n]) {
+ break
+ }
+ }
+ return n
+ }
+ break
}
}
}
-
- if w == len(`\\?\c:`) {
- path_buf[w] = '\\'
- w += 1
- }
- return string(path_buf[:w])
+ return 0
}
+
+
+_is_abs :: proc(path: string) -> bool {
+ if _is_reserved_name(path) {
+ return true
+ }
+ l := _volume_name_len(path)
+ if l == 0 {
+ return false
+ }
+
+ path := path
+ path = path[l:]
+ if path == "" {
+ return false
+ }
+ return is_path_separator(path[0])
+}
+
diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin
index 8ff0e1656..b05c186a0 100644
--- a/core/os/os2/temp_file.odin
+++ b/core/os/os2/temp_file.odin
@@ -1,14 +1,15 @@
package os2
+import "core:runtime"
-create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
+create_temp :: proc(dir, pattern: string) -> (^File, Error) {
return _create_temp(dir, pattern)
}
-mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
- return _mkdir_temp(dir, pattern)
+mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
+ return _mkdir_temp(dir, pattern, allocator)
}
-temp_dir :: proc(allocator := context.allocator) -> string {
+temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
return _temp_dir(allocator)
}
diff --git a/core/os/os2/temp_file_linux.odin b/core/os/os2/temp_file_linux.odin
new file mode 100644
index 000000000..201fb0e93
--- /dev/null
+++ b/core/os/os2/temp_file_linux.odin
@@ -0,0 +1,20 @@
+//+private
+package os2
+
+import "core:runtime"
+
+
+_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
+ //TODO
+ return nil, nil
+}
+
+_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
+ //TODO
+ return "", nil
+}
+
+_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
+ //TODO
+ return "", nil
+}
diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin
index 43f9f43b4..08837f7f0 100644
--- a/core/os/os2/temp_file_windows.odin
+++ b/core/os/os2/temp_file_windows.odin
@@ -1,29 +1,29 @@
//+private
package os2
+import "core:runtime"
import win32 "core:sys/windows"
-_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
- return 0, nil
+_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
+ return nil, nil
}
-_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
+_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
return "", nil
}
-_temp_dir :: proc(allocator := context.allocator) -> string {
- b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator)
- for {
- n := win32.GetTempPathW(u32(len(b)), raw_data(b))
- if n > u32(len(b)) {
- resize(&b, int(n))
- continue
- }
- if n == 3 && b[1] == ':' && b[2] == '\\' {
-
- } else if n > 0 && b[n-1] == '\\' {
- n -= 1
- }
- return win32.utf16_to_utf8(b[:n], allocator)
+_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
+ n := win32.GetTempPathW(0, nil)
+ if n == 0 {
+ return "", nil
}
+ b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator())
+ n = win32.GetTempPathW(u32(len(b)), raw_data(b))
+
+ if n == 3 && b[1] == ':' && b[2] == '\\' {
+
+ } else if n > 0 && b[n-1] == '\\' {
+ n -= 1
+ }
+ return win32.utf16_to_utf8(b[:n], allocator)
}
diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin
index 6dd99c621..1fb653b85 100644
--- a/core/os/os2/user.odin
+++ b/core/os/os2/user.odin
@@ -1,18 +1,19 @@
package os2
import "core:strings"
+import "core:runtime"
-user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
- switch ODIN_OS {
- case "windows":
+user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ #partial switch ODIN_OS {
+ case .Windows:
dir = get_env("LocalAppData")
if dir != "" {
- dir = strings.clone(dir, allocator)
+ dir = strings.clone_safe(dir, allocator) or_return
}
- case "darwin":
+ case .Darwin:
dir = get_env("HOME")
if dir != "" {
- dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
+ dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME")
@@ -21,24 +22,26 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin
if dir == "" {
return
}
- dir = strings.concatenate({dir, "/.cache"}, allocator)
+ dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return
}
}
- is_defined = dir != ""
+ if dir == "" {
+ err = .Invalid_Path
+ }
return
}
-user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
- switch ODIN_OS {
- case "windows":
+user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ #partial switch ODIN_OS {
+ case .Windows:
dir = get_env("AppData")
if dir != "" {
- dir = strings.clone(dir, allocator)
+ dir = strings.clone_safe(dir, allocator) or_return
}
- case "darwin":
+ case .Darwin:
dir = get_env("HOME")
if dir != "" {
- dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
+ dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME")
@@ -47,22 +50,24 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi
if dir == "" {
return
}
- dir = strings.concatenate({dir, "/.config"}, allocator)
+ dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return
}
}
- is_defined = dir != ""
+ if dir == "" {
+ err = .Invalid_Path
+ }
return
}
-user_home_dir :: proc() -> (dir: string, is_defined: bool) {
+user_home_dir :: proc() -> (dir: string, err: Error) {
env := "HOME"
- switch ODIN_OS {
- case "windows":
+ #partial switch ODIN_OS {
+ case .Windows:
env = "USERPROFILE"
}
if v := get_env(env); v != "" {
- return v, true
+ return v, nil
}
- return "", false
+ return "", .Invalid_Path
}
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index d40c80aeb..b5e67558c 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -163,9 +163,6 @@ O_SYNC :: 0x0080
O_ASYNC :: 0x0040
O_CLOEXEC :: 0x1000000
-SEEK_SET :: 0
-SEEK_CUR :: 1
-SEEK_END :: 2
SEEK_DATA :: 3
SEEK_HOLE :: 4
SEEK_MAX :: SEEK_HOLE
@@ -260,13 +257,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete
-S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
-S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
-S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
-S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
-S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
-S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
-S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+S_ISLNK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFSOCK }
R_OK :: 4 // Test for read permission
W_OK :: 2 // Test for write permission
@@ -279,7 +276,7 @@ foreign libc {
@(link_name="__error") __error :: proc() -> ^int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
- @(link_name="close") _unix_close :: proc(handle: Handle) ---
+ @(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
@@ -290,11 +287,21 @@ foreign libc {
@(link_name="fstat64") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
@(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---
- @(link_name="fdopendir$INODE64") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+
+ @(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir ---
+ @(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+ @(link_name="fdopendir") _unix_fdopendir_arm64 :: proc(fd: Handle) -> Dir ---
+ @(link_name="readdir_r") _unix_readdir_r_arm64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
- @(link_name="readdir_r$INODE64") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
- @(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
+
+ @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
+
+ @(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
+ @(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
+
+ @(link_name="fchmod") _unix_fchmod :: proc(fd: Handle, mode: u16) -> c.int ---
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
@@ -303,11 +310,22 @@ foreign libc {
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+ @(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
+
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
+when ODIN_ARCH != .arm64 {
+ _unix_fdopendir :: proc {_unix_fdopendir_amd64}
+ _unix_readdir_r :: proc {_unix_readdir_r_amd64}
+} else {
+ _unix_fdopendir :: proc {_unix_fdopendir_arm64}
+ _unix_readdir_r :: proc {_unix_readdir_r_arm64}
+}
+
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
@@ -319,18 +337,36 @@ get_last_error :: proc() -> int {
return __error()^
}
-open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path)
+get_last_error_string :: proc() -> string {
+ return cast(string)_darwin_string_error(cast(c.int)get_last_error())
+}
+
+open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, i32(flags), u16(mode))
- delete(cstr)
if handle == -1 {
return INVALID_HANDLE, 1
}
+
+when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
+ if mode != 0 {
+ err := fchmod(handle, cast(u16)mode)
+ if err != 0 {
+ _unix_close(handle)
+ return INVALID_HANDLE, 1
+ }
+ }
+}
+
return handle, 0
}
-close :: proc(fd: Handle) {
- _unix_close(fd)
+fchmod :: proc(fd: Handle, mode: u16) -> Errno {
+ return cast(Errno)_unix_fchmod(fd, mode)
+}
+
+close :: proc(fd: Handle) -> bool {
+ return _unix_close(fd) == 0
}
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
@@ -389,6 +425,70 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+exists :: proc(path: string) -> bool {
+ cpath := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cpath, O_RDONLY)
+ return res == 0
+}
+
+rename :: proc(old: string, new: string) -> bool {
+ old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
+ new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
+ return _unix_rename(old_cstr, new_cstr) != -1
+}
+
+remove :: proc(path: string) -> bool {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _unix_remove(path_cstr) != -1
+}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@@ -451,7 +551,7 @@ _rewinddir :: proc(dirp: Dir) {
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result)
-
+
if rc != 0 {
err = Errno(get_last_error())
return
@@ -491,7 +591,7 @@ _readlink :: proc(path: string) -> (string, Errno) {
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
buf : [256]byte
- res := _unix_fcntl(fd, F_GETPATH, &buf[0])
+ res := _unix__fcntl(fd, F_GETPATH, &buf[0])
if res != 0 {
return "", Errno(get_last_error())
}
@@ -530,19 +630,26 @@ heap_alloc :: proc(size: int) -> rawptr {
return _unix_calloc(1, size)
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
return _unix_realloc(ptr, new_size)
}
heap_free :: proc(ptr: rawptr) {
_unix_free(ptr)
}
-getenv :: proc(name: string) -> (string, bool) {
- path_str := strings.clone_to_cstring(name, context.temp_allocator)
+lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
}
- return string(cstr), true
+ return strings.clone(string(cstr), allocator), true
+}
+
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
}
get_current_directory :: proc() -> string {
@@ -570,7 +677,17 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
return ERROR_NONE
}
+make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
_unix_exit(i32(code))
}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index e9314b468..adf4f246f 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -7,465 +7,700 @@ import "core:runtime"
import "core:strings"
import "core:c"
-Handle :: distinct i32;
-File_Time :: distinct u64;
-Errno :: distinct i32;
-Syscall :: distinct i32;
+Handle :: distinct i32
+File_Time :: distinct u64
+Errno :: distinct i32
-INVALID_HANDLE :: ~Handle(0);
+INVALID_HANDLE :: ~Handle(0)
-ERROR_NONE: Errno : 0;
-EPERM: Errno : 1;
-ENOENT: Errno : 2;
-ESRCH: Errno : 3;
-EINTR: Errno : 4;
-EIO: Errno : 5;
-ENXIO: Errno : 6;
-E2BIG: Errno : 7;
-ENOEXEC: Errno : 8;
-EBADF: Errno : 9;
-ECHILD: Errno : 10;
-EBEADLK: Errno : 11;
-ENOMEM: Errno : 12;
-EACCESS: Errno : 13;
-EFAULT: Errno : 14;
-ENOTBLK: Errno : 15;
-EBUSY: Errno : 16;
-EEXIST: Errno : 17;
-EXDEV: Errno : 18;
-ENODEV: Errno : 19;
-ENOTDIR: Errno : 20;
-EISDIR: Errno : 21;
-EINVAL: Errno : 22;
-ENFILE: Errno : 23;
-EMFILE: Errno : 24;
-ENOTTY: Errno : 25;
-ETXTBSY: Errno : 26;
-EFBIG: Errno : 27;
-ENOSPC: Errno : 28;
-ESPIPE: Errno : 29;
-EROFS: Errno : 30;
-EMLINK: Errno : 31;
-EPIPE: Errno : 32;
-EDOM: Errno : 33;
-ERANGE: Errno : 34; /* Result too large */
-EAGAIN: Errno : 35;
-EINPROGRESS: Errno : 36;
-EALREADY: Errno : 37;
-ENOTSOCK: Errno : 38;
-EDESTADDRREQ: Errno : 39;
-EMSGSIZE: Errno : 40;
-EPROTOTYPE: Errno : 41;
-ENOPROTOOPT: Errno : 42;
-EPROTONOSUPPORT: Errno : 43;
-ESOCKTNOSUPPORT: Errno : 44;
-EOPNOTSUPP: Errno : 45;
-EPFNOSUPPORT: Errno : 46;
-EAFNOSUPPORT: Errno : 47;
-EADDRINUSE: Errno : 48;
-EADDRNOTAVAIL: Errno : 49;
-ENETDOWN: Errno : 50;
-ENETUNREACH: Errno : 51;
-ENETRESET: Errno : 52;
-ECONNABORTED: Errno : 53;
-ECONNRESET: Errno : 54;
-ENOBUFS: Errno : 55;
-EISCONN: Errno : 56;
-ENOTCONN: Errno : 57;
-ESHUTDOWN: Errno : 58;
-ETIMEDOUT: Errno : 60;
-ECONNREFUSED: Errno : 61;
-ELOOP: Errno : 62;
-ENAMETOOLING: Errno : 63;
-EHOSTDOWN: Errno : 64;
-EHOSTUNREACH: Errno : 65;
-ENOTEMPTY: Errno : 66;
-EPROCLIM: Errno : 67;
-EUSERS: Errno : 68;
-EDQUOT: Errno : 69;
-ESTALE: Errno : 70;
-EBADRPC: Errno : 72;
-ERPCMISMATCH: Errno : 73;
-EPROGUNAVAIL: Errno : 74;
-EPROGMISMATCH: Errno : 75;
-EPROCUNAVAIL: Errno : 76;
-ENOLCK: Errno : 77;
-ENOSYS: Errno : 78;
-EFTYPE: Errno : 79;
-EAUTH: Errno : 80;
-ENEEDAUTH: Errno : 81;
-EIDRM: Errno : 82;
-ENOMSG: Errno : 83;
-EOVERFLOW: Errno : 84;
-ECANCELED: Errno : 85;
-EILSEQ: Errno : 86;
-ENOATTR: Errno : 87;
-EDOOFUS: Errno : 88;
-EBADMSG: Errno : 89;
-EMULTIHOP: Errno : 90;
-ENOLINK: Errno : 91;
-EPROTO: Errno : 92;
-ENOTCAPABLE: Errno : 93;
-ECAPMODE: Errno : 94;
-ENOTRECOVERABLE: Errno : 95;
-EOWNERDEAD: Errno : 96;
+ERROR_NONE: Errno : 0
+EPERM: Errno : 1
+ENOENT: Errno : 2
+ESRCH: Errno : 3
+EINTR: Errno : 4
+EIO: Errno : 5
+ENXIO: Errno : 6
+E2BIG: Errno : 7
+ENOEXEC: Errno : 8
+EBADF: Errno : 9
+ECHILD: Errno : 10
+EBEADLK: Errno : 11
+ENOMEM: Errno : 12
+EACCESS: Errno : 13
+EFAULT: Errno : 14
+ENOTBLK: Errno : 15
+EBUSY: Errno : 16
+EEXIST: Errno : 17
+EXDEV: Errno : 18
+ENODEV: Errno : 19
+ENOTDIR: Errno : 20
+EISDIR: Errno : 21
+EINVAL: Errno : 22
+ENFILE: Errno : 23
+EMFILE: Errno : 24
+ENOTTY: Errno : 25
+ETXTBSY: Errno : 26
+EFBIG: Errno : 27
+ENOSPC: Errno : 28
+ESPIPE: Errno : 29
+EROFS: Errno : 30
+EMLINK: Errno : 31
+EPIPE: Errno : 32
+EDOM: Errno : 33
+ERANGE: Errno : 34 /* Result too large */
+EAGAIN: Errno : 35
+EINPROGRESS: Errno : 36
+EALREADY: Errno : 37
+ENOTSOCK: Errno : 38
+EDESTADDRREQ: Errno : 39
+EMSGSIZE: Errno : 40
+EPROTOTYPE: Errno : 41
+ENOPROTOOPT: Errno : 42
+EPROTONOSUPPORT: Errno : 43
+ESOCKTNOSUPPORT: Errno : 44
+EOPNOTSUPP: Errno : 45
+EPFNOSUPPORT: Errno : 46
+EAFNOSUPPORT: Errno : 47
+EADDRINUSE: Errno : 48
+EADDRNOTAVAIL: Errno : 49
+ENETDOWN: Errno : 50
+ENETUNREACH: Errno : 51
+ENETRESET: Errno : 52
+ECONNABORTED: Errno : 53
+ECONNRESET: Errno : 54
+ENOBUFS: Errno : 55
+EISCONN: Errno : 56
+ENOTCONN: Errno : 57
+ESHUTDOWN: Errno : 58
+ETIMEDOUT: Errno : 60
+ECONNREFUSED: Errno : 61
+ELOOP: Errno : 62
+ENAMETOOLING: Errno : 63
+EHOSTDOWN: Errno : 64
+EHOSTUNREACH: Errno : 65
+ENOTEMPTY: Errno : 66
+EPROCLIM: Errno : 67
+EUSERS: Errno : 68
+EDQUOT: Errno : 69
+ESTALE: Errno : 70
+EBADRPC: Errno : 72
+ERPCMISMATCH: Errno : 73
+EPROGUNAVAIL: Errno : 74
+EPROGMISMATCH: Errno : 75
+EPROCUNAVAIL: Errno : 76
+ENOLCK: Errno : 77
+ENOSYS: Errno : 78
+EFTYPE: Errno : 79
+EAUTH: Errno : 80
+ENEEDAUTH: Errno : 81
+EIDRM: Errno : 82
+ENOMSG: Errno : 83
+EOVERFLOW: Errno : 84
+ECANCELED: Errno : 85
+EILSEQ: Errno : 86
+ENOATTR: Errno : 87
+EDOOFUS: Errno : 88
+EBADMSG: Errno : 89
+EMULTIHOP: Errno : 90
+ENOLINK: Errno : 91
+EPROTO: Errno : 92
+ENOTCAPABLE: Errno : 93
+ECAPMODE: Errno : 94
+ENOTRECOVERABLE: Errno : 95
+EOWNERDEAD: Errno : 96
-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;
+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
-SEEK_SET :: 0;
-SEEK_CUR :: 1;
-SEEK_END :: 2;
-SEEK_DATA :: 3;
-SEEK_HOLE :: 4;
-SEEK_MAX :: SEEK_HOLE;
+SEEK_DATA :: 3
+SEEK_HOLE :: 4
+SEEK_MAX :: SEEK_HOLE
// NOTE: These are OS specific!
// Do not mix these up!
-RTLD_LAZY :: 0x001;
-RTLD_NOW :: 0x002;
-//RTLD_BINDING_MASK :: 0x3; // Called MODEMASK in dlfcn.h
-RTLD_GLOBAL :: 0x100;
-RTLD_LOCAL :: 0x000;
-RTLD_TRACE :: 0x200;
-RTLD_NODELETE :: 0x01000;
-RTLD_NOLOAD :: 0x02000;
+RTLD_LAZY :: 0x001
+RTLD_NOW :: 0x002
+//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h
+RTLD_GLOBAL :: 0x100
+RTLD_LOCAL :: 0x000
+RTLD_TRACE :: 0x200
+RTLD_NODELETE :: 0x01000
+RTLD_NOLOAD :: 0x02000
-args := _alloc_command_line_arguments();
+MAX_PATH :: 1024
+
+args := _alloc_command_line_arguments()
Unix_File_Time :: struct {
- seconds: i64,
+ seconds: time_t,
nanoseconds: c.long,
}
-pid_t :: u32;
+dev_t :: u64
+ino_t :: u64
+nlink_t :: u64
+off_t :: i64
+mode_t :: u16
+pid_t :: u32
+uid_t :: u32
+gid_t :: u32
+blkcnt_t :: i64
+blksize_t :: i32
+fflags_t :: u32
+
+when ODIN_ARCH == .amd64 /* LP64 */ {
+ time_t :: i64
+} else {
+ time_t :: i32
+}
+
OS_Stat :: struct {
- device_id: u64,
- serial: u64,
- nlink: u64,
- mode: u32,
+ device_id: dev_t,
+ serial: ino_t,
+ nlink: nlink_t,
+ mode: mode_t,
_padding0: i16,
- uid: u32,
- gid: u32,
+ uid: uid_t,
+ gid: gid_t,
_padding1: i32,
- rdev: u64,
+ rdev: dev_t,
last_access: Unix_File_Time,
modified: Unix_File_Time,
status_change: Unix_File_Time,
birthtime: Unix_File_Time,
- size: i64,
- blocks: i64,
- block_size: i32,
+ size: off_t,
+ blocks: blkcnt_t,
+ block_size: blksize_t,
- flags: u32,
+ flags: fflags_t,
gen: u64,
- lspare: i64,
+ lspare: [10]u64,
}
+
+// since FreeBSD v12
+Dirent :: struct {
+ ino: ino_t,
+ off: off_t,
+ reclen: u16,
+ type: u8,
+ _pad0: u8,
+ namlen: u16,
+ _pad1: u16,
+ name: [256]byte,
+}
+
+Dir :: distinct rawptr // DIR*
+
// File type
-S_IFMT :: 0o170000; // Type of file mask
-S_IFIFO :: 0o010000; // Named pipe (fifo)
-S_IFCHR :: 0o020000; // Character special
-S_IFDIR :: 0o040000; // Directory
-S_IFBLK :: 0o060000; // Block special
-S_IFREG :: 0o100000; // Regular
-S_IFLNK :: 0o120000; // Symbolic link
-S_IFSOCK :: 0o140000; // Socket
-//S_ISVTX :: 0o001000; // Save swapped text even after use
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+//S_ISVTX :: 0o001000 // Save swapped text even after use
// File mode
// Read, write, execute/search by owner
-S_IRWXU :: 0o0700; // RWX mask for owner
-S_IRUSR :: 0o0400; // R for owner
-S_IWUSR :: 0o0200; // W for owner
-S_IXUSR :: 0o0100; // X for owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
// Read, write, execute/search by group
-S_IRWXG :: 0o0070; // RWX mask for group
-S_IRGRP :: 0o0040; // R for group
-S_IWGRP :: 0o0020; // W for group
-S_IXGRP :: 0o0010; // X for group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
// Read, write, execute/search by others
-S_IRWXO :: 0o0007; // RWX mask for other
-S_IROTH :: 0o0004; // R for other
-S_IWOTH :: 0o0002; // W for other
-S_IXOTH :: 0o0001; // X for other
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
-S_ISUID :: 0o4000; // Set user id on execution
-S_ISGID :: 0o2000; // Set group id on execution
-S_ISVTX :: 0o1000; // Directory restrcted delete
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISVTX :: 0o1000 // Directory restrcted delete
-S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
-S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
-S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
-S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
-S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
-S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
-S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
+S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
-F_OK :: 0; // Test for file existance
-X_OK :: 1; // Test for execute permission
-W_OK :: 2; // Test for write permission
-R_OK :: 4; // Test for read permission
+F_OK :: 0 // Test for file existance
+X_OK :: 1 // Test for execute permission
+W_OK :: 2 // Test for write permission
+R_OK :: 4 // Test for read permission
foreign libc {
- @(link_name="__error") __errno_location :: proc() -> ^int ---;
- @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
+ @(link_name="__error") __errno_location :: proc() -> ^int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
- @(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---;
- @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---;
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---;
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---;
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+
+ @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+ @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
+ @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---;
- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---;
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
foreign dl {
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
- @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
- @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
- @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---;
+ @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
}
is_path_separator :: proc(r: rune) -> bool {
- return r == '/';
+ return r == '/'
}
get_last_error :: proc() -> int {
- return __errno_location()^;
+ return __errno_location()^
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path);
- handle := _unix_open(cstr, c.int(flags), c.int(mode));
- delete(cstr);
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
- return INVALID_HANDLE, Errno(get_last_error());
+ return INVALID_HANDLE, Errno(get_last_error())
}
- return handle, ERROR_NONE;
+ return handle, ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- result := _unix_close(fd);
+ result := _unix_close(fd)
if result == -1 {
- return Errno(get_last_error());
+ return Errno(get_last_error())
}
- return ERROR_NONE;
+ return ERROR_NONE
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
- bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
+ bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
if bytes_read == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return int(bytes_read), ERROR_NONE;
+ return int(bytes_read), ERROR_NONE
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
- return 0, ERROR_NONE;
+ return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
+ bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
if bytes_written == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return int(bytes_written), ERROR_NONE;
+ return int(bytes_written), ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, c.int(whence));
+ res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return res, ERROR_NONE;
+ return res, ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
- s, err := fstat(fd);
+ s, err := fstat(fd)
if err != ERROR_NONE {
- return -1, err;
+ return -1, err
}
- return s.size, ERROR_NONE;
+ return s.size, ERROR_NONE
}
-stdin: Handle = 0;
-stdout: Handle = 1;
-stderr: Handle = 2;
-
-last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
- s, err := fstat(fd);
- if err != ERROR_NONE {
- return 0, err;
+rename :: proc(old_path, new_path: string) -> Errno {
+ old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
+ new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
+ res := _unix_rename(old_path_cstr, new_path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
}
- modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
- return File_Time(modified), ERROR_NONE;
+ return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_unlink(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_rmdir(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!
+last_write_time :: proc(fd: Handle) -> File_Time {}
+last_write_time_by_name :: proc(name: string) -> File_Time {}
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
}
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
- s, err := stat(name);
+ s, err := _stat(name)
if err != ERROR_NONE {
- return 0, err;
+ return 0, err
}
- modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
- return File_Time(modified), ERROR_NONE;
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
}
-stat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path);
- defer delete(cstr);
-
- s: OS_Stat;
- result := _unix_stat(cstr, &s);
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ s: OS_Stat = ---
+ result := _unix_lstat(cstr, &s)
if result == -1 {
- return s, Errno(get_last_error());
+ return s, Errno(get_last_error())
}
- return s, ERROR_NONE;
+ return s, ERROR_NONE
}
-fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
- s: OS_Stat;
- result := _unix_fstat(fd, &s);
- if result == -1 {
- return s, Errno(get_last_error());
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_lstat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
}
- return s, ERROR_NONE;
+ return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+ s: OS_Stat = ---
+ result := _unix_fstat(fd, &s)
+ if result == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+ dirp := _unix_fdopendir(fd)
+ if dirp == cast(Dir)nil {
+ return nil, Errno(get_last_error())
+ }
+ return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+ rc := _unix_closedir(dirp)
+ if rc != 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+ _unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+ result: ^Dirent
+ rc := _unix_readdir_r(dirp, &entry, &result)
+
+ if rc != 0 {
+ err = Errno(get_last_error())
+ return
+ }
+ err = ERROR_NONE
+
+ if result == nil {
+ end_of_stream = true
+ return
+ }
+
+ return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ bufsz : uint = MAX_PATH
+ buf := make([]byte, MAX_PATH)
+ for {
+ rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ if rc == -1 {
+ delete(buf)
+ return "", Errno(get_last_error())
+ } else if rc == int(bufsz) {
+ bufsz += MAX_PATH
+ delete(buf)
+ buf = make([]byte, bufsz)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+ }
+ }
+ unreachable()
+}
+
+// XXX FreeBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+ return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+ rel := rel
+ if rel == "" {
+ rel = "."
+ }
+
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+ path_ptr := _unix_realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Errno(get_last_error())
+ }
+ defer _unix_free(path_ptr)
+
+ path_cstr := transmute(cstring)path_ptr
+ path = strings.clone( string(path_cstr) )
+
+ return path, ERROR_NONE
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
- cstr := strings.clone_to_cstring(path);
- defer delete(cstr);
- result := _unix_access(cstr, c.int(mask));
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ result := _unix_access(cstr, c.int(mask))
if result == -1 {
- return false, Errno(get_last_error());
+ return false, Errno(get_last_error())
}
- return true, ERROR_NONE;
+ return true, ERROR_NONE
}
heap_alloc :: proc(size: int) -> rawptr {
- assert(size >= 0);
- return _unix_calloc(1, c.size_t(size));
+ assert(size >= 0)
+ return _unix_calloc(1, c.size_t(size))
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
- return _unix_realloc(ptr, c.size_t(new_size));
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
+ return _unix_realloc(ptr, c.size_t(new_size))
}
heap_free :: proc(ptr: rawptr) {
- _unix_free(ptr);
+ _unix_free(ptr)
}
-getenv :: proc(name: string) -> (string, bool) {
- path_str := strings.clone_to_cstring(name);
- defer delete(path_str);
- cstr := _unix_getenv(path_str);
+lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ cstr := _unix_getenv(path_str)
if cstr == nil {
- return "", false;
+ return "", false
}
- return string(cstr), true;
+ return strings.clone(string(cstr), allocator), true
+}
+
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
}
get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems.
// The largest value I could find was 4096, so might as well use the page size.
- page_size := get_page_size();
- buf := make([dynamic]u8, page_size);
+ page_size := get_page_size()
+ buf := make([dynamic]u8, page_size)
#no_bounds_check for {
- cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)));
+ cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
if cwd != nil {
- return string(cwd);
+ return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
- return "";
+ return ""
}
- resize(&buf, len(buf)+page_size);
+ resize(&buf, len(buf)+page_size)
}
- unreachable();
+ unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
- cstr := strings.clone_to_cstring(path, context.temp_allocator);
- res := _unix_chdir(cstr);
- if res == -1 do return Errno(get_last_error());
- return ERROR_NONE;
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_chdir(cstr)
+ if res == -1 do return Errno(get_last_error())
+ return ERROR_NONE
}
exit :: proc "contextless" (code: int) -> ! {
- _unix_exit(c.int(code));
+ runtime._cleanup_runtime_contextless()
+ _unix_exit(c.int(code))
}
current_thread_id :: proc "contextless" () -> int {
- return cast(int) pthread_getthreadid_np();
+ return cast(int) pthread_getthreadid_np()
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
- cstr := strings.clone_to_cstring(filename);
- defer delete(cstr);
- handle := _unix_dlopen(cstr, c.int(flags));
- return handle;
+ cstr := strings.clone_to_cstring(filename, context.temp_allocator)
+ handle := _unix_dlopen(cstr, c.int(flags))
+ return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
- assert(handle != nil);
- cstr := strings.clone_to_cstring(symbol);
- defer delete(cstr);
- proc_handle := _unix_dlsym(handle, cstr);
- return proc_handle;
+ assert(handle != nil)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+ proc_handle := _unix_dlsym(handle, cstr)
+ return proc_handle
}
dlclose :: proc(handle: rawptr) -> bool {
- assert(handle != nil);
- return _unix_dlclose(handle) == 0;
+ assert(handle != nil)
+ return _unix_dlclose(handle) == 0
}
dlerror :: proc() -> string {
- return string(_unix_dlerror());
+ return string(_unix_dlerror())
}
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
- @static page_size := -1;
- if page_size != -1 do return page_size;
+ @static page_size := -1
+ if page_size != -1 do return page_size
- page_size = int(_unix_getpagesize());
- return page_size;
+ page_size = int(_unix_getpagesize())
+ return page_size
}
_alloc_command_line_arguments :: proc() -> []string {
- res := make([]string, len(runtime.args__));
+ res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
- res[i] = string(arg);
+ res[i] = string(arg)
}
- return res;
+ return res
}
-
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index bc4717b44..de3a22187 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -8,8 +8,10 @@ import "core:strings"
import "core:c"
import "core:strconv"
import "core:intrinsics"
+import "core:sys/unix"
Handle :: distinct i32
+Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
@@ -149,6 +151,8 @@ ERFKILL: Errno : 132 /* Operation not possible due to RF-kill */
EHWPOISON: Errno : 133 /* Memory page has hardware error */
+ADDR_NO_RANDOMIZE :: 0x40000
+
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
O_RDWR :: 0x00002
@@ -163,9 +167,6 @@ O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000
-SEEK_SET :: 0
-SEEK_CUR :: 1
-SEEK_END :: 2
SEEK_DATA :: 3
SEEK_HOLE :: 4
SEEK_MAX :: SEEK_HOLE
@@ -265,35 +266,156 @@ X_OK :: 1 // Test for execute permission
W_OK :: 2 // Test for write permission
R_OK :: 4 // Test for read permission
-SYS_GETTID :: 186
+AT_FDCWD :: ~uintptr(99) /* -100 */
+AT_REMOVEDIR :: uintptr(0x200)
+AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
+
+_unix_personality :: proc(persona: u64) -> int {
+ return int(intrinsics.syscall(unix.SYS_personality, uintptr(persona)))
+}
+
+_unix_fork :: proc() -> Pid {
+ when ODIN_ARCH != .arm64 {
+ res := int(intrinsics.syscall(unix.SYS_fork))
+ } else {
+ res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD))
+ }
+ return -1 if res < 0 else Pid(res)
+}
+
+_unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle {
+ when ODIN_ARCH != .arm64 {
+ res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+ } else { // NOTE: arm64 does not have open
+ res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+ }
+ return -1 if res < 0 else Handle(res)
+}
+
+_unix_close :: proc(fd: Handle) -> int {
+ return int(intrinsics.syscall(unix.SYS_close, uintptr(fd)))
+}
+
+_unix_read :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(unix.SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
+}
+
+_unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(unix.SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
+}
+
+_unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return i64(intrinsics.syscall(unix.SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
+ } else {
+ low := uintptr(offset & 0xFFFFFFFF)
+ high := uintptr(offset >> 32)
+ result: i64
+ res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+ return -1 if res < 0 else result
+ }
+}
+
+_unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
+ when ODIN_ARCH == .amd64 {
+ return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
+ } else when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
+ } else { // NOTE: arm64 does not have stat
+ return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
+ }
+}
+
+_unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(unix.SYS_fstat, uintptr(fd), uintptr(stat)))
+ } else {
+ return int(intrinsics.syscall(unix.SYS_fstat64, uintptr(fd), uintptr(stat)))
+ }
+}
+
+_unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
+ when ODIN_ARCH == .amd64 {
+ return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
+ } else when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
+ } else { // NOTE: arm64 does not have any lstat
+ return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
+ }
+}
+
+_unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
+ } else { // NOTE: arm64 does not have readlink
+ return int(intrinsics.syscall(unix.SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
+ }
+}
+
+_unix_access :: proc(path: cstring, mask: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask)))
+ } else { // NOTE: arm64 does not have access
+ return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
+ }
+}
+
+_unix_getcwd :: proc(buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(unix.SYS_getcwd, uintptr(buf), uintptr(size)))
+}
+
+_unix_chdir :: proc(path: cstring) -> int {
+ return int(intrinsics.syscall(unix.SYS_chdir, uintptr(rawptr(path))))
+}
+
+_unix_rename :: proc(old, new: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
+ } else { // NOTE: arm64 does not have rename
+ return int(intrinsics.syscall(unix.SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
+ }
+}
+
+_unix_unlink :: proc(path: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path))))
+ } else { // NOTE: arm64 does not have unlink
+ return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
+ }
+}
+
+_unix_rmdir :: proc(path: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path))))
+ } else { // NOTE: arm64 does not have rmdir
+ return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
+ }
+}
+
+_unix_mkdir :: proc(path: cstring, mode: u32) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
+ } else { // NOTE: arm64 does not have mkdir
+ return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
+ }
+}
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
- @(link_name="gettid") _unix_gettid :: proc() -> u64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
- @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
- @(link_name="lstat") _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
+ @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -309,63 +431,160 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
+// determine errno from syscall return value
+@private
+_get_errno :: proc(res: int) -> Errno {
+ if res < 0 && res > -4096 {
+ return Errno(-res)
+ }
+ return 0
+}
+
+// get errno from libc
get_last_error :: proc() -> int {
return __errno_location()^
}
+personality :: proc(persona: u64) -> (Errno) {
+ res := _unix_personality(persona)
+ if res == -1 {
+ return _get_errno(res)
+ }
+ return ERROR_NONE
+}
+
+fork :: proc() -> (Pid, Errno) {
+ pid := _unix_fork()
+ if pid == -1 {
+ return -1, _get_errno(int(pid))
+ }
+ return pid, ERROR_NONE
+}
+
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path)
- handle := _unix_open(cstr, c.int(flags), c.int(mode))
- delete(cstr)
- if handle == -1 {
- return INVALID_HANDLE, Errno(get_last_error())
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := _unix_open(cstr, flags, mode)
+ if handle < 0 {
+ return INVALID_HANDLE, _get_errno(int(handle))
}
return handle, ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- result := _unix_close(fd)
- if result == -1 {
- return Errno(get_last_error())
- }
- return ERROR_NONE
+ return _get_errno(_unix_close(fd))
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
- if bytes_read == -1 {
- return -1, Errno(get_last_error())
+ if bytes_read < 0 {
+ return -1, _get_errno(bytes_read)
}
- return int(bytes_read), ERROR_NONE
+ return bytes_read, ERROR_NONE
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
- if bytes_written == -1 {
- return -1, Errno(get_last_error())
+ bytes_written := _unix_write(fd, &data[0], uint(len(data)))
+ if bytes_written < 0 {
+ return -1, _get_errno(bytes_written)
}
return int(bytes_written), ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, c.int(whence))
- if res == -1 {
- return -1, Errno(get_last_error())
+ res := _unix_seek(fd, offset, whence)
+ if res < 0 {
+ return -1, _get_errno(int(res))
}
return res, ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
- s, err := _fstat(fd)
- if err != ERROR_NONE {
- return 0, err
- }
- return max(s.size, 0), ERROR_NONE
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
+ result := _unix_fstat(fd, &s)
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return max(s.size, 0), ERROR_NONE
}
+rename :: proc(old_path, new_path: string) -> Errno {
+ old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
+ new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
+ return _get_errno(_unix_rename(old_path_cstr, new_path_cstr))
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_unlink(path_cstr))
+}
+
+make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_mkdir(path_cstr, mode))
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_rmdir(path_cstr))
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+exists :: proc(path: string) -> bool {
+ cpath := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cpath, O_RDONLY)
+ return res == 0
+}
// NOTE(bill): Uses startup to initialize it
@@ -397,36 +616,37 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_stat(cstr, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_fstat(fd, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@@ -476,16 +696,15 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
- path_cstr := strings.clone_to_cstring(path)
- defer delete(path_cstr)
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = 256
buf := make([]byte, bufsz)
for {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
- if rc == -1 {
+ if rc < 0 {
delete(buf)
- return "", Errno(get_last_error())
+ return "", _get_errno(rc)
} else if rc == int(bufsz) {
// NOTE(laleksic, 2021-01-21): Any cleaner way to resize the slice?
bufsz *= 2
@@ -513,8 +732,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
- rel_cstr := strings.clone_to_cstring(rel)
- defer delete(rel_cstr)
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
@@ -529,11 +747,10 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
- result := _unix_access(cstr, c.int(mask))
- if result == -1 {
- return false, Errno(get_last_error())
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ result := _unix_access(cstr, mask)
+ if result < 0 {
+ return false, _get_errno(result)
}
return true, ERROR_NONE
}
@@ -544,6 +761,8 @@ heap_alloc :: proc(size: int) -> rawptr {
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
return _unix_realloc(ptr, c.size_t(new_size))
}
@@ -551,14 +770,37 @@ heap_free :: proc(ptr: rawptr) {
_unix_free(ptr)
}
-getenv :: proc(name: string) -> (string, bool) {
- path_str := strings.clone_to_cstring(name)
- defer delete(path_str)
+lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ // NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
}
- return string(cstr), true
+ return strings.clone(string(cstr), allocator), true
+}
+
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
+}
+
+set_env :: proc(key, value: string) -> Errno {
+ s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
+ res := _unix_putenv(strings.unsafe_string_to_cstring(s))
+ if res < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+unset_env :: proc(key: string) -> Errno {
+ s := strings.clone_to_cstring(key, context.temp_allocator)
+ res := _unix_putenv(s)
+ if res < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
}
get_current_directory :: proc() -> string {
@@ -568,11 +810,12 @@ get_current_directory :: proc() -> string {
page_size := get_page_size()
buf := make([dynamic]u8, page_size)
for {
- #no_bounds_check cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
- if cwd != nil {
- return string(cwd)
+ #no_bounds_check res := _unix_getcwd(&buf[0], uint(len(buf)))
+
+ if res >= 0 {
+ return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
}
- if Errno(get_last_error()) != ERANGE {
+ if _get_errno(res) != ERANGE {
return ""
}
resize(&buf, len(buf)+page_size)
@@ -583,30 +826,29 @@ get_current_directory :: proc() -> string {
set_current_directory :: proc(path: string) -> (err: Errno) {
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
- if res == -1 {
- return Errno(get_last_error())
+ if res < 0 {
+ return _get_errno(res)
}
return ERROR_NONE
}
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
_unix_exit(c.int(code))
}
current_thread_id :: proc "contextless" () -> int {
- return cast(int)intrinsics.syscall(SYS_GETTID)
+ return unix.sys_gettid()
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
- cstr := strings.clone_to_cstring(filename)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
- cstr := strings.clone_to_cstring(symbol)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
}
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
new file mode 100644
index 000000000..9a3dbd874
--- /dev/null
+++ b/core/os/os_openbsd.odin
@@ -0,0 +1,708 @@
+package os
+
+foreign import libc "system:c"
+
+import "core:strings"
+import "core:c"
+import "core:runtime"
+
+Handle :: distinct i32
+Pid :: distinct i32
+File_Time :: distinct u64
+Errno :: distinct i32
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE: Errno: 0
+
+EPERM: Errno: 1
+ENOENT: Errno: 2
+ESRCH: Errno: 3
+EINTR: Errno: 4
+EIO: Errno: 5
+ENXIO: Errno: 6
+E2BIG: Errno: 7
+ENOEXEC: Errno: 8
+EBADF: Errno: 9
+ECHILD: Errno: 10
+EDEADLK: Errno: 11
+ENOMEM: Errno: 12
+EACCES: Errno: 13
+EFAULT: Errno: 14
+ENOTBLK: Errno: 15
+EBUSY: Errno: 16
+EEXIST: Errno: 17
+EXDEV: Errno: 18
+ENODEV: Errno: 19
+ENOTDIR: Errno: 20
+EISDIR: Errno: 21
+EINVAL: Errno: 22
+ENFILE: Errno: 23
+EMFILE: Errno: 24
+ENOTTY: Errno: 25
+ETXTBSY: Errno: 26
+EFBIG: Errno: 27
+ENOSPC: Errno: 28
+ESPIPE: Errno: 29
+EROFS: Errno: 30
+EMLINK: Errno: 31
+EPIPE: Errno: 32
+EDOM: Errno: 33
+ERANGE: Errno: 34
+EAGAIN: Errno: 35
+EWOULDBLOCK: Errno: EAGAIN
+EINPROGRESS: Errno: 36
+EALREADY: Errno: 37
+ENOTSOCK: Errno: 38
+EDESTADDRREQ: Errno: 39
+EMSGSIZE: Errno: 40
+EPROTOTYPE: Errno: 41
+ENOPROTOOPT: Errno: 42
+EPROTONOSUPPORT: Errno: 43
+ESOCKTNOSUPPORT: Errno: 44
+EOPNOTSUPP: Errno: 45
+EPFNOSUPPORT: Errno: 46
+EAFNOSUPPORT: Errno: 47
+EADDRINUSE: Errno: 48
+EADDRNOTAVAIL: Errno: 49
+ENETDOWN: Errno: 50
+ENETUNREACH: Errno: 51
+ENETRESET: Errno: 52
+ECONNABORTED: Errno: 53
+ECONNRESET: Errno: 54
+ENOBUFS: Errno: 55
+EISCONN: Errno: 56
+ENOTCONN: Errno: 57
+ESHUTDOWN: Errno: 58
+ETOOMANYREFS: Errno: 59
+ETIMEDOUT: Errno: 60
+ECONNREFUSED: Errno: 61
+ELOOP: Errno: 62
+ENAMETOOLONG: Errno: 63
+EHOSTDOWN: Errno: 64
+EHOSTUNREACH: Errno: 65
+ENOTEMPTY: Errno: 66
+EPROCLIM: Errno: 67
+EUSERS: Errno: 68
+EDQUOT: Errno: 69
+ESTALE: Errno: 70
+EREMOTE: Errno: 71
+EBADRPC: Errno: 72
+ERPCMISMATCH: Errno: 73
+EPROGUNAVAIL: Errno: 74
+EPROGMISMATCH: Errno: 75
+EPROCUNAVAIL: Errno: 76
+ENOLCK: Errno: 77
+ENOSYS: Errno: 78
+EFTYPE: Errno: 79
+EAUTH: Errno: 80
+ENEEDAUTH: Errno: 81
+EIPSEC: Errno: 82
+ENOATTR: Errno: 83
+EILSEQ: Errno: 84
+ENOMEDIUM: Errno: 85
+EMEDIUMTYPE: Errno: 86
+EOVERFLOW: Errno: 87
+ECANCELED: Errno: 88
+EIDRM: Errno: 89
+ENOMSG: Errno: 90
+ENOTSUP: Errno: 91
+EBADMSG: Errno: 92
+ENOTRECOVERABLE: Errno: 93
+EOWNERDEAD: Errno: 94
+EPROTO: Errno: 95
+
+O_RDONLY :: 0x00000
+O_WRONLY :: 0x00001
+O_RDWR :: 0x00002
+O_NONBLOCK :: 0x00004
+O_APPEND :: 0x00008
+O_ASYNC :: 0x00040
+O_SYNC :: 0x00080
+O_CREATE :: 0x00200
+O_TRUNC :: 0x00400
+O_EXCL :: 0x00800
+O_NOCTTY :: 0x08000
+O_CLOEXEC :: 0x10000
+
+RTLD_LAZY :: 0x001
+RTLD_NOW :: 0x002
+RTLD_LOCAL :: 0x000
+RTLD_GLOBAL :: 0x100
+RTLD_TRACE :: 0x200
+RTLD_NODELETE :: 0x400
+
+MAX_PATH :: 1024
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+pid_t :: i32
+time_t :: i64
+mode_t :: u32
+dev_t :: i32
+ino_t :: u64
+nlink_t :: u32
+uid_t :: u32
+gid_t :: u32
+off_t :: i64
+blkcnt_t :: u64
+blksize_t :: i32
+
+Unix_File_Time :: struct {
+ seconds: time_t,
+ nanoseconds: c.long,
+}
+
+OS_Stat :: struct {
+ mode: mode_t, // inode protection mode
+ device_id: dev_t, // inode's device
+ serial: ino_t, // inode's number
+ nlink: nlink_t, // number of hard links
+ uid: uid_t, // user ID of the file's owner
+ gid: gid_t, // group ID of the file's group
+ rdev: dev_t, // device type
+
+ last_access: Unix_File_Time, // time of last access
+ modified: Unix_File_Time, // time of last data modification
+ status_change: Unix_File_Time, // time of last file status change
+
+ size: off_t, // file size, in bytes
+ blocks: blkcnt_t, // blocks allocated for file
+ block_size: blksize_t, // optimal blocksize for I/O
+
+ flags: u32, // user defined flags for file
+ gen: u32, // file generation number
+ birthtime: Unix_File_Time, // time of file creation
+}
+
+MAXNAMLEN :: 255
+
+// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above
+Dirent :: struct {
+ ino: ino_t, // file number of entry
+ off: off_t, // offset after this entry
+ reclen: u16, // length of this record
+ type: u8, // file type
+ namlen: u8, // length of string in name
+ _padding: [4]u8,
+ name: [MAXNAMLEN + 1]byte, // name
+}
+
+Dir :: distinct rawptr // DIR*
+
+// File type
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+S_ISVTX :: 0o001000 // Save swapped text even after use
+
+// File mode
+ // Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+ // Read, write, execute/search by group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
+
+ // Read, write, execute/search by others
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISTXT :: 0o1000 // Sticky bit
+
+S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+
+F_OK :: 0x00 // Test for file existance
+X_OK :: 0x01 // Test for execute permission
+W_OK :: 0x02 // Test for write permission
+R_OK :: 0x04 // Test for read permission
+
+AT_FDCWD :: -100
+AT_EACCESS :: 0x01
+AT_SYMLINK_NOFOLLOW :: 0x02
+AT_SYMLINK_FOLLOW :: 0x04
+AT_REMOVEDIR :: 0x08
+
+@(default_calling_convention="c")
+foreign libc {
+ @(link_name="__errno") __errno :: proc() -> ^int ---
+
+ @(link_name="fork") _unix_fork :: proc() -> pid_t ---
+ @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
+
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+ @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
+ @(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
+
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
+}
+
+is_path_separator :: proc(r: rune) -> bool {
+ return r == '/'
+}
+
+get_last_error :: proc() -> int {
+ return __errno()^
+}
+
+fork :: proc() -> (Pid, Errno) {
+ pid := _unix_fork()
+ if pid == -1 {
+ return Pid(-1), Errno(get_last_error())
+ }
+ return Pid(pid), ERROR_NONE
+}
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := _unix_open(cstr, c.int(flags), c.int(mode))
+ if handle == -1 {
+ return INVALID_HANDLE, Errno(get_last_error())
+ }
+ return handle, ERROR_NONE
+}
+
+close :: proc(fd: Handle) -> Errno {
+ result := _unix_close(fd)
+ if result == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+ if bytes_read == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_read), ERROR_NONE
+}
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+ bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
+ if bytes_written == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_written), ERROR_NONE
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+ res := _unix_seek(fd, offset, c.int(whence))
+ if res == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return res, ERROR_NONE
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return -1, err
+ }
+ return s.size, ERROR_NONE
+}
+
+rename :: proc(old_path, new_path: string) -> Errno {
+ old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
+ new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
+ res := _unix_rename(old_path_cstr, new_path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_unlink(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_rmdir(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!
+last_write_time :: proc(fd: Handle) -> File_Time {}
+last_write_time_by_name :: proc(name: string) -> File_Time {}
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
+}
+
+last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
+ s, err := _stat(name)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
+}
+
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_stat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_lstat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_fstat(fd, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+ dirp := _unix_fdopendir(fd)
+ if dirp == cast(Dir)nil {
+ return nil, Errno(get_last_error())
+ }
+ return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+ rc := _unix_closedir(dirp)
+ if rc != 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+ _unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+ result: ^Dirent
+ rc := _unix_readdir_r(dirp, &entry, &result)
+
+ if rc != 0 {
+ err = Errno(get_last_error())
+ return
+ }
+ err = ERROR_NONE
+
+ if result == nil {
+ end_of_stream = true
+ return
+ }
+
+ return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ bufsz : uint = MAX_PATH
+ buf := make([]byte, MAX_PATH)
+ for {
+ rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ if rc == -1 {
+ delete(buf)
+ return "", Errno(get_last_error())
+ } else if rc == int(bufsz) {
+ bufsz += MAX_PATH
+ delete(buf)
+ buf = make([]byte, bufsz)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+ }
+ }
+ unreachable()
+}
+
+// XXX OpenBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+ return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+ rel := rel
+ if rel == "" {
+ rel = "."
+ }
+
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+ path_ptr := _unix_realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Errno(get_last_error())
+ }
+ defer _unix_free(path_ptr)
+
+ path_cstr := transmute(cstring)path_ptr
+ path = strings.clone( string(path_cstr) )
+
+ return path, ERROR_NONE
+}
+
+access :: proc(path: string, mask: int) -> (bool, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cstr, c.int(mask))
+ if res == -1 {
+ return false, Errno(get_last_error())
+ }
+ return true, ERROR_NONE
+}
+
+heap_alloc :: proc(size: int) -> rawptr {
+ assert(size >= 0)
+ return _unix_calloc(1, c.size_t(size))
+}
+
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
+ return _unix_realloc(ptr, c.size_t(new_size))
+}
+
+heap_free :: proc(ptr: rawptr) {
+ _unix_free(ptr)
+}
+
+lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ cstr := _unix_getenv(path_str)
+ if cstr == nil {
+ return "", false
+ }
+ return strings.clone(string(cstr), allocator), true
+}
+
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
+}
+
+get_current_directory :: proc() -> string {
+ buf := make([dynamic]u8, MAX_PATH)
+ for {
+ cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)))
+ if cwd != nil {
+ return string(cwd)
+ }
+ if Errno(get_last_error()) != ERANGE {
+ return ""
+ }
+ resize(&buf, len(buf) + MAX_PATH)
+ }
+ unreachable()
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_chdir(cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
+ _unix_exit(c.int(code))
+}
+
+current_thread_id :: proc "contextless" () -> int {
+ return _unix_getthrid()
+}
+
+dlopen :: proc(filename: string, flags: int) -> rawptr {
+ cstr := strings.clone_to_cstring(filename, context.temp_allocator)
+ handle := _unix_dlopen(cstr, c.int(flags))
+ return handle
+}
+dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
+ assert(handle != nil)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+ proc_handle := _unix_dlsym(handle, cstr)
+ return proc_handle
+}
+dlclose :: proc(handle: rawptr) -> bool {
+ assert(handle != nil)
+ return _unix_dlclose(handle) == 0
+}
+dlerror :: proc() -> string {
+ return string(_unix_dlerror())
+}
+
+get_page_size :: proc() -> int {
+ // NOTE(tetra): The page size never changes, so why do anything complicated
+ // if we don't have to.
+ @static page_size := -1
+ if page_size != -1 {
+ return page_size
+ }
+
+ page_size = int(_unix_getpagesize())
+ return page_size
+}
+
+
+_alloc_command_line_arguments :: proc() -> []string {
+ res := make([]string, len(runtime.args__))
+ for arg, i in runtime.args__ {
+ res[i] = string(arg)
+ }
+ return res
+}
diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin
index d2ba166bd..7bab1b949 100644
--- a/core/os/os_wasi.odin
+++ b/core/os/os_wasi.odin
@@ -1,6 +1,7 @@
package os
import "core:sys/wasm/wasi"
+import "core:runtime"
Handle :: distinct i32
Errno :: distinct i32
@@ -93,5 +94,6 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code))
}
\ No newline at end of file
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index e6efb89df..fe9496e4c 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -2,6 +2,7 @@
package os
import win32 "core:sys/windows"
+import "core:runtime"
Handle :: distinct uintptr
File_Time :: distinct u64
@@ -128,6 +129,7 @@ get_page_size :: proc() -> int {
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
win32.ExitProcess(win32.DWORD(code))
}
diff --git a/core/os/stat.odin b/core/os/stat.odin
index 6f4c8b0ef..1b64ad33b 100644
--- a/core/os/stat.odin
+++ b/core/os/stat.odin
@@ -2,7 +2,6 @@ package os
import "core:time"
-
File_Info :: struct {
fullpath: string,
name: string,
diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin
index 08c6f53c4..dae7ab2fb 100644
--- a/core/os/stat_unix.odin
+++ b/core/os/stat_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package os
import "core:time"
@@ -61,7 +61,7 @@ _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
fi.size = s.size
fi.mode = cast(File_Mode)s.mode
- fi.is_dir = S_ISDIR(u32(s.mode))
+ fi.is_dir = S_ISDIR(s.mode)
// NOTE(laleksic, 2021-01-21): Not really creation time, but closest we can get (maybe better to leave it 0?)
fi.creation_time = _make_time_from_unix_file_time(s.status_change)
@@ -119,7 +119,6 @@ lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, e
}
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) {
-
context.allocator = allocator
s: OS_Stat
diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin
index 2d9f98fd4..79bb8c42e 100644
--- a/core/os/stat_windows.odin
+++ b/core/os/stat_windows.odin
@@ -20,7 +20,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
return "", Errno(win32.GetLastError())
}
if n <= u32(len(buf)) {
- return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE
+ return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE
}
resize(&buf, len(buf)*2)
}
@@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno)
return _stat(name, attrs, allocator)
}
-fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
+fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) {
if fd == 0 {
return {}, ERROR_INVALID_HANDLE
}
@@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno)
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
- fi: File_Info
- fi.fullpath = path
fi.name = basename(path)
fi.mode |= file_type_mode(h)
- return fi, ERROR_NONE
+ errno = ERROR_NONE
+ case:
+ fi, errno = file_info_from_get_file_information_by_handle(path, h)
}
-
- return file_info_from_get_file_information_by_handle(path, h)
+ fi.fullpath = path
+ return
}
@@ -132,26 +132,11 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
@(private)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
- if fd == 0 {
- return "", ERROR_INVALID_HANDLE
+ buf, err := cleanpath_from_handle_u16(fd)
+ if err != 0 {
+ return "", err
}
- h := win32.HANDLE(fd)
-
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch Errno(err) {
- case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
- return "", Errno(err)
- case ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
- }
- return cleanpath_from_buf(buf), ERROR_NONE
+ return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
}
@(private)
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
@@ -160,27 +145,19 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
}
h := win32.HANDLE(fd)
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch Errno(err) {
- case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
- return nil, Errno(err)
- case ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
+ n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
+ if n == 0 {
+ return nil, Errno(win32.GetLastError())
}
- return cleanpath_strip_prefix(buf), ERROR_NONE
+ buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
+ buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
+ return buf[:buf_len], ERROR_NONE
}
@(private)
cleanpath_from_buf :: proc(buf: []u16) -> string {
buf := buf
buf = cleanpath_strip_prefix(buf)
- return win32.utf16_to_utf8(buf, context.allocator)
+ return win32.utf16_to_utf8(buf, context.allocator) or_else ""
}
@(private)
diff --git a/core/os/stream.odin b/core/os/stream.odin
index 5cf5c8405..2c6e1d47f 100644
--- a/core/os/stream.odin
+++ b/core/os/stream.odin
@@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{
return
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+ when ODIN_OS == .Windows || ODIN_OS == .WASI {
fd := Handle(uintptr(s.stream_data))
os_err: Errno
n, os_err = read_at(fd, p, offset)
@@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{
return
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+ when ODIN_OS == .Windows || ODIN_OS == .WASI {
fd := Handle(uintptr(s.stream_data))
os_err: Errno
n, os_err = write_at(fd, p, offset)
@@ -53,7 +53,7 @@ _file_stream_vtable := &io.Stream_VTable{
return sz
},
impl_flush = proc(s: io.Stream) -> io.Error {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
fd := Handle(uintptr(s.stream_data))
flush(fd)
} else {
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index cba44953d..00a9c9fb0 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -89,7 +89,7 @@ scan_chunk :: proc(pattern: string) -> (star: bool, chunk, rest: string) {
scan_loop: for i = 0; i < len(pattern); i += 1 {
switch pattern[i] {
case '\\':
- when ODIN_OS != "windows" {
+ when ODIN_OS != .Windows {
if i+1 < len(pattern) {
i += 1
}
@@ -161,7 +161,7 @@ match_chunk :: proc(chunk, s: string) -> (rest: string, ok: bool, err: Match_Err
chunk = chunk[1:]
case '\\':
- when ODIN_OS != "windows" {
+ when ODIN_OS != .Windows {
chunk = chunk[1:]
if len(chunk) == 0 {
err = .Syntax_Error
@@ -188,7 +188,7 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
return
}
chunk := chunk
- if chunk[0] == '\\' && ODIN_OS != "windows" {
+ if chunk[0] == '\\' && ODIN_OS != .Windows {
chunk = chunk[1:]
if len(chunk) == 0 {
err = .Syntax_Error
@@ -220,19 +220,21 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
+ context.allocator = allocator
+
if !has_meta(pattern) {
// TODO(bill): os.lstat on here to check for error
- m := make([]string, 1, allocator)
+ m := make([]string, 1)
m[0] = pattern
return m[:], .None
}
- temp_buf: [8]byte
-
dir, file := split(pattern)
volume_len := 0
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
+ temp_buf: [8]byte
volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
+
} else {
dir = clean_glob_path(dir)
}
@@ -247,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
- dmatches := make([dynamic]string, 0, 0, allocator)
+ dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
if err != .None {
@@ -259,11 +261,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
-_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) {
+_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
+ context.allocator = allocator
+
if matches != nil {
m = matches^
} else {
- m = make([dynamic]string, 0, 0, context.allocator)
+ m = make([dynamic]string, 0, 0)
}
@@ -276,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
{
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
+
if ferr != 0 {
return
}
@@ -300,7 +305,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
n := fi.name
matched := match(pattern, n) or_return
if matched {
- append(&m, join(dir, n))
+ append(&m, join({dir, n}))
}
}
return
@@ -308,7 +313,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
@(private)
has_meta :: proc(path: string) -> bool {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
CHARS :: `*?[`
} else {
CHARS :: `*?[\`
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin
index 39cd80a47..32e4a8a37 100644
--- a/core/path/filepath/path.odin
+++ b/core/path/filepath/path.odin
@@ -1,14 +1,16 @@
// The path/filepath package uses either forward slashes or backslashes depending on the operating system
-// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
+// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:strings"
+SEPARATOR_CHARS :: `/\`
+
// is_separator checks whether the byte is a valid separator character
is_separator :: proc(c: byte) -> bool {
switch c {
case '/': return true
- case '\\': return ODIN_OS == "windows"
+ case '\\': return ODIN_OS == .Windows
}
return false
}
@@ -32,7 +34,7 @@ volume_name :: proc(path: string) -> string {
}
volume_name_len :: proc(path: string) -> int {
- if ODIN_OS == "windows" {
+ if ODIN_OS == .Windows {
if len(path) < 2 {
return 0
}
@@ -69,6 +71,16 @@ volume_name_len :: proc(path: string) -> int {
return 0
}
+/*
+ Gets the file name and extension from a path.
+
+ i.e:
+ 'path/to/name.tar.gz' -> 'name.tar.gz'
+ 'path/to/name.txt' -> 'name.txt'
+ 'path/to/name' -> 'name'
+
+ Returns "." if the path is an empty string.
+*/
base :: proc(path: string) -> string {
if path == "" {
return "."
@@ -94,6 +106,118 @@ base :: proc(path: string) -> string {
return path
}
+/*
+ Gets the name of a file from a path.
+
+ The stem of a file is such that stem(path) + ext(path) = base(path).
+
+ Only the last dot is considered when splitting the file extension.
+ See `short_stem`.
+
+ i.e:
+ 'name.tar.gz' -> 'name.tar'
+ 'name.txt' -> 'name'
+
+ Returns an empty string if there is no stem. e.g: '.gitignore'.
+ Returns an empty string if there's a trailing path separator.
+*/
+stem :: proc(path: string) -> string {
+ if len(path) > 0 && is_separator(path[len(path) - 1]) {
+ // NOTE(tetra): Trailing separator
+ return ""
+ }
+
+ // NOTE(tetra): Get the basename
+ path := path
+ if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
+ path = path[i+1:]
+ }
+
+ if i := strings.last_index_byte(path, '.'); i != -1 {
+ return path[:i]
+ }
+
+ return path
+}
+
+/*
+ Gets the name of a file from a path.
+
+ The short stem is such that short_stem(path) + long_ext(path) = base(path).
+
+ The first dot is used to split off the file extension, unlike `stem` which uses the last dot.
+
+ i.e:
+ 'name.tar.gz' -> 'name'
+ 'name.txt' -> 'name'
+
+ Returns an empty string if there is no stem. e.g: '.gitignore'.
+ Returns an empty string if there's a trailing path separator.
+*/
+short_stem :: proc(path: string) -> string {
+ s := stem(path)
+ if i := strings.index_byte(s, '.'); i != -1 {
+ return s[:i]
+ }
+ return s
+}
+
+/*
+ Gets the file extension from a path, including the dot.
+
+ The file extension is such that stem(path) + ext(path) = base(path).
+
+ Only the last dot is considered when splitting the file extension.
+ See `long_ext`.
+
+ i.e:
+ 'name.tar.gz' -> '.gz'
+ 'name.txt' -> '.txt'
+
+ Returns an empty string if there is no dot.
+ Returns an empty string if there is a trailing path separator.
+*/
+ext :: proc(path: string) -> string {
+ for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
+ if path[i] == '.' {
+ return path[i:]
+ }
+ }
+ return ""
+}
+
+/*
+ Gets the file extension from a path, including the dot.
+
+ The long file extension is such that short_stem(path) + long_ext(path) = base(path).
+
+ The first dot is used to split off the file extension, unlike `ext` which uses the last dot.
+
+ i.e:
+ 'name.tar.gz' -> '.tar.gz'
+ 'name.txt' -> '.txt'
+
+ Returns an empty string if there is no dot.
+ Returns an empty string if there is a trailing path separator.
+*/
+long_ext :: proc(path: string) -> string {
+ if len(path) > 0 && is_separator(path[len(path) - 1]) {
+ // NOTE(tetra): Trailing separator
+ return ""
+ }
+
+ // NOTE(tetra): Get the basename
+ path := path
+ if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
+ path = path[i+1:]
+ }
+
+ if i := strings.index_byte(path, '.'); i != -1 {
+ return path[i:]
+ }
+
+ return ""
+}
clean :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
@@ -122,6 +246,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
vol_and_path = original_path,
vol_len = vol_len,
}
+ defer lazy_buffer_destroy(out)
r, dot_dot := 0, 0
if rooted {
@@ -170,7 +295,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
cleaned, new_allocation := from_slash(s)
if new_allocation {
delete(s)
- lazy_buffer_destroy(out)
}
return cleaned
}
@@ -189,15 +313,6 @@ to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: str
return strings.replace_all(path, SEPARATOR_STRING, "/", allocator)
}
-ext :: proc(path: string) -> string {
- for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
- if path[i] == '.' {
- return path[i:]
- }
- }
- return ""
-}
-
Relative_Error :: enum {
None,
@@ -284,13 +399,14 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
}
dir :: proc(path: string, allocator := context.allocator) -> string {
+ context.allocator = allocator
vol := volume_name(path)
i := len(path) - 1
for i >= len(vol) && !is_separator(path[i]) {
i -= 1
}
- dir := clean(path[len(vol) : i+1], allocator)
- defer delete(dir, allocator)
+ dir := clean(path[len(vol) : i+1])
+ defer delete(dir)
if dir == "." && len(vol) > 2 {
return strings.clone(vol)
}
@@ -299,6 +415,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
+// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
+// For Windows the separator is `;`, for Unix it's `:`.
+// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
+// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
+// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> []string {
if path == "" {
return nil
@@ -321,7 +442,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
start, quote = 0, false
- list := make([]string, count, allocator)
+ list := make([]string, count + 1, allocator)
index := 0
for i := 0; i < len(path); i += 1 {
c := path[i]
@@ -335,6 +456,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
}
assert(index == count)
+ list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index 1db528a2f..8faf6097c 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -1,7 +1,7 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package filepath
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
foreign import libc "System.framework"
} else {
foreign import libc "system:c"
@@ -38,7 +38,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
return path_str, true
}
-join :: proc(elems: ..string, allocator := context.allocator) -> string {
+join :: proc(elems: []string, allocator := context.allocator) -> string {
for e, i in elems {
if e != "" {
p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator)
@@ -54,11 +54,16 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
@(private)
foreign libc {
@(link_name="__error") __error :: proc() -> ^i32 ---
}
+} else when ODIN_OS == .OpenBSD {
+ @(private)
+ foreign libc {
+ @(link_name="__errno") __error :: proc() -> ^i32 ---
+ }
} else {
@(private)
foreign libc {
diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin
index 25b2ae500..cdfe3ddbb 100644
--- a/core/path/filepath/path_windows.odin
+++ b/core/path/filepath/path_windows.odin
@@ -68,7 +68,7 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
return "", os.Errno(win32.GetLastError())
}
if n <= u32(len(buf)) {
- return win32.utf16_to_utf8(buf[:n], ta), os.ERROR_NONE
+ return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
}
resize(&buf, len(buf)*2)
}
@@ -88,7 +88,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
}
-join :: proc(elems: ..string, allocator := context.allocator) -> string {
+join :: proc(elems: []string, allocator := context.allocator) -> string {
for e, i in elems {
if e != "" {
return join_non_empty(elems[i:], allocator)
diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin
index 29d4fd5b1..dad63cc09 100644
--- a/core/path/filepath/walk.odin
+++ b/core/path/filepath/walk.odin
@@ -71,7 +71,7 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
@(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
- f, err := os.open(dir_name)
+ f, err := os.open(dir_name, os.O_RDONLY)
if err != 0 {
return nil, err
}
diff --git a/core/path/path_error.odin b/core/path/path_error.odin
new file mode 100644
index 000000000..2be0b4cf4
--- /dev/null
+++ b/core/path/path_error.odin
@@ -0,0 +1,5 @@
+package path
+
+#panic(
+`core:path/slashpath - for paths separated by forward slashes, e.g. paths in URLs, this does not deal with OS-specific paths
+core:path/filepath - uses either forward slashes or backslashes depending on the operating system, deals with Windows/NT paths with volume letters or backslashes (on the related platforms)`)
diff --git a/core/path/match.odin b/core/path/slashpath/match.odin
similarity index 99%
rename from core/path/match.odin
rename to core/path/slashpath/match.odin
index 0bea4f6e7..09e774275 100644
--- a/core/path/match.odin
+++ b/core/path/slashpath/match.odin
@@ -1,4 +1,4 @@
-package path
+package slashpath
import "core:strings"
import "core:unicode/utf8"
diff --git a/core/path/path.odin b/core/path/slashpath/path.odin
similarity index 92%
rename from core/path/path.odin
rename to core/path/slashpath/path.odin
index d54106b3a..865f619bf 100644
--- a/core/path/path.odin
+++ b/core/path/slashpath/path.odin
@@ -1,9 +1,9 @@
-// The path package is only to be used for paths separated by forward slashes,
+// The slashpath package is only to be used for paths separated by forward slashes,
// e.g. paths in URLs
//
// This package does not deal with Windows/NT paths with volume letters or backslashes
// To manipulate operating system specific paths, use the path/filepath package
-package path
+package slashpath
import "core:strings"
@@ -146,11 +146,11 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
}
// join joins numerous path elements into a single path
-join :: proc(elems: ..string, allocator := context.allocator) -> string {
+join :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator
for elem, i in elems {
if elem != "" {
- s := strings.join(elems[i:], "/")
+ s := strings.join(elems[i:], "/", context.temp_allocator)
return clean(s)
}
}
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 7f64d0974..27a83e680 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -234,7 +234,7 @@ is_nil :: proc(v: any) -> bool {
return true
}
data := as_bytes(v)
- if data != nil {
+ if data == nil {
return true
}
for v in data {
@@ -365,6 +365,19 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return nil
}
+deref :: proc(val: any) -> any {
+ if val != nil {
+ ti := type_info_base(type_info_of(val.id))
+ if info, ok := ti.variant.(Type_Info_Pointer); ok {
+ return any{
+ (^rawptr)(val.data)^,
+ info.elem.id,
+ }
+ }
+ }
+ return val
+}
+
// Struct_Tag represents the type of the string of a struct field
@@ -641,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info {
}
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
- return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0])
+ return len(info.variants) == 1 && is_pointer(info.variants[0])
}
union_variant_typeid :: proc(a: any) -> typeid {
@@ -680,7 +693,6 @@ union_variant_typeid :: proc(a: any) -> typeid {
return nil
}
panic("expected a union to reflect.union_variant_typeid")
-
}
get_union_variant_raw_tag :: proc(a: any) -> i64 {
@@ -1042,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = u64(v)
case f32: value = u64(v)
case f64: value = u64(v)
case f32le: value = u64(v)
@@ -1147,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = f64(v)
case f32: value = f64(v)
case f64: value = (v)
case f32le: value = f64(v)
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 74778013a..7be7ff812 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
return ok
}
+is_pointer_internally :: proc(info: ^Type_Info) -> bool {
+ if info == nil { return false }
+ #partial switch v in info.variant {
+ case Type_Info_Pointer, Type_Info_Multi_Pointer,
+ Type_Info_Procedure:
+ return true
+ case Type_Info_String:
+ return v.is_cstring
+ }
+ return false
+}
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
@@ -334,11 +345,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
-write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
- write_type(buf, type_info_of(id))
+write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+ return write_type_writer(strings.to_writer(buf), type_info_of(id))
}
-write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
- write_type(writer, type_info_of(id))
+write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+ return write_type_writer(writer, type_info_of(id), n_written)
}
write_typeid :: proc{
@@ -472,6 +483,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
write_type(w, info.elem, &n) or_return
case Type_Info_Enumerated_Array:
+ if info.is_sparse {
+ io.write_string(w, "#sparse", &n) or_return
+ }
io.write_string(w, "[", &n) or_return
write_type(w, info.index, &n) or_return
io.write_string(w, "]", &n) or_return
@@ -528,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
case Type_Info_Union:
io.write_string(w, "union ", &n) or_return
- if info.maybe {
- io.write_string(w, "#maybe ", &n) or_return
- }
+ if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return }
+ if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return }
if info.custom_align {
io.write_string(w, "#align ", &n) or_return
io.write_i64(w, i64(ti.align), 10, &n) or_return
@@ -560,11 +573,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
write_type(w, info.elem, &n) or_return
case is_rune(info.elem):
io.write_encoded_rune(w, rune(info.lower), true, &n) or_return
- io.write_string(w, "..", &n) or_return
+ io.write_string(w, "..=", &n) or_return
io.write_encoded_rune(w, rune(info.upper), true, &n) or_return
case:
io.write_i64(w, info.lower, 10, &n) or_return
- io.write_string(w, "..", &n) or_return
+ io.write_string(w, "..=", &n) or_return
io.write_i64(w, info.upper, 10, &n) or_return
}
if info.underlying != nil {
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index be30eef02..8fb3d7210 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -33,6 +33,11 @@ Calling_Convention :: enum u8 {
None = 6,
Naked = 7,
+
+ _ = 8, // reserved
+
+ Win64 = 9,
+ SysV = 10,
}
Type_Info_Enum_Value :: distinct i64
@@ -95,6 +100,7 @@ Type_Info_Enumerated_Array :: struct {
count: int,
min_value: Type_Info_Enum_Value,
max_value: Type_Info_Enum_Value,
+ is_sparse: bool,
}
Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}
Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}
@@ -129,7 +135,7 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
- maybe: bool,
+ shared_nil: bool,
}
Type_Info_Enum :: struct {
base: ^Type_Info,
@@ -261,6 +267,19 @@ type_table: []Type_Info
args__: []cstring
+when ODIN_OS == .Windows {
+ // NOTE(Jeroen): If we're a Windows DLL, fwdReason will be populated.
+ // This tells a DLL if it's first loaded, about to be unloaded, or a thread is joining/exiting.
+
+ DLL_Forward_Reason :: enum u32 {
+ Process_Detach = 0, // About to unload DLL
+ Process_Attach = 1, // Entry point
+ Thread_Attach = 2,
+ Thread_Detach = 3,
+ }
+ dll_forward_reason: DLL_Forward_Reason
+}
+
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
@@ -345,7 +364,6 @@ Context :: struct {
assertion_failure_proc: Assertion_Failure_Proc,
logger: Logger,
- user_data: any,
user_ptr: rawptr,
user_index: int,
@@ -386,6 +404,60 @@ Raw_Cstring :: struct {
}
+/*
+ // Defined internally by the compiler
+ Odin_OS_Type :: enum int {
+ Unknown,
+ Windows,
+ Darwin,
+ Linux,
+ Essence,
+ FreeBSD,
+ OpenBSD,
+ WASI,
+ JS,
+ Freestanding,
+ }
+*/
+Odin_OS_Type :: type_of(ODIN_OS)
+
+/*
+ // Defined internally by the compiler
+ Odin_Arch_Type :: enum int {
+ Unknown,
+ amd64,
+ i386,
+ arm32,
+ arm64,
+ wasm32,
+ wasm64,
+ }
+*/
+Odin_Arch_Type :: type_of(ODIN_ARCH)
+
+/*
+ // Defined internally by the compiler
+ Odin_Build_Mode_Type :: enum int {
+ Executable,
+ Dynamic,
+ Object,
+ Assembly,
+ LLVM_IR,
+ }
+*/
+Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
+
+/*
+ // Defined internally by the compiler
+ Odin_Endian_Type :: enum int {
+ Unknown,
+ Little,
+ Big,
+ }
+*/
+Odin_Endian_Type :: type_of(ODIN_ENDIAN)
+
+
/////////////////////////////
// Init Startup Procedures //
/////////////////////////////
@@ -394,7 +466,7 @@ Raw_Cstring :: struct {
// This is probably only useful for freestanding targets
foreign {
@(link_name="__$startup_runtime")
- _startup_runtime :: proc() ---
+ _startup_runtime :: proc "odin" () ---
}
@(link_name="__$cleanup_runtime")
@@ -402,6 +474,11 @@ _cleanup_runtime :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}
+_cleanup_runtime_contextless :: proc "contextless" () {
+ context = default_context()
+ _cleanup_runtime()
+}
+
/////////////////////////////
/////////////////////////////
@@ -451,16 +528,18 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
return &type_table[n]
}
-typeid_base :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_of(id)
- ti = type_info_base(ti)
- return ti.id
+when !ODIN_DISALLOW_RTTI {
+ typeid_base :: proc "contextless" (id: typeid) -> typeid {
+ ti := type_info_of(id)
+ ti = type_info_base(ti)
+ return ti.id
+ }
+ typeid_core :: proc "contextless" (id: typeid) -> typeid {
+ ti := type_info_core(type_info_of(id))
+ return ti.id
+ }
+ typeid_base_without_enum :: typeid_core
}
-typeid_core :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_core(type_info_of(id))
- return ti.id
-}
-typeid_base_without_enum :: typeid_core
@@ -500,7 +579,7 @@ __init_context :: proc "contextless" (c: ^Context) {
return
}
- // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contexless" calling convention
+ // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contextless" calling convention
c.allocator.procedure = default_allocator_proc
c.allocator.data = nil
@@ -516,7 +595,7 @@ __init_context :: proc "contextless" (c: ^Context) {
}
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
- when ODIN_OS == "freestanding" {
+ when ODIN_OS == .Freestanding {
// Do nothing
} else {
print_caller_location(loc)
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
index 44da894c1..cebb91987 100644
--- a/core/runtime/core_builtin.odin
+++ b/core/runtime/core_builtin.odin
@@ -3,7 +3,17 @@ package runtime
import "core:intrinsics"
@builtin
-Maybe :: union($T: typeid) #maybe {T}
+Maybe :: union($T: typeid) {T}
+
+
+@builtin
+container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
+ where intrinsics.type_has_field(T, field_name),
+ intrinsics.type_field_type(T, field_name) == Field_Type {
+ offset :: offset_of_by_string(T, field_name)
+ return (^T)(uintptr(ptr) - offset) if ptr != nil else nil
+}
+
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
@@ -119,6 +129,9 @@ reserve :: proc{reserve_dynamic_array, reserve_map}
@builtin
resize :: proc{resize_dynamic_array}
+// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
+@builtin
+shrink :: proc{shrink_dynamic_array, shrink_map}
@builtin
free :: proc{mem_free}
@@ -274,12 +287,30 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
}
@builtin
-reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
+reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil {
- __dynamic_map_reserve(__get_map_header(m), capacity)
+ __dynamic_map_reserve(__get_map_header(m), capacity, loc)
}
}
+/*
+ Shrinks the capacity of a map down to the current length, or the given capacity.
+
+ If `new_cap` is negative, then `len(m)` is used.
+
+ Returns false if `cap(m) < new_cap`, or the allocator report failure.
+
+ If `len(m) < new_cap`, then `len(m)` will be left unchanged.
+*/
+@builtin
+shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
+ if m != nil {
+ new_cap := new_cap if new_cap >= 0 else len(m)
+ return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
+ }
+ return
+}
+
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
// If m is nil, or there is no such element, this procedure is a no-op
@builtin
@@ -386,12 +417,13 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
if array == nil {
return
}
- n := len(array)
+ n := max(len(array), index)
m :: 1
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
when size_of(E) != 0 {
- copy(array[index+m:], array[index:])
+ copy(array[index + m:], array[index:])
array[index] = arg
}
ok = true
@@ -409,12 +441,13 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
return
}
- n := len(array)
+ n := max(len(array), index)
m := len(args)
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
when size_of(E) != 0 {
- copy(array[index+m:], array[index:])
+ copy(array[index + m:], array[index:])
copy(array[index:], args)
}
ok = true
@@ -427,17 +460,18 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
if array == nil {
return
}
- if len(args) == 0 {
+ if len(arg) == 0 {
ok = true
return
}
- n := len(array)
- m := len(args)
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ n := max(len(array), index)
+ m := len(arg)
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
copy(array[index+m:], array[index:])
- copy(array[index:], args)
+ copy(array[index:], arg)
ok = true
}
return
@@ -523,6 +557,54 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
return true
}
+/*
+ Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
+
+ If `new_cap` is negative, then `len(array)` is used.
+
+ Returns false if `cap(array) < new_cap`, or the allocator report failure.
+
+ If `len(array) < new_cap`, then `len(array)` will be left unchanged.
+*/
+shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
+ if array == nil {
+ return
+ }
+ a := (^Raw_Dynamic_Array)(array)
+
+ new_cap := new_cap if new_cap >= 0 else a.len
+
+ if new_cap > a.cap {
+ return
+ }
+
+ if a.allocator.procedure == nil {
+ a.allocator = context.allocator
+ }
+ assert(a.allocator.procedure != nil)
+
+ old_size := a.cap * size_of(E)
+ new_size := new_cap * size_of(E)
+
+ new_data, err := a.allocator.procedure(
+ a.allocator.data,
+ .Resize,
+ new_size,
+ align_of(E),
+ a.data,
+ old_size,
+ loc,
+ )
+ if err != nil {
+ return
+ }
+
+ a.data = raw_data(new_data)
+ a.len = min(new_cap, a.len)
+ a.cap = new_cap
+ return true
+}
+
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value
@@ -587,26 +669,30 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
@builtin
-raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
- return (^E)(a)
+raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
+ return ([^]E)(a)
}
@builtin
-raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
+raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
+ return ([^]E)(a)
+}
+@builtin
+raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
ptr := (transmute(Raw_Slice)s).data
- return (^E)(ptr)
+ return ([^]E)(ptr)
}
@builtin
-raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
+raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
ptr := (transmute(Raw_Dynamic_Array)s).data
- return (^E)(ptr)
+ return ([^]E)(ptr)
}
@builtin
-raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
+raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
return (transmute(Raw_String)s).data
}
@builtin
-raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}
+raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
@@ -614,13 +700,19 @@ raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_str
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) {
if !condition {
- proc(message: string, loc: Source_Code_Location) {
+ // NOTE(bill): This is wrapped in a procedure call
+ // to improve performance to make the CPU not
+ // execute speculatively, making it about an order of
+ // magnitude faster
+ @(cold)
+ internal :: proc(message: string, loc: Source_Code_Location) {
p := context.assertion_failure_proc
if p == nil {
p = default_assertion_failure_proc
}
p("runtime assertion", message, loc)
- }(message, loc)
+ }
+ internal(message, loc)
}
}
diff --git a/core/runtime/core_builtin_matrix.odin b/core/runtime/core_builtin_matrix.odin
index 08dca288e..53589587c 100644
--- a/core/runtime/core_builtin_matrix.odin
+++ b/core/runtime/core_builtin_matrix.odin
@@ -146,14 +146,14 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
y[0, 0] = +x[1, 1] / d
- y[1, 0] = -x[1, 0] / d
- y[0, 1] = -x[0, 1] / d
+ y[1, 0] = -x[0, 1] / d
+ y[0, 1] = -x[1, 0] / d
y[1, 1] = +x[0, 0] / d
} else {
id := 1 / d
y[0, 0] = +x[1, 1] * id
- y[1, 0] = -x[1, 0] * id
- y[0, 1] = -x[0, 1] * id
+ y[1, 0] = -x[0, 1] * id
+ y[0, 1] = -x[1, 0] * id
y[1, 1] = +x[0, 0] * id
}
return
@@ -214,16 +214,16 @@ matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
- y[0, 0] = x[1, 1] / d
- y[0, 1] = x[1, 0] / d
- y[1, 0] = x[0, 1] / d
- y[1, 1] = x[0, 0] / d
+ y[0, 0] = +x[1, 1] / d
+ y[0, 1] = -x[0, 1] / d
+ y[1, 0] = -x[1, 0] / d
+ y[1, 1] = +x[0, 0] / d
} else {
id := 1 / d
- y[0, 0] = x[1, 1] * id
- y[0, 1] = x[1, 0] * id
- y[1, 0] = x[0, 1] * id
- y[1, 1] = x[0, 0] * id
+ y[0, 0] = +x[1, 1] * id
+ y[0, 1] = -x[0, 1] * id
+ y[1, 0] = -x[1, 0] * id
+ y[1, 1] = +x[0, 0] * id
}
return
}
diff --git a/core/runtime/default_allocators_nil.odin b/core/runtime/default_allocators_nil.odin
index ccb4a3381..04dea0e19 100644
--- a/core/runtime/default_allocators_nil.odin
+++ b/core/runtime/default_allocators_nil.odin
@@ -32,7 +32,7 @@ nil_allocator :: proc() -> Allocator {
-when ODIN_OS == "freestanding" {
+when ODIN_OS == .Freestanding {
default_allocator_proc :: nil_allocator_proc
default_allocator :: nil_allocator
}
\ No newline at end of file
diff --git a/core/runtime/default_allocators_windows.odin b/core/runtime/default_allocators_windows.odin
index 9cabbcce8..45d4d3e6a 100644
--- a/core/runtime/default_allocators_windows.odin
+++ b/core/runtime/default_allocators_windows.odin
@@ -17,7 +17,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
_windows_default_free(old_memory)
case .Free_All:
- // NOTE(tetra): Do nothing.
+ return nil, .Mode_Not_Implemented
case .Resize:
data, err = _windows_default_resize(old_memory, old_size, size, alignment)
@@ -29,7 +29,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
}
case .Query_Info:
- // Do nothing
+ return nil, .Mode_Not_Implemented
}
return
diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin
index 01143e222..52f781980 100644
--- a/core/runtime/default_temporary_allocator.odin
+++ b/core/runtime/default_temporary_allocator.odin
@@ -3,7 +3,7 @@ package runtime
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
-when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
Default_Temp_Allocator :: struct {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}
@@ -185,7 +185,7 @@ when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCAT
}
case .Query_Info:
- // Nothing to give
+ return nil, .Mode_Not_Implemented
}
return
diff --git a/core/runtime/dynamic_array_internal.odin b/core/runtime/dynamic_array_internal.odin
index 6f800de7a..d39c2dd0b 100644
--- a/core/runtime/dynamic_array_internal.odin
+++ b/core/runtime/dynamic_array_internal.odin
@@ -41,6 +41,35 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
return false
}
+__dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_cap: int, loc := #caller_location) -> (did_shrink: bool) {
+ array := (^Raw_Dynamic_Array)(array_)
+
+ // NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
+ // assuming that appending/reserving will set the allocator, if it is not already set.
+ if array.allocator.procedure == nil {
+ array.allocator = context.allocator
+ }
+ assert(array.allocator.procedure != nil)
+
+ if new_cap > array.cap {
+ return
+ }
+
+ old_size := array.cap * elem_size
+ new_size := new_cap * elem_size
+ allocator := array.allocator
+
+ new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc)
+ if err != nil {
+ return
+ }
+
+ array.data = raw_data(new_data)
+ array.len = min(new_cap, array.len)
+ array.cap = new_cap
+ return true
+}
+
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
array := (^Raw_Dynamic_Array)(array_)
@@ -65,7 +94,7 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
ok := true
- if array.cap <= array.len+item_count {
+ if array.cap < array.len+item_count {
cap := 2 * array.cap + max(8, item_count)
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
}
@@ -86,7 +115,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
array := (^Raw_Dynamic_Array)(array_)
ok := true
- if array.cap <= array.len+1 {
+ if array.cap < array.len+1 {
cap := 2 * array.cap + max(8, 1)
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
}
diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin
index 4d4c51d6a..fee0f570f 100644
--- a/core/runtime/dynamic_map_internal.odin
+++ b/core/runtime/dynamic_map_internal.odin
@@ -239,6 +239,16 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller
}
}
+__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
+ c := context
+ if m.entries.allocator.procedure != nil {
+ c.allocator = m.entries.allocator
+ }
+ context = c
+
+ return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc)
+}
+
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) {
#force_inline __dynamic_map_reserve(header, new_count, loc)
}
diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin
new file mode 100644
index 000000000..9f7d219c3
--- /dev/null
+++ b/core/runtime/entry_unix.odin
@@ -0,0 +1,33 @@
+//+private
+//+build linux, darwin, freebsd, openbsd
+package runtime
+
+import "core:intrinsics"
+
+when ODIN_BUILD_MODE == .Dynamic {
+ @(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
+ _odin_entry_point :: proc "c" () {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ }
+ @(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/)
+ _odin_exit_point :: proc "c" () {
+ context = default_context()
+ #force_no_inline _cleanup_runtime()
+ }
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ return 0
+ }
+} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ args__ = argv[:argc]
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+}
diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin
new file mode 100644
index 000000000..125abc756
--- /dev/null
+++ b/core/runtime/entry_wasm.odin
@@ -0,0 +1,19 @@
+//+private
+//+build wasm32, wasm64
+package runtime
+
+import "core:intrinsics"
+
+when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ @(link_name="_start", linkage="strong", require, export)
+ _start :: proc "c" () {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ }
+ @(link_name="_end", linkage="strong", require, export)
+ _end :: proc "c" () {
+ context = default_context()
+ #force_no_inline _cleanup_runtime()
+ }
+}
\ No newline at end of file
diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin
new file mode 100644
index 000000000..a315c1209
--- /dev/null
+++ b/core/runtime/entry_windows.odin
@@ -0,0 +1,49 @@
+//+private
+//+build windows
+package runtime
+
+import "core:intrinsics"
+
+when ODIN_BUILD_MODE == .Dynamic {
+ @(link_name="DllMain", linkage="strong", require)
+ DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
+ context = default_context()
+
+ // Populate Windows DLL-specific global
+ dll_forward_reason = DLL_Forward_Reason(fdwReason)
+
+ switch dll_forward_reason {
+ case .Process_Attach:
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ case .Process_Detach:
+ #force_no_inline _cleanup_runtime()
+ case .Thread_Attach:
+ break
+ case .Thread_Detach:
+ break
+ }
+ return true
+ }
+} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ when ODIN_ARCH == .i386 || ODIN_NO_CRT {
+ @(link_name="mainCRTStartup", linkage="strong", require)
+ mainCRTStartup :: proc "stdcall" () -> i32 {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+ } else {
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ args__ = argv[:argc]
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin
index 7f1aeb2d7..0d0b39072 100644
--- a/core/runtime/error_checks.odin
+++ b/core/runtime/error_checks.odin
@@ -1,7 +1,7 @@
package runtime
bounds_trap :: proc "contextless" () -> ! {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
windows_trap_array_bounds()
} else {
trap()
@@ -9,7 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
}
type_assertion_trap :: proc "contextless" () -> ! {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
windows_trap_type_assertion()
} else {
trap()
@@ -21,11 +21,12 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
if 0 <= index && index < count {
return
}
+ @(cold)
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Index ")
print_i64(i64(index))
- print_string(" is out of bounds range 0:")
+ print_string(" is out of range 0..<")
print_i64(i64(count))
print_byte('\n')
bounds_trap()
@@ -35,11 +36,11 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid slice indices: ")
+ print_string(" Invalid slice indices ")
print_i64(i64(lo))
print_string(":")
print_i64(i64(hi))
- print_string(":")
+ print_string(" is out of range 0..<")
print_i64(i64(len))
print_byte('\n')
bounds_trap()
@@ -47,7 +48,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid slice indices: ")
+ print_string(" Invalid slice indices ")
print_i64(i64(lo))
print_string(":")
print_i64(i64(hi))
@@ -81,13 +82,14 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
if 0 <= low && low <= high && high <= max {
return
}
+ @(cold)
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid dynamic array values: ")
+ print_string(" Invalid dynamic array indices ")
print_i64(i64(low))
print_string(":")
print_i64(i64(high))
- print_string(":")
+ print_string(" is out of range 0..<")
print_i64(i64(max))
print_byte('\n')
bounds_trap()
@@ -97,17 +99,18 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
- if 0 <= row_index && row_index < row_count &&
+ if 0 <= row_index && row_index < row_count &&
0 <= column_index && column_index < column_count {
return
}
+ @(cold)
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Matrix indices [")
print_i64(i64(row_index))
print_string(", ")
print_i64(i64(column_index))
- print_string(" is out of bounds range [0..<")
+ print_string(" is out of range [0..<")
print_i64(i64(row_count))
print_string(", 0..<")
print_i64(i64(column_count))
@@ -119,71 +122,101 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
}
-type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
- if ok {
- return
- }
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) {
- print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid type assertion from ")
- print_typeid(from)
- print_string(" to ")
- print_typeid(to)
- print_byte('\n')
- type_assertion_trap()
- }
- handle_error(file, line, column, from, to)
-}
-
-type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
- if ok {
- return
+when ODIN_DISALLOW_RTTI {
+ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ if ok {
+ return
+ }
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32) {
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion\n")
+ type_assertion_trap()
+ }
+ handle_error(file, line, column)
}
- variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
- if id == nil || data == nil {
+ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ if ok {
+ return
+ }
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32) {
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion\n")
+ type_assertion_trap()
+ }
+ handle_error(file, line, column)
+ }
+} else {
+ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
+ if ok {
+ return
+ }
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) {
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion from ")
+ print_typeid(from)
+ print_string(" to ")
+ print_typeid(to)
+ print_byte('\n')
+ type_assertion_trap()
+ }
+ handle_error(file, line, column, from, to)
+ }
+
+ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ if ok {
+ return
+ }
+
+ variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+ if id == nil || data == nil {
+ return id
+ }
+ ti := type_info_base(type_info_of(id))
+ #partial switch v in ti.variant {
+ case Type_Info_Any:
+ return (^any)(data).id
+ case Type_Info_Union:
+ tag_ptr := uintptr(data) + v.tag_offset
+ idx := 0
+ switch v.tag_type.size {
+ case 1: idx = int((^u8)(tag_ptr)^) - 1
+ case 2: idx = int((^u16)(tag_ptr)^) - 1
+ case 4: idx = int((^u32)(tag_ptr)^) - 1
+ case 8: idx = int((^u64)(tag_ptr)^) - 1
+ case 16: idx = int((^u128)(tag_ptr)^) - 1
+ }
+ if idx < 0 {
+ return nil
+ } else if idx < len(v.variants) {
+ return v.variants[idx].id
+ }
+ }
return id
}
- ti := type_info_base(type_info_of(id))
- #partial switch v in ti.variant {
- case Type_Info_Any:
- return (^any)(data).id
- case Type_Info_Union:
- tag_ptr := uintptr(data) + v.tag_offset
- idx := 0
- switch v.tag_type.size {
- case 1: idx = int((^u8)(tag_ptr)^) - 1
- case 2: idx = int((^u16)(tag_ptr)^) - 1
- case 4: idx = int((^u32)(tag_ptr)^) - 1
- case 8: idx = int((^u64)(tag_ptr)^) - 1
- case 16: idx = int((^u128)(tag_ptr)^) - 1
- }
- if idx < 0 {
- return nil
- } else if idx < len(v.variants) {
- return v.variants[idx].id
+
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+
+ actual := variant_type(from, from_data)
+
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion from ")
+ print_typeid(from)
+ print_string(" to ")
+ print_typeid(to)
+ if actual != from {
+ print_string(", actual type: ")
+ print_typeid(actual)
}
+ print_byte('\n')
+ type_assertion_trap()
}
- return id
+ handle_error(file, line, column, from, to, from_data)
}
-
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
-
- actual := variant_type(from, from_data)
-
- print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid type assertion from ")
- print_typeid(from)
- print_string(" to ")
- print_typeid(to)
- if actual != from {
- print_string(", actual type: ")
- print_typeid(actual)
- }
- print_byte('\n')
- type_assertion_trap()
- }
- handle_error(file, line, column, from, to, from_data)
}
@@ -191,6 +224,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
if 0 <= len {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
print_caller_location(loc)
print_string(" Invalid slice length for make: ")
@@ -205,6 +239,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #
if 0 <= len && len <= cap {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
print_caller_location(loc)
print_string(" Invalid dynamic array parameters for make: ")
@@ -221,6 +256,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
if 0 <= cap {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
print_caller_location(loc)
print_string(" Invalid map capacity for make: ")
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index ed58f4318..30798f623 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -2,13 +2,15 @@ package runtime
import "core:intrinsics"
+@(private="file")
+IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64
+
@(private)
RUNTIME_LINKAGE :: "strong" when (
(ODIN_USE_SEPARATE_MODULES ||
- ODIN_BUILD_MODE == "dynamic" ||
+ ODIN_BUILD_MODE == .Dynamic ||
!ODIN_NO_CRT) &&
- !(ODIN_ARCH == "wasm32" ||
- ODIN_ARCH == "wasm64")) else "internal"
+ !IS_WASM) else "internal"
RUNTIME_REQUIRE :: true
@@ -35,10 +37,8 @@ bswap_64 :: proc "contextless" (x: u64) -> u64 {
bswap_128 :: proc "contextless" (x: u128) -> u128 {
z := transmute([4]u32)x
- z[0] = bswap_32(z[3])
- z[1] = bswap_32(z[2])
- z[2] = bswap_32(z[1])
- z[3] = bswap_32(z[0])
+ z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
+ z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
return transmute(u128)z
}
@@ -752,6 +752,9 @@ extendhfsf2 :: proc "c" (value: u16) -> f32 {
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
floattidf :: proc "c" (a: i128) -> f64 {
+when IS_WASM {
+ return 0
+} else {
DBL_MANT_DIG :: 53
if a == 0 {
return 0.0
@@ -791,10 +794,14 @@ floattidf :: proc "c" (a: i128) -> f64 {
fb[0] = u32(a) // mantissa-low
return transmute(f64)fb
}
+}
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
+when IS_WASM {
+ return 0
+} else {
DBL_MANT_DIG :: 53
if a == 0 {
return 0.0
@@ -832,6 +839,7 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
fb[0] = u32(a) // mantissa-low
return transmute(f64)fb
}
+}
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 8a14eba08..89c196fc2 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -143,18 +143,36 @@ print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
print_string(file_path)
- print_byte('(')
- print_u64(u64(line))
- print_byte(':')
- print_u64(u64(column))
- print_byte(')')
+ when ODIN_ERROR_POS_STYLE == .Default {
+ print_byte('(')
+ print_u64(u64(line))
+ print_byte(':')
+ print_u64(u64(column))
+ print_byte(')')
+ } else when ODIN_ERROR_POS_STYLE == .Unix {
+ print_byte(':')
+ print_u64(u64(line))
+ print_byte(':')
+ print_u64(u64(column))
+ print_byte(':')
+ } else {
+ #panic("unhandled ODIN_ERROR_POS_STYLE")
+ }
}
print_typeid :: proc "contextless" (id: typeid) {
- if id == nil {
- print_string("nil")
+ when ODIN_DISALLOW_RTTI {
+ if id == nil {
+ print_string("nil")
+ } else {
+ print_string("")
+ }
} else {
- ti := type_info_of(id)
- print_type(ti)
+ if id == nil {
+ print_string("nil")
+ } else {
+ ti := type_info_of(id)
+ print_type(ti)
+ }
}
}
print_type :: proc "contextless" (ti: ^Type_Info) {
@@ -250,6 +268,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_type(info.elem)
case Type_Info_Enumerated_Array:
+ if info.is_sparse {
+ print_string("#sparse")
+ }
print_byte('[')
print_type(info.index)
print_byte(']')
diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin
index fe37c7e7d..782efa773 100644
--- a/core/runtime/procs.odin
+++ b/core/runtime/procs.odin
@@ -1,10 +1,10 @@
package runtime
-when ODIN_NO_CRT && ODIN_OS == "windows" {
+when ODIN_NO_CRT && ODIN_OS == .Windows {
foreign import lib "system:NtDll.lib"
@(private="file")
- @(default_calling_convention="std")
+ @(default_calling_convention="stdcall")
foreign lib {
RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
@@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
RtlMoveMemory(dst, src, len)
return dst
}
-} else when ODIN_NO_CRT || (ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64") {
+} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64) {
@(link_name="memset", linkage="strong", require)
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
if ptr != nil && len != 0 {
@@ -42,7 +42,6 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
if dst != src {
d, s := ([^]byte)(dst), ([^]byte)(src)
- d_end, s_end := d[len:], s[len:]
for i := len-1; i >= 0; i -= 1 {
d[i] = s[i]
}
@@ -54,7 +53,6 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
if dst != src {
d, s := ([^]byte)(dst), ([^]byte)(src)
- d_end, s_end := d[len:], s[len:]
for i := len-1; i >= 0; i -= 1 {
d[i] = s[i]
}
diff --git a/core/runtime/procs_darwin.odin b/core/runtime/procs_darwin.odin
new file mode 100644
index 000000000..b54a28dcc
--- /dev/null
+++ b/core/runtime/procs_darwin.odin
@@ -0,0 +1,21 @@
+//+private
+package runtime
+
+foreign import "system:Foundation.framework"
+
+import "core:intrinsics"
+
+objc_id :: ^intrinsics.objc_object
+objc_Class :: ^intrinsics.objc_class
+objc_SEL :: ^intrinsics.objc_selector
+
+foreign Foundation {
+ objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
+ sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
+ objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
+
+ objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+ objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
+ objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
+ objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+}
diff --git a/core/runtime/procs_wasm32.odin b/core/runtime/procs_wasm32.odin
index 5caf6d2f8..2a4210c1e 100644
--- a/core/runtime/procs_wasm32.odin
+++ b/core/runtime/procs_wasm32.odin
@@ -1,23 +1,40 @@
//+build wasm32
package runtime
+@(private="file")
+ti_int :: struct #raw_union {
+ using s: struct { lo, hi: u64 },
+ all: i128,
+}
+
@(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "c" (a: i64, b_: i32) -> i64 {
- /*
+__ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
+ bits_in_dword :: size_of(u32)*8
b := u32(b_)
- input := transmute([2]i32)a
- result: [2]i32
- if b & 32 != 0 {
- result[0] = 0
- result[1] = input[0] << (b - 32)
+
+ input, result: ti_int
+ input.all = a
+ if b & bits_in_dword != 0 {
+ result.lo = 0
+ result.hi = input.lo << (b-bits_in_dword)
} else {
if b == 0 {
return a
}
- result[0] = input[0]<>(32-b))
+ result.lo = input.lo<>(bits_in_dword-b))
}
- return transmute(i64)result
- */
- return 0
+ return result.all
+}
+
+
+@(link_name="__multi3", linkage="strong")
+__multi3 :: proc "c" (a, b: i128) -> i128 {
+ x, y, r: ti_int
+
+ x.all = a
+ y.all = b
+ r.all = i128(x.lo * y.lo) // TODO this is incorrect
+ r.hi += x.hi*y.lo + x.lo*y.hi
+ return r.all
}
\ No newline at end of file
diff --git a/core/runtime/procs_windows_amd64.asm b/core/runtime/procs_windows_amd64.asm
new file mode 100644
index 000000000..660f8982a
--- /dev/null
+++ b/core/runtime/procs_windows_amd64.asm
@@ -0,0 +1,13 @@
+global __chkstk
+global _tls_index
+global _fltused
+
+section .data
+ _tls_index: dd 0
+ _fltused: dd 0x9875
+
+
+section .text
+__chkstk: ; proc "c" (rawptr)
+ ; TODO implement correctly
+ ret
\ No newline at end of file
diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin
index 394df14e6..e430357be 100644
--- a/core/runtime/procs_windows_amd64.odin
+++ b/core/runtime/procs_windows_amd64.odin
@@ -20,13 +20,6 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
}
when ODIN_NO_CRT {
- @(private, export, link_name="_tls_index")
- _tls_index: u32
-
- @(private, export, link_name="_fltused")
- _fltused: i32 = 0x9875
-
- @(private, export, link_name="__chkstk")
- __chkstk :: proc "c" (rawptr) {
- }
-}
\ No newline at end of file
+ @(require)
+ foreign import crt_lib "procs_windows_amd64.asm"
+}
diff --git a/core/runtime/procs_windows_386.odin b/core/runtime/procs_windows_i386.odin
similarity index 100%
rename from core/runtime/procs_windows_386.odin
rename to core/runtime/procs_windows_i386.odin
diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin
index 1fd1b5f84..87ef73c2c 100644
--- a/core/runtime/udivmod128.odin
+++ b/core/runtime/udivmod128.odin
@@ -11,7 +11,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
q, r: [2]u64
sr: u32 = 0
- low :: 1 when ODIN_ENDIAN == "big" else 0
+ low :: 1 when ODIN_ENDIAN == .Big else 0
high :: 1 - low
U64_BITS :: 8*size_of(u64)
U128_BITS :: 8*size_of(u128)
diff --git a/core/simd/simd.odin b/core/simd/simd.odin
new file mode 100644
index 000000000..a0a4df28d
--- /dev/null
+++ b/core/simd/simd.odin
@@ -0,0 +1,187 @@
+package simd
+
+import "core:builtin"
+import "core:intrinsics"
+
+// 128-bit vector aliases
+u8x16 :: #simd[16]u8
+i8x16 :: #simd[16]i8
+u16x8 :: #simd[8]u16
+i16x8 :: #simd[8]i16
+u32x4 :: #simd[4]u32
+i32x4 :: #simd[4]i32
+u64x2 :: #simd[2]u64
+i64x2 :: #simd[2]i64
+f32x4 :: #simd[4]f32
+f64x2 :: #simd[2]f64
+
+boolx16 :: #simd[16]bool
+b8x16 :: #simd[16]b8
+b16x8 :: #simd[8]b16
+b32x4 :: #simd[4]b32
+b64x2 :: #simd[2]b64
+
+// 256-bit vector aliases
+u8x32 :: #simd[32]u8
+i8x32 :: #simd[32]i8
+u16x16 :: #simd[16]u16
+i16x16 :: #simd[16]i16
+u32x8 :: #simd[8]u32
+i32x8 :: #simd[8]i32
+u64x4 :: #simd[4]u64
+i64x4 :: #simd[4]i64
+f32x8 :: #simd[8]f32
+f64x4 :: #simd[4]f64
+
+boolx32 :: #simd[32]bool
+b8x32 :: #simd[32]b8
+b16x16 :: #simd[16]b16
+b32x8 :: #simd[8]b32
+b64x4 :: #simd[4]b64
+
+// 512-bit vector aliases
+u8x64 :: #simd[64]u8
+i8x64 :: #simd[64]i8
+u16x32 :: #simd[32]u16
+i16x32 :: #simd[32]i16
+u32x16 :: #simd[16]u32
+i32x16 :: #simd[16]i32
+u64x8 :: #simd[8]u64
+i64x8 :: #simd[8]i64
+f32x16 :: #simd[16]f32
+f64x8 :: #simd[8]f64
+
+boolx64 :: #simd[64]bool
+b8x64 :: #simd[64]b8
+b16x32 :: #simd[32]b16
+b32x16 :: #simd[16]b32
+b64x8 :: #simd[8]b64
+
+
+add :: intrinsics.simd_add
+sub :: intrinsics.simd_sub
+mul :: intrinsics.simd_mul
+div :: intrinsics.simd_div // floats only
+
+// Keeps Odin's Behaviour
+// (x << y) if y <= mask else 0
+shl :: intrinsics.simd_shl
+shr :: intrinsics.simd_shr
+
+// Similar to C's Behaviour
+// x << (y & mask)
+shl_masked :: intrinsics.simd_shl_masked
+shr_masked :: intrinsics.simd_shr_masked
+
+// Saturation Arithmetic
+add_sat :: intrinsics.simd_add_sat
+sub_sat :: intrinsics.simd_sub_sat
+
+and :: intrinsics.simd_and
+or :: intrinsics.simd_or
+xor :: intrinsics.simd_xor
+and_not :: intrinsics.simd_and_not
+
+neg :: intrinsics.simd_neg
+
+abs :: intrinsics.simd_abs
+
+min :: intrinsics.simd_min
+max :: intrinsics.simd_max
+clamp :: intrinsics.simd_clamp
+
+// Return an unsigned integer of the same size as the input type
+// NOT A BOOLEAN
+// element-wise:
+// false => 0x00...00
+// true => 0xff...ff
+lanes_eq :: intrinsics.simd_lanes_eq
+lanes_ne :: intrinsics.simd_lanes_ne
+lanes_lt :: intrinsics.simd_lanes_lt
+lanes_le :: intrinsics.simd_lanes_le
+lanes_gt :: intrinsics.simd_lanes_gt
+lanes_ge :: intrinsics.simd_lanes_ge
+
+// extract :: proc(a: #simd[N]T, idx: uint) -> T
+extract :: intrinsics.simd_extract
+// replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T
+replace :: intrinsics.simd_replace
+
+reduce_add_ordered :: intrinsics.simd_reduce_add_ordered
+reduce_mul_ordered :: intrinsics.simd_reduce_mul_ordered
+reduce_min :: intrinsics.simd_reduce_min
+reduce_max :: intrinsics.simd_reduce_max
+reduce_and :: intrinsics.simd_reduce_and
+reduce_or :: intrinsics.simd_reduce_or
+reduce_xor :: intrinsics.simd_reduce_xor
+
+// swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T
+swizzle :: builtin.swizzle
+
+// shuffle :: proc(a, b: #simd[N]T, indices: #simd[max 2*N]u32) -> #simd[len(indices)]T
+shuffle :: intrinsics.simd_shuffle
+
+// select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T
+select :: intrinsics.simd_select
+
+
+sqrt :: intrinsics.sqrt
+ceil :: intrinsics.simd_ceil
+floor :: intrinsics.simd_floor
+trunc :: intrinsics.simd_trunc
+nearest :: intrinsics.simd_nearest
+
+to_bits :: intrinsics.simd_to_bits
+
+lanes_reverse :: intrinsics.simd_lanes_reverse
+
+lanes_rotate_left :: intrinsics.simd_lanes_rotate_left
+lanes_rotate_right :: intrinsics.simd_lanes_rotate_right
+
+count_ones :: intrinsics.count_ones
+count_zeros :: intrinsics.count_zeros
+count_trailing_zeros :: intrinsics.count_trailing_zeros
+count_leading_zeros :: intrinsics.count_leading_zeros
+reverse_bits :: intrinsics.reverse_bits
+
+fused_mul_add :: intrinsics.fused_mul_add
+fma :: intrinsics.fused_mul_add
+
+to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANES]E {
+ return (^[LANES]E)(v)
+}
+to_array :: #force_inline proc "contextless" (v: #simd[$LANES]$E) -> [LANES]E {
+ return transmute([LANES]E)(v)
+}
+from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E {
+ return transmute(#simd[LANES]E)v
+}
+
+from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T {
+ assert(len(slice) >= LANES, "slice length must be a least the number of lanes")
+ array: [LANES]E
+ #no_bounds_check for i in 0.. T where intrinsics.type_is_integer(E) {
+ return xor(v, T(~E(0)))
+}
+
+copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
+ neg_zero := to_bits(T(-0.0))
+ sign_bit := to_bits(sign) & neg_zero
+ magnitude := to_bits(v) &~ neg_zero
+ return transmute(T)(sign_bit|magnitude)
+}
+
+signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
+ is_nan := lanes_ne(v, v)
+ return select(is_nan, v, copysign(T(1), v))
+}
+
+recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
+ return T(1) / v
+}
diff --git a/core/simd/x86/abm.odin b/core/simd/x86/abm.odin
new file mode 100644
index 000000000..79b806242
--- /dev/null
+++ b/core/simd/x86/abm.odin
@@ -0,0 +1,24 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+
+@(require_results, enable_target_feature="lzcnt")
+_lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 {
+ return intrinsics.count_leading_zeros(x)
+}
+@(require_results, enable_target_feature="popcnt")
+_popcnt32 :: #force_inline proc "c" (x: u32) -> i32 {
+ return i32(intrinsics.count_ones(x))
+}
+
+when ODIN_ARCH == .amd64 {
+ @(require_results, enable_target_feature="lzcnt")
+ _lzcnt_u64 :: #force_inline proc "c" (x: u64) -> u64 {
+ return intrinsics.count_leading_zeros(x)
+ }
+ @(require_results, enable_target_feature="popcnt")
+ _popcnt64 :: #force_inline proc "c" (x: u64) -> i32 {
+ return i32(intrinsics.count_ones(x))
+ }
+}
\ No newline at end of file
diff --git a/core/simd/x86/adx.odin b/core/simd/x86/adx.odin
new file mode 100644
index 000000000..d03cffcff
--- /dev/null
+++ b/core/simd/x86/adx.odin
@@ -0,0 +1,56 @@
+//+build i386, amd64
+package simd_x86
+
+@(require_results)
+_addcarry_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
+ x, y := llvm_addcarry_u32(c_in, a, b)
+ out^ = y
+ return x
+}
+@(require_results)
+_addcarryx_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
+ return llvm_addcarryx_u32(c_in, a, b, out)
+}
+@(require_results)
+_subborrow_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
+ x, y := llvm_subborrow_u32(c_in, a, b)
+ out^ = y
+ return x
+}
+
+when ODIN_ARCH == .amd64 {
+ @(require_results)
+ _addcarry_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
+ x, y := llvm_addcarry_u64(c_in, a, b)
+ out^ = y
+ return x
+ }
+ @(require_results)
+ _addcarryx_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
+ return llvm_addcarryx_u64(c_in, a, b, out)
+ }
+ @(require_results)
+ _subborrow_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
+ x, y := llvm_subborrow_u64(c_in, a, b)
+ out^ = y
+ return x
+ }
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.addcarry.32")
+ llvm_addcarry_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) ---
+ @(link_name="llvm.x86.addcarryx.u32")
+ llvm_addcarryx_u32 :: proc(a: u8, b: u32, c: u32, d: rawptr) -> u8 ---
+ @(link_name="llvm.x86.subborrow.32")
+ llvm_subborrow_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) ---
+
+ // amd64 only
+ @(link_name="llvm.x86.addcarry.64")
+ llvm_addcarry_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) ---
+ @(link_name="llvm.x86.addcarryx.u64")
+ llvm_addcarryx_u64 :: proc(a: u8, b: u64, c: u64, d: rawptr) -> u8 ---
+ @(link_name="llvm.x86.subborrow.64")
+ llvm_subborrow_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) ---
+}
diff --git a/core/simd/x86/cmpxchg16b.odin b/core/simd/x86/cmpxchg16b.odin
new file mode 100644
index 000000000..d575dd9df
--- /dev/null
+++ b/core/simd/x86/cmpxchg16b.odin
@@ -0,0 +1,8 @@
+//+build amd64
+package simd_x86
+
+import "core:intrinsics"
+
+cmpxchg16b :: #force_inline proc "c" (dst: ^u128, old, new: u128, $success, $failure: intrinsics.Atomic_Memory_Order) -> (val: u128) {
+ return intrinsics.atomic_compare_exchange_strong_explicit(dst, old, new, success, failure)
+}
\ No newline at end of file
diff --git a/core/simd/x86/cpu.odin b/core/simd/x86/cpu.odin
new file mode 100644
index 000000000..14e90c0f0
--- /dev/null
+++ b/core/simd/x86/cpu.odin
@@ -0,0 +1,94 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+
+// cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
+cpuid :: intrinsics.x86_cpuid
+
+// xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
+xgetbv :: intrinsics.x86_xgetbv
+
+
+CPU_Feature :: enum u64 {
+ aes, // AES hardware implementation (AES NI)
+ adx, // Multi-precision add-carry instruction extensions
+ avx, // Advanced vector extension
+ avx2, // Advanced vector extension 2
+ bmi1, // Bit manipulation instruction set 1
+ bmi2, // Bit manipulation instruction set 2
+ erms, // Enhanced REP for MOVSB and STOSB
+ fma, // Fused-multiply-add instructions
+ os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
+ pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM
+ popcnt, // Hamming weight instruction POPCNT.
+ rdrand, // RDRAND instruction (on-chip random number generator)
+ rdseed, // RDSEED instruction (on-chip random number generator)
+ sse2, // Streaming SIMD extension 2 (always available on amd64)
+ sse3, // Streaming SIMD extension 3
+ ssse3, // Supplemental streaming SIMD extension 3
+ sse41, // Streaming SIMD extension 4 and 4.1
+ sse42, // Streaming SIMD extension 4 and 4.2
+}
+
+CPU_Features :: distinct bit_set[CPU_Feature; u64]
+
+cpu_features: Maybe(CPU_Features)
+
+@(init, private)
+init_cpu_features :: proc "c" () {
+ is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
+ return hwc&value != 0
+ }
+ try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
+ if is_set(hwc, value) {
+ set^ += {feature}
+ }
+ }
+
+ max_id, _, _, _ := cpuid(0, 0)
+ if max_id < 1 {
+ return
+ }
+
+ set: CPU_Features
+
+ _, _, ecx1, edx1 := cpuid(1, 0)
+
+ try_set(&set, .sse2, 26, edx1)
+ try_set(&set, .sse3, 0, ecx1)
+ try_set(&set, .pclmulqdq, 1, ecx1)
+ try_set(&set, .ssse3, 9, ecx1)
+ try_set(&set, .fma, 12, ecx1)
+ try_set(&set, .sse41, 19, ecx1)
+ try_set(&set, .sse42, 20, ecx1)
+ try_set(&set, .popcnt, 23, ecx1)
+ try_set(&set, .aes, 25, ecx1)
+ try_set(&set, .os_xsave, 27, ecx1)
+ try_set(&set, .rdrand, 30, ecx1)
+
+ os_supports_avx := false
+ if .os_xsave in set {
+ eax, _ := xgetbv(0)
+ os_supports_avx = is_set(1, eax) && is_set(2, eax)
+ }
+ if os_supports_avx {
+ try_set(&set, .avx, 28, ecx1)
+ }
+
+ if max_id < 7 {
+ return
+ }
+
+ _, ebx7, _, _ := cpuid(7, 0)
+ try_set(&set, .bmi1, 3, ebx7)
+ if os_supports_avx {
+ try_set(&set, .avx2, 5, ebx7)
+ }
+ try_set(&set, .bmi2, 8, ebx7)
+ try_set(&set, .erms, 9, ebx7)
+ try_set(&set, .rdseed, 18, ebx7)
+ try_set(&set, .adx, 19, ebx7)
+
+ cpu_features = set
+}
diff --git a/core/simd/x86/fxsr.odin b/core/simd/x86/fxsr.odin
new file mode 100644
index 000000000..cd78de7d4
--- /dev/null
+++ b/core/simd/x86/fxsr.odin
@@ -0,0 +1,36 @@
+//+build i386, amd64
+package simd_x86
+
+@(enable_target_feature="fxsr")
+_fxsave :: #force_inline proc "c" (mem_addr: rawptr) {
+ fxsave(mem_addr)
+}
+@(enable_target_feature="fxsr")
+_fxrstor :: #force_inline proc "c" (mem_addr: rawptr) {
+ fxrstor(mem_addr)
+}
+
+when ODIN_ARCH == .amd64 {
+ @(enable_target_feature="fxsr")
+ _fxsave64 :: #force_inline proc "c" (mem_addr: rawptr) {
+ fxsave64(mem_addr)
+ }
+ @(enable_target_feature="fxsr")
+ _fxrstor64 :: #force_inline proc "c" (mem_addr: rawptr) {
+ fxrstor64(mem_addr)
+ }
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.fxsave")
+ fxsave :: proc(p: rawptr) ---
+ @(link_name="llvm.x86.fxrstor")
+ fxrstor :: proc(p: rawptr) ---
+
+ // amd64 only
+ @(link_name="llvm.x86.fxsave64")
+ fxsave64 :: proc(p: rawptr) ---
+ @(link_name="llvm.x86.fxrstor64")
+ fxrstor64 :: proc(p: rawptr) ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/pclmulqdq.odin b/core/simd/x86/pclmulqdq.odin
new file mode 100644
index 000000000..692fb7ce1
--- /dev/null
+++ b/core/simd/x86/pclmulqdq.odin
@@ -0,0 +1,13 @@
+//+build i386, amd64
+package simd_x86
+
+@(require_results, enable_target_feature="pclmulqdq")
+_mm_clmulepi64_si128 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
+ return pclmulqdq(a, b, u8(IMM8))
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.pclmulqdq")
+ pclmulqdq :: proc(a, round_key: __m128i, #const imm8: u8) -> __m128i ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/rdtsc.odin b/core/simd/x86/rdtsc.odin
new file mode 100644
index 000000000..54024c3f2
--- /dev/null
+++ b/core/simd/x86/rdtsc.odin
@@ -0,0 +1,20 @@
+//+build i386, amd64
+package simd_x86
+
+@(require_results)
+_rdtsc :: #force_inline proc "c" () -> u64 {
+ return rdtsc()
+}
+
+@(require_results)
+__rdtscp :: #force_inline proc "c" (aux: ^u32) -> u64 {
+ return rdtscp(aux)
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.rdtsc")
+ rdtsc :: proc() -> u64 ---
+ @(link_name="llvm.x86.rdtscp")
+ rdtscp :: proc(aux: rawptr) -> u64 ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/sha.odin b/core/simd/x86/sha.odin
new file mode 100644
index 000000000..f015f4b8a
--- /dev/null
+++ b/core/simd/x86/sha.odin
@@ -0,0 +1,49 @@
+//+build i386, amd64
+package simd_x86
+
+@(require_results, enable_target_feature="sha")
+_mm_sha1msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)sha1msg1(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha1msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)sha1msg2(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha1nexte_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)sha1nexte(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha1rnds4_epu32 :: #force_inline proc "c" (a, b: __m128i, $FUNC: u32) -> __m128i where 0 <= FUNC, FUNC <= 3 {
+ return transmute(__m128i)sha1rnds4(transmute(i32x4)a, transmute(i32x4)b, u8(FUNC & 0xff))
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha256msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)sha256msg1(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha256msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)sha256msg2(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sha")
+_mm_sha256rnds2_epu32 :: #force_inline proc "c" (a, b, k: __m128i) -> __m128i {
+ return transmute(__m128i)sha256rnds2(transmute(i32x4)a, transmute(i32x4)b, transmute(i32x4)k)
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.sha1msg1")
+ sha1msg1 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sha1msg2")
+ sha1msg2 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sha1nexte")
+ sha1nexte :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sha1rnds4")
+ sha1rnds4 :: proc(a, b: i32x4, #const c: u8) -> i32x4 ---
+ @(link_name="llvm.x86.sha256msg1")
+ sha256msg1 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sha256msg2")
+ sha256msg2 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sha256rnds2")
+ sha256rnds2 :: proc(a, b, k: i32x4) -> i32x4 ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/sse.odin b/core/simd/x86/sse.odin
new file mode 100644
index 000000000..3efdeccba
--- /dev/null
+++ b/core/simd/x86/sse.odin
@@ -0,0 +1,618 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+import "core:simd"
+
+// _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w)
+_MM_SHUFFLE :: intrinsics.simd_x86__MM_SHUFFLE
+
+_MM_HINT_T0 :: 3
+_MM_HINT_T1 :: 2
+_MM_HINT_T2 :: 1
+_MM_HINT_NTA :: 0
+_MM_HINT_ET0 :: 7
+_MM_HINT_ET1 :: 6
+
+
+_MM_EXCEPT_INVALID :: 0x0001
+_MM_EXCEPT_DENORM :: 0x0002
+_MM_EXCEPT_DIV_ZERO :: 0x0004
+_MM_EXCEPT_OVERFLOW :: 0x0008
+_MM_EXCEPT_UNDERFLOW :: 0x0010
+_MM_EXCEPT_INEXACT :: 0x0020
+_MM_EXCEPT_MASK :: 0x003f
+
+_MM_MASK_INVALID :: 0x0080
+_MM_MASK_DENORM :: 0x0100
+_MM_MASK_DIV_ZERO :: 0x0200
+_MM_MASK_OVERFLOW :: 0x0400
+_MM_MASK_UNDERFLOW :: 0x0800
+_MM_MASK_INEXACT :: 0x1000
+_MM_MASK_MASK :: 0x1f80
+
+_MM_ROUND_NEAREST :: 0x0000
+_MM_ROUND_DOWN :: 0x2000
+_MM_ROUND_UP :: 0x4000
+_MM_ROUND_TOWARD_ZERO :: 0x6000
+
+_MM_ROUND_MASK :: 0x6000
+
+_MM_FLUSH_ZERO_MASK :: 0x8000
+_MM_FLUSH_ZERO_ON :: 0x8000
+_MM_FLUSH_ZERO_OFF :: 0x0000
+
+
+@(require_results, enable_target_feature="sse")
+_mm_add_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return addss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_add_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.add(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_sub_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return subss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_sub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.sub(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_mul_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return mulss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_mul_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.mul(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_div_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return divss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_div_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.div(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_sqrt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return sqrtss(a)
+}
+@(require_results, enable_target_feature="sse")
+_mm_sqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return sqrtps(a)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_rcp_ss :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return rcpss(a)
+}
+@(require_results, enable_target_feature="sse")
+_mm_rcp_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return rcpps(a)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_rsqrt_ss :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return rsqrtss(a)
+}
+@(require_results, enable_target_feature="sse")
+_mm_rsqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return rsqrtps(a)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_min_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return minss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_min_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return minps(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_max_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return maxss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_max_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return maxps(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_and_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return transmute(__m128)simd.and(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_andnot_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return transmute(__m128)simd.and_not(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_or_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return transmute(__m128)simd.or(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_xor_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return transmute(__m128)simd.xor(transmute(__m128i)a, transmute(__m128i)b)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_cmpeq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 0)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmplt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 1)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmple_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 2)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpgt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, cmpss(b, a, 1), 4, 1, 2, 3)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, cmpss(b, a, 2), 4, 1, 2, 3)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpneq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 4)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnlt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 5)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnle_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 6)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpngt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, cmpss(b, a, 5), 4, 1, 2, 3)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, cmpss(b, a, 6), 4, 1, 2, 3)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 7)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpunord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpss(a, b, 3)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_cmpeq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 0)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmplt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 1)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmple_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 2)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpgt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 1)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 2)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpneq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 4)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnlt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 5)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnle_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(a, b, 6)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpngt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 5)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpnge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 6)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 7)
+}
+@(require_results, enable_target_feature="sse")
+_mm_cmpunord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return cmpps(b, a, 3)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_comieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comieq_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_comilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comilt_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_comile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comile_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_comigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comigt_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_comige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comige_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_comineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return comineq_ss(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_ucomieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomieq_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_ucomilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomilt_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_ucomile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomile_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_ucomigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomigt_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_ucomige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomige_ss(a, b)
+}
+@(require_results, enable_target_feature="sse")
+_mm_ucomineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
+ return ucomineq_ss(a, b)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_cvtss_si32 :: #force_inline proc "c" (a: __m128) -> i32 {
+ return cvtss2si(a)
+}
+_mm_cvt_ss2si :: _mm_cvtss_si32
+_mm_cvttss_si32 :: _mm_cvtss_si32
+
+@(require_results, enable_target_feature="sse")
+_mm_cvtss_f32 :: #force_inline proc "c" (a: __m128) -> f32 {
+ return simd.extract(a, 0)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_cvtsi32_ss :: #force_inline proc "c" (a: __m128, b: i32) -> __m128 {
+ return cvtsi2ss(a, b)
+}
+_mm_cvt_si2ss :: _mm_cvtsi32_ss
+
+
+@(require_results, enable_target_feature="sse")
+_mm_set_ss :: #force_inline proc "c" (a: f32) -> __m128 {
+ return __m128{a, 0, 0, 0}
+}
+@(require_results, enable_target_feature="sse")
+_mm_set1_ps :: #force_inline proc "c" (a: f32) -> __m128 {
+ return __m128(a)
+}
+_mm_set_ps1 :: _mm_set1_ps
+
+@(require_results, enable_target_feature="sse")
+_mm_set_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 {
+ return __m128{d, c, b, a}
+}
+@(require_results, enable_target_feature="sse")
+_mm_setr_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 {
+ return __m128{a, b, c, d}
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_setzero_ps :: #force_inline proc "c" () -> __m128 {
+ return __m128{0, 0, 0, 0}
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_shuffle_ps :: #force_inline proc "c" (a, b: __m128, $MASK: u32) -> __m128 {
+ return simd.shuffle(
+ a, b,
+ u32(MASK) & 0b11,
+ (u32(MASK)>>2) & 0b11,
+ ((u32(MASK)>>4) & 0b11)+4,
+ ((u32(MASK)>>6) & 0b11)+4)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_unpackhi_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, b, 2, 6, 3, 7)
+}
+@(require_results, enable_target_feature="sse")
+_mm_unpacklo_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, b, 0, 4, 1, 5)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_movehl_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, b, 6, 7, 2, 3)
+}
+@(require_results, enable_target_feature="sse")
+_mm_movelh_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, b, 0, 1, 4, 5)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_movemask_ps :: #force_inline proc "c" (a: __m128) -> u32 {
+ return movmskps(a)
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_load_ss :: #force_inline proc "c" (p: ^f32) -> __m128 {
+ return __m128{p^, 0, 0, 0}
+}
+@(require_results, enable_target_feature="sse")
+_mm_load1_ps :: #force_inline proc "c" (p: ^f32) -> __m128 {
+ a := p^
+ return __m128(a)
+}
+_mm_load_ps1 :: _mm_load1_ps
+
+@(require_results, enable_target_feature="sse")
+_mm_load_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
+ return (^__m128)(p)^
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_loadu_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
+ dst := _mm_undefined_ps()
+ intrinsics.mem_copy_non_overlapping(&dst, p, size_of(__m128))
+ return dst
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_loadr_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
+ return simd.lanes_reverse(_mm_load_ps(p))
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_loadu_si64 :: #force_inline proc "c" (mem_addr: rawptr) -> __m128i {
+ a := intrinsics.unaligned_load((^i64)(mem_addr))
+ return __m128i{a, 0}
+}
+
+@(enable_target_feature="sse")
+_mm_store_ss :: #force_inline proc "c" (p: ^f32, a: __m128) {
+ p^ = simd.extract(a, 0)
+}
+
+@(enable_target_feature="sse")
+_mm_store1_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
+ b := simd.swizzle(a, 0, 0, 0, 0)
+ (^__m128)(p)^ = b
+}
+_mm_store_ps1 :: _mm_store1_ps
+
+
+@(enable_target_feature="sse")
+_mm_store_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
+ (^__m128)(p)^ = a
+}
+@(enable_target_feature="sse")
+_mm_storeu_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
+ b := a
+ intrinsics.mem_copy_non_overlapping(p, &b, size_of(__m128))
+}
+@(enable_target_feature="sse")
+_mm_storer_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
+ (^__m128)(p)^ = simd.lanes_reverse(a)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_move_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return simd.shuffle(a, b, 4, 1, 2, 3)
+}
+
+@(enable_target_feature="sse")
+_mm_sfence :: #force_inline proc "c" () {
+ sfence()
+}
+
+@(require_results, enable_target_feature="sse")
+_mm_getcsr :: #force_inline proc "c" () -> (result: u32) {
+ stmxcsr(&result)
+ return result
+}
+
+@(enable_target_feature="sse")
+_mm_setcsr :: #force_inline proc "c" (val: u32) {
+ val := val
+ ldmxcsr(&val)
+}
+
+@(require_results, enable_target_feature="sse")
+_MM_GET_EXCEPTION_MASK :: #force_inline proc "c" () -> u32 {
+ return _mm_getcsr() & _MM_MASK_MASK
+}
+@(require_results, enable_target_feature="sse")
+_MM_GET_EXCEPTION_STATE :: #force_inline proc "c" () -> u32 {
+ return _mm_getcsr() & _MM_EXCEPT_MASK
+}
+@(require_results, enable_target_feature="sse")
+_MM_GET_FLUSH_ZERO_MODE :: #force_inline proc "c" () -> u32 {
+ return _mm_getcsr() & _MM_FLUSH_ZERO_MASK
+}
+@(require_results, enable_target_feature="sse")
+_MM_GET_ROUNDING_MODE :: #force_inline proc "c" () -> u32 {
+ return _mm_getcsr() & _MM_ROUND_MASK
+}
+
+@(enable_target_feature="sse")
+_MM_SET_EXCEPTION_MASK :: #force_inline proc "c" (x: u32) {
+ _mm_setcsr((_mm_getcsr() &~ _MM_MASK_MASK) | x)
+}
+@(enable_target_feature="sse")
+_MM_SET_EXCEPTION_STATE :: #force_inline proc "c" (x: u32) {
+ _mm_setcsr((_mm_getcsr() &~ _MM_EXCEPT_MASK) | x)
+}
+@(enable_target_feature="sse")
+_MM_SET_FLUSH_ZERO_MODE :: #force_inline proc "c" (x: u32) {
+ _mm_setcsr((_mm_getcsr() &~ _MM_FLUSH_ZERO_MASK) | x)
+}
+@(enable_target_feature="sse")
+_MM_SET_ROUNDING_MODE :: #force_inline proc "c" (x: u32) {
+ _mm_setcsr((_mm_getcsr() &~ _MM_ROUND_MASK) | x)
+}
+
+@(enable_target_feature="sse")
+_mm_prefetch :: #force_inline proc "c" (p: rawptr, $STRATEGY: u32) {
+ prefetch(p, (STRATEGY>>2)&1, STRATEGY&3, 1)
+}
+
+
+@(require_results, enable_target_feature="sse")
+_mm_undefined_ps :: #force_inline proc "c" () -> __m128 {
+ return _mm_set1_ps(0)
+}
+
+@(enable_target_feature="sse")
+_MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) {
+ tmp0 := _mm_unpacklo_ps(row0^, row1^)
+ tmp1 := _mm_unpacklo_ps(row2^, row3^)
+ tmp2 := _mm_unpackhi_ps(row0^, row1^)
+ tmp3 := _mm_unpackhi_ps(row2^, row3^)
+
+ row0^ = _mm_movelh_ps(tmp0, tmp2)
+ row1^ = _mm_movelh_ps(tmp2, tmp0)
+ row2^ = _mm_movelh_ps(tmp1, tmp3)
+ row3^ = _mm_movelh_ps(tmp3, tmp1)
+}
+
+@(enable_target_feature="sse")
+_mm_stream_ps :: #force_inline proc "c" (addr: [^]f32, a: __m128) {
+ intrinsics.non_temporal_store((^__m128)(addr), a)
+}
+
+when ODIN_ARCH == .amd64 {
+ @(require_results, enable_target_feature="sse")
+ _mm_cvtss_si64 :: #force_inline proc "c"(a: __m128) -> i64 {
+ return cvtss2si64(a)
+ }
+ @(require_results, enable_target_feature="sse")
+ _mm_cvttss_si64 :: #force_inline proc "c"(a: __m128) -> i64 {
+ return cvttss2si64(a)
+ }
+ @(require_results, enable_target_feature="sse")
+ _mm_cvtsi64_ss :: #force_inline proc "c"(a: __m128, b: i64) -> __m128 {
+ return cvtsi642ss(a, b)
+ }
+}
+
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.sse.add.ss")
+ addss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.sub.ss")
+ subss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.mul.ss")
+ mulss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.div.ss")
+ divss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.sqrt.ss")
+ sqrtss :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.sqrt.ps")
+ sqrtps :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.rcp.ss")
+ rcpss :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.rcp.ps")
+ rcpps :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.rsqrt.ss")
+ rsqrtss :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.rsqrt.ps")
+ rsqrtps :: proc(a: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.min.ss")
+ minss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.min.ps")
+ minps :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.max.ss")
+ maxss :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.max.ps")
+ maxps :: proc(a, b: __m128) -> __m128 ---
+ @(link_name="llvm.x86.sse.movmsk.ps")
+ movmskps :: proc(a: __m128) -> u32 ---
+ @(link_name="llvm.x86.sse.cmp.ps")
+ cmpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
+ @(link_name="llvm.x86.sse.comieq.ss")
+ comieq_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.comilt.ss")
+ comilt_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.comile.ss")
+ comile_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.comigt.ss")
+ comigt_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.comige.ss")
+ comige_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.comineq.ss")
+ comineq_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomieq.ss")
+ ucomieq_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomilt.ss")
+ ucomilt_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomile.ss")
+ ucomile_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomigt.ss")
+ ucomigt_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomige.ss")
+ ucomige_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.ucomineq.ss")
+ ucomineq_ss :: proc(a, b: __m128) -> b32 ---
+ @(link_name="llvm.x86.sse.cvtss2si")
+ cvtss2si :: proc(a: __m128) -> i32 ---
+ @(link_name="llvm.x86.sse.cvttss2si")
+ cvttss2si :: proc(a: __m128) -> i32 ---
+ @(link_name="llvm.x86.sse.cvtsi2ss")
+ cvtsi2ss :: proc(a: __m128, b: i32) -> __m128 ---
+ @(link_name="llvm.x86.sse.sfence")
+ sfence :: proc() ---
+ @(link_name="llvm.x86.sse.stmxcsr")
+ stmxcsr :: proc(p: rawptr) ---
+ @(link_name="llvm.x86.sse.ldmxcsr")
+ ldmxcsr :: proc(p: rawptr) ---
+ @(link_name="llvm.prefetch")
+ prefetch :: proc(p: rawptr, #const rw, loc, ty: u32) ---
+ @(link_name="llvm.x86.sse.cmp.ss")
+ cmpss :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
+
+
+ // amd64 only
+ @(link_name="llvm.x86.sse.cvtss2si64")
+ cvtss2si64 :: proc(a: __m128) -> i64 ---
+ @(link_name="llvm.x86.sse.cvttss2si64")
+ cvttss2si64 :: proc(a: __m128) -> i64 ---
+ @(link_name="llvm.x86.sse.cvtsi642ss")
+ cvtsi642ss :: proc(a: __m128, b: i64) -> __m128 ---
+}
diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin
new file mode 100644
index 000000000..f33bd2195
--- /dev/null
+++ b/core/simd/x86/sse2.odin
@@ -0,0 +1,1191 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+import "core:simd"
+
+@(enable_target_feature="sse2")
+_mm_pause :: #force_inline proc "c" () {
+ pause()
+}
+@(enable_target_feature="sse2")
+_mm_clflush :: #force_inline proc "c" (p: rawptr) {
+ clflush(p)
+}
+@(enable_target_feature="sse2")
+_mm_lfence :: #force_inline proc "c" () {
+ lfence()
+}
+@(enable_target_feature="sse2")
+_mm_mfence :: #force_inline proc "c" () {
+ mfence()
+}
+
+@(require_results, enable_target_feature="sse2")
+_mm_add_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_add_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_add_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_add_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add(transmute(i64x2)a, transmute(i64x2)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_adds_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add_sat(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_adds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add_sat(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_adds_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add_sat(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_adds_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.add_sat(transmute(u16x8)a, transmute(u16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_avg_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pavgb(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_avg_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pavgw(transmute(u16x8)a, transmute(u16x8)b)
+}
+
+@(require_results, enable_target_feature="sse2")
+_mm_madd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaddwd(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_max_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxsw(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_max_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxub(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_min_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminsw(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_min_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminub(transmute(u8x16)a, transmute(u8x16)b)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_mulhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmulhw(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_mulhi_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmulhuw(transmute(u16x8)a, transmute(u16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_mullo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.mul(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_mul_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmuludq(transmute(u32x4)a, transmute(u32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sad_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)psadbw(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub(transmute(i64x2)a, transmute(i64x2)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_subs_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub_sat(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_subs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub_sat(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_subs_epu8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub_sat(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_subs_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.sub_sat(transmute(u16x8)a, transmute(u16x8)b)
+}
+
+
+
+@(private)
+@(require_results, enable_target_feature="sse2")
+_mm_slli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ shift :: IMM8 & 0xff
+
+ return transmute(__m128i)simd.shuffle(
+ transmute(i8x16)a,
+ i8x16(0),
+ 0 when shift > 15 else (16 - shift + 0),
+ 1 when shift > 15 else (16 - shift + 1),
+ 2 when shift > 15 else (16 - shift + 2),
+ 3 when shift > 15 else (16 - shift + 3),
+ 4 when shift > 15 else (16 - shift + 4),
+ 5 when shift > 15 else (16 - shift + 5),
+ 6 when shift > 15 else (16 - shift + 6),
+ 7 when shift > 15 else (16 - shift + 7),
+ 8 when shift > 15 else (16 - shift + 8),
+ 9 when shift > 15 else (16 - shift + 9),
+ 10 when shift > 15 else (16 - shift + 10),
+ 11 when shift > 15 else (16 - shift + 11),
+ 12 when shift > 15 else (16 - shift + 12),
+ 13 when shift > 15 else (16 - shift + 13),
+ 14 when shift > 15 else (16 - shift + 14),
+ 15 when shift > 15 else (16 - shift + 15),
+ )
+}
+
+@(private)
+@(require_results, enable_target_feature="sse2")
+_mm_srli_si128_impl :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ shift :: IMM8
+ return transmute(__m128i)simd.shuffle(
+ transmute(i8x16)a,
+ i8x16(0),
+ 0 + 16 when shift > 15 else (shift + 0),
+ 1 + 16 when shift > 15 else (shift + 1),
+ 2 + 16 when shift > 15 else (shift + 2),
+ 3 + 16 when shift > 15 else (shift + 3),
+ 4 + 16 when shift > 15 else (shift + 4),
+ 5 + 16 when shift > 15 else (shift + 5),
+ 6 + 16 when shift > 15 else (shift + 6),
+ 7 + 16 when shift > 15 else (shift + 7),
+ 8 + 16 when shift > 15 else (shift + 8),
+ 9 + 16 when shift > 15 else (shift + 9),
+ 10 + 16 when shift > 15 else (shift + 10),
+ 11 + 16 when shift > 15 else (shift + 11),
+ 12 + 16 when shift > 15 else (shift + 12),
+ 13 + 16 when shift > 15 else (shift + 13),
+ 14 + 16 when shift > 15 else (shift + 14),
+ 15 + 16 when shift > 15 else (shift + 15),
+ )
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_slli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return _mm_slli_si128_impl(a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_bslli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return _mm_slli_si128_impl(a, IMM8)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_bsrli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return _mm_srli_si128_impl(a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_slli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)pslliw(transmute(i16x8)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sll_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psllw(transmute(i16x8)a, transmute(i16x8)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_slli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psllid(transmute(i32x4)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sll_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)pslld(transmute(i32x4)a, transmute(i32x4)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_slli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)pslliq(transmute(i64x2)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psllq(transmute(i64x2)a, transmute(i64x2)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psraw(transmute(i16x8)a, transmute(i16x8)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srai_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psraid(transmute(i32x4)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sra_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psrad(transmute(i32x4)a, transmute(i32x4)count)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return _mm_srli_si128_impl(a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psrlw(transmute(i16x8)a, transmute(i16x8)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srli_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psrlid(transmute(i32x4)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srl_epi32 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psrld(transmute(i32x4)a, transmute(i32x4)count)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srli_epi64 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)psrliq(transmute(i64x2)a, IMM8)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_srl_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
+ return transmute(__m128i)psrlq(transmute(i64x2)a, transmute(i64x2)count)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_and_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return simd.and(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_andnot_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return simd.and_not(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_or_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return simd.or(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_xor_si128 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return simd.xor(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpeq_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_eq(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpeq_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_eq(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpeq_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_eq(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpgt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_gt(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpgt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_gt(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpgt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_gt(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmplt_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_lt(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmplt_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_lt(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmplt_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_lt(transmute(i32x4)a, transmute(i32x4)b)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_cvtepi32_pd :: #force_inline proc "c" (a: __m128i) -> __m128d {
+ v := transmute(i32x4)a
+ return cast(__m128d)simd.shuffle(v, v, 0, 1)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsi32_sd :: #force_inline proc "c" (a: __m128d, b: i32) -> __m128d {
+ return simd.replace(a, 0, f64(b))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtepi32_ps :: #force_inline proc "c" (a: __m128i) -> __m128 {
+ return cvtdq2ps(transmute(i32x4)a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i {
+ return transmute(__m128i)cvtps2dq(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsi32_si128 :: #force_inline proc "c" (a: i32) -> __m128i {
+ return transmute(__m128i)i32x4{a, 0, 0, 0}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsi128_si32 :: #force_inline proc "c" (a: __m128i) -> i32 {
+ return simd.extract(transmute(i32x4)a, 0)
+}
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_set_epi64x :: #force_inline proc "c" (e1, e0: i64) -> __m128i {
+ return transmute(__m128i)i64x2{e0, e1}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i {
+ return transmute(__m128i)i32x4{e0, e1, e2, e3}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i {
+ return transmute(__m128i)i16x8{e0, e1, e2, e3, e4, e5, e6, e7}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i {
+ return transmute(__m128i)i8x16{e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set1_epi64x :: #force_inline proc "c" (a: i64) -> __m128i {
+ return _mm_set_epi64x(a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set1_epi32 :: #force_inline proc "c" (a: i32) -> __m128i {
+ return _mm_set_epi32(a, a, a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set1_epi16 :: #force_inline proc "c" (a: i16) -> __m128i {
+ return _mm_set_epi16(a, a, a, a, a, a, a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set1_epi8 :: #force_inline proc "c" (a: i8) -> __m128i {
+ return _mm_set_epi8(a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setr_epi32 :: #force_inline proc "c" (e3, e2, e1, e0: i32) -> __m128i {
+ return _mm_set_epi32(e0, e1, e2, e3)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setr_epi16 :: #force_inline proc "c" (e7, e6, e5, e4, e3, e2, e1, e0: i16) -> __m128i {
+ return _mm_set_epi16(e0, e1, e2, e3, e4, e5, e6, e7)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setr_epi8 :: #force_inline proc "c" (e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0: i8) -> __m128i {
+ return _mm_set_epi8(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setzero_si128 :: #force_inline proc "c" () -> __m128i {
+ return _mm_set1_epi64x(0)
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_loadl_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
+ return _mm_set_epi64x(0, intrinsics.unaligned_load((^i64)(mem_addr)))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_load_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
+ return mem_addr^
+}
+@(require_results, enable_target_feature="sse2")
+_mm_loadu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
+ dst := _mm_undefined_si128()
+ intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128i))
+ return dst
+}
+@(enable_target_feature="sse2")
+_mm_maskmoveu_si128 :: #force_inline proc "c" (a, mask: __m128i, mem_addr: rawptr) {
+ maskmovdqu(transmute(i8x16)a, transmute(i8x16)mask, mem_addr)
+}
+@(enable_target_feature="sse2")
+_mm_store_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) {
+ mem_addr^ = a
+}
+@(enable_target_feature="sse2")
+_mm_storeu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) {
+ storeudq(mem_addr, a)
+}
+@(enable_target_feature="sse2")
+_mm_storel_epi64 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) {
+ a := a
+ intrinsics.mem_copy_non_overlapping(mem_addr, &a, 8)
+}
+@(enable_target_feature="sse2")
+_mm_stream_si128 :: #force_inline proc "c" (mem_addr: ^__m128i, a: __m128i) {
+ intrinsics.non_temporal_store(mem_addr, a)
+}
+@(enable_target_feature="sse2")
+_mm_stream_si32 :: #force_inline proc "c" (mem_addr: ^i32, a: i32) {
+ intrinsics.non_temporal_store(mem_addr, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_move_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ zero := _mm_setzero_si128()
+ return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)zero, 0, 2)
+}
+
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_packs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)packsswb(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_packs_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)packssdw(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_packus_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)packuswb(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_extract_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 {
+ return i32(simd.extract(transmute(u16x8)a, IMM8))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_insert_epi16 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i {
+ return i32(simd.replace(transmute(u16x8)a, IMM8, i16(i)))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_movemask_epi8 :: #force_inline proc "c" (a: __m128i) -> i32 {
+ return pmovmskb(transmute(i8x16)a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_shuffle_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ v := transmute(i32x4)a
+ return transmute(__m128i)simd.shuffle(
+ v,
+ v,
+ IMM8 & 0b11,
+ (IMM8 >> 2) & 0b11,
+ (IMM8 >> 4) & 0b11,
+ (IMM8 >> 6) & 0b11,
+ )
+}
+@(require_results, enable_target_feature="sse2")
+_mm_shufflehi_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ v := transmute(i16x8)a
+ return transmute(__m128i)simd.shuffle(
+ v,
+ v,
+ 0,
+ 1,
+ 2,
+ 3,
+ (IMM8 & 0b11) + 4,
+ ((IMM8 >> 2) & 0b11) + 4,
+ ((IMM8 >> 4) & 0b11) + 4,
+ ((IMM8 >> 6) & 0b11) + 4,
+ )
+}
+@(require_results, enable_target_feature="sse2")
+_mm_shufflelo_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
+ v := transmute(i16x8)a
+ return transmute(__m128i)simd.shuffle(
+ v,
+ v,
+ IMM8 & 0b11,
+ (IMM8 >> 2) & 0b11,
+ (IMM8 >> 4) & 0b11,
+ (IMM8 >> 6) & 0b11,
+ 4,
+ 5,
+ 6,
+ 7,
+ )
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpackhi_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(
+ transmute(i8x16)a,
+ transmute(i8x16)b,
+ 8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31,
+ )
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpackhi_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 4, 12, 5, 13, 6, 14, 7, 15)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpackhi_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 2, 6, 3, 7)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpackhi_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 1, 3)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpacklo_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(
+ transmute(i8x16)a,
+ transmute(i8x16)b,
+ 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23,
+ )
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpacklo_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i16x8)a, transmute(i16x8)b, 0, 8, 1, 9, 2, 10, 3, 11)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpacklo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i32x4)a, transmute(i32x4)b, 0, 4, 1, 5)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpacklo_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.shuffle(transmute(i64x2)a, transmute(i64x2)b, 0, 2)
+}
+
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_add_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(a, 0, _mm_cvtsd_f64(a) + _mm_cvtsd_f64(b))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_add_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.add(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_div_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(a, 0, _mm_cvtsd_f64(a) / _mm_cvtsd_f64(b))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_div_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.div(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_max_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return maxsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_max_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return maxpd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_min_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return minsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_min_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return minpd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_mul_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(a, 0, _mm_cvtsd_f64(a) * _mm_cvtsd_f64(b))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_mul_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.mul(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sqrt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(a, 0, _mm_cvtsd_f64(sqrtsd(b)))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sqrt_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
+ return simd.sqrt(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(a, 0, _mm_cvtsd_f64(a) - _mm_cvtsd_f64(b))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_sub_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.sub(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_and_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return transmute(__m128d)_mm_and_si128(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_andnot_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return transmute(__m128d)_mm_andnot_si128(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_or_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return transmute(__m128d)_mm_or_si128(transmute(__m128i)a, transmute(__m128i)b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_xor_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return transmute(__m128d)_mm_xor_si128(transmute(__m128i)a, transmute(__m128i)b)
+}
+
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_cmpeq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmplt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 1)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmple_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 2)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpgt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(_mm_cmplt_sd(b, a), 1, simd.extract(a, 1))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(_mm_cmple_sd(b, a), 1, simd.extract(a, 1))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 7)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpunord_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 3)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpneq_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 4)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnlt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 5)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnle_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmpsd(a, b, 6)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpngt_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(_mm_cmpnlt_sd(b, a), 1, simd.extract(a, 1))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnge_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.replace(_mm_cmpnle_sd(b, a), 1, simd.extract(a, 1))
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpeq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmplt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 1)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmple_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 2)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpgt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return _mm_cmplt_pd(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return _mm_cmple_pd(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 7)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpunord_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 3)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpneq_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 4)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnlt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 5)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnle_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return cmppd(a, b, 6)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpngt_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return _mm_cmpnlt_pd(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cmpnge_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return _mm_cmpnle_pd(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comieqsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comiltsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comilesd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comigtsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comigesd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_comineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return comineqsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomieq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomieqsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomilt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomiltsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomile_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomilesd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomigt_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomigtsd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomige_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomigesd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_ucomineq_sd :: #force_inline proc "c" (a, b: __m128d) -> i32 {
+ return ucomineqsd(a, b)
+}
+
+
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_cvtpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 {
+ return cvtpd2ps(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtps_pd :: #force_inline proc "c" (a: __m128) -> __m128d {
+ return cvtps2pd(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i {
+ return transmute(__m128i)cvtpd2dq(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 {
+ return cvtsd2si(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsd_ss :: #force_inline proc "c" (a, b: __m128d) -> __m128 {
+ return cvtsd2ss(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtsd_f64 :: #force_inline proc "c" (a: __m128d) -> f64 {
+ return simd.extract(a, 0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvtss_sd :: #force_inline proc "c" (a, b: __m128) -> __m128d {
+ return cvtss2sd(a, b)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvttpd_epi32 :: #force_inline proc "c" (a: __m128d) -> __m128i {
+ return transmute(__m128i)cvttpd2dq(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvttsd_si32 :: #force_inline proc "c" (a: __m128d) -> i32 {
+ return cvttsd2si(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_cvttps_epi32 :: #force_inline proc "c" (a: __m128) -> __m128i {
+ return transmute(__m128i)cvttps2dq(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_sd :: #force_inline proc "c" (a: f64) -> __m128d {
+ return _mm_set_pd(0.0, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set1_pd :: #force_inline proc "c" (a: f64) -> __m128d {
+ return _mm_set_pd(a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_pd1 :: #force_inline proc "c" (a: f64) -> __m128d {
+ return _mm_set_pd(a, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_set_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d {
+ return __m128d{b, a}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setr_pd :: #force_inline proc "c" (a: f64, b: f64) -> __m128d {
+ return _mm_set_pd(b, a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_setzero_pd :: #force_inline proc "c" () -> __m128d {
+ return _mm_set_pd(0.0, 0.0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_movemask_pd :: #force_inline proc "c" (a: __m128d) -> i32 {
+ return movmskpd(a)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_load_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ return (^__m128d)(mem_addr)^
+}
+@(require_results, enable_target_feature="sse2")
+_mm_load_sd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ return _mm_setr_pd(mem_addr^, 0.)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_loadh_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d {
+ return _mm_setr_pd(simd.extract(a, 0), mem_addr^)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_loadl_pd :: #force_inline proc "c" (a: __m128d, mem_addr: ^f64) -> __m128d {
+ return _mm_setr_pd(mem_addr^, simd.extract(a, 1))
+}
+@(enable_target_feature="sse2")
+_mm_stream_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ intrinsics.non_temporal_store((^__m128d)(mem_addr), a)
+}
+@(enable_target_feature="sse2")
+_mm_store_sd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ mem_addr^ = simd.extract(a, 0)
+}
+@(enable_target_feature="sse2")
+_mm_store_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ (^__m128d)(mem_addr)^ = a
+}
+@(enable_target_feature="sse2")
+_mm_storeu_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ storeupd(mem_addr, a)
+}
+@(enable_target_feature="sse2")
+_mm_store1_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0)
+}
+@(enable_target_feature="sse2")
+_mm_store_pd1 :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 0, 0)
+}
+@(enable_target_feature="sse2")
+_mm_storer_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ (^__m128d)(mem_addr)^ = simd.shuffle(a, a, 1, 0)
+}
+@(enable_target_feature="sse2")
+_mm_storeh_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ mem_addr^ = simd.extract(a, 1)
+}
+@(enable_target_feature="sse2")
+_mm_storel_pd :: #force_inline proc "c" (mem_addr: ^f64, a: __m128d) {
+ mem_addr^ = simd.extract(a, 0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_load1_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ d := mem_addr^
+ return _mm_setr_pd(d, d)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_load_pd1 :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ return _mm_load1_pd(mem_addr)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_loadr_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ a := _mm_load_pd(mem_addr)
+ return simd.shuffle(a, a, 1, 0)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_loadu_pd :: #force_inline proc "c" (mem_addr: ^f64) -> __m128d {
+ dst := _mm_undefined_pd()
+ intrinsics.mem_copy_non_overlapping(&dst, mem_addr, size_of(__m128d))
+ return dst
+}
+@(require_results, enable_target_feature="sse2")
+_mm_shuffle_pd :: #force_inline proc "c" (a, b: __m128d, $MASK: u32) -> __m128d {
+ return simd.shuffle(a, b, MASK&0b1, ((MASK>>1)&0b1) + 2)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_move_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return _mm_setr_pd(simd.extract(b, 0), simd.extract(a, 1))
+}
+
+
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_castpd_ps :: #force_inline proc "c" (a: __m128d) -> __m128 {
+ return transmute(__m128)a
+}
+@(require_results, enable_target_feature="sse2")
+_mm_castpd_si128 :: #force_inline proc "c" (a: __m128d) -> __m128i {
+ return transmute(__m128i)a
+}
+@(require_results, enable_target_feature="sse2")
+_mm_castps_pd :: #force_inline proc "c" (a: __m128) -> __m128d {
+ return transmute(__m128d)a
+}
+@(require_results, enable_target_feature="sse2")
+_mm_castps_si128 :: #force_inline proc "c" (a: __m128) -> __m128i {
+ return transmute(__m128i)a
+}
+@(require_results, enable_target_feature="sse2")
+_mm_castsi128_pd :: #force_inline proc "c" (a: __m128i) -> __m128d {
+ return transmute(__m128d)a
+}
+@(require_results, enable_target_feature="sse2")
+_mm_castsi128_ps :: #force_inline proc "c" (a: __m128i) -> __m128 {
+ return transmute(__m128)a
+}
+
+
+@(require_results, enable_target_feature="sse2")
+_mm_undefined_pd :: #force_inline proc "c" () -> __m128d {
+ return __m128d{0, 0}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_undefined_si128 :: #force_inline proc "c" () -> __m128i {
+ return __m128i{0, 0}
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpackhi_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.shuffle(a, b, 1, 3)
+}
+@(require_results, enable_target_feature="sse2")
+_mm_unpacklo_pd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return simd.shuffle(a, b, 0, 2)
+}
+
+
+when ODIN_ARCH == .amd64 {
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 {
+ return cvtsd2si64(a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 {
+ return _mm_cvtsd_si64(a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvttsd_si64 :: #force_inline proc "c" (a: __m128d) -> i64 {
+ return cvttsd2si64(a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvttsd_si64x :: #force_inline proc "c" (a: __m128d) -> i64 {
+ return _mm_cvttsd_si64(a)
+ }
+ @(enable_target_feature="sse2")
+ _mm_stream_si64 :: #force_inline proc "c" (mem_addr: ^i64, a: i64) {
+ intrinsics.non_temporal_store(mem_addr, a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi64_si128 :: #force_inline proc "c" (a: i64) -> __m128i {
+ return _mm_set_epi64x(0, a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi64x_si128 :: #force_inline proc "c" (a: i64) -> __m128i {
+ return _mm_cvtsi64_si128(a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi128_si64 :: #force_inline proc "c" (a: __m128i) -> i64 {
+ return simd.extract(transmute(i64x2)a, 0)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi128_si64x :: #force_inline proc "c" (a: __m128i) -> i64 {
+ return _mm_cvtsi128_si64(a)
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi64_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d {
+ return simd.replace(a, 0, f64(b))
+ }
+ @(require_results, enable_target_feature="sse2")
+ _mm_cvtsi64x_sd :: #force_inline proc "c" (a: __m128d, b: i64) -> __m128d {
+ return _mm_cvtsi64_sd(a, b)
+ }
+}
+
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name="llvm.x86.sse2.pause")
+ pause :: proc() ---
+ @(link_name="llvm.x86.sse2.clflush")
+ clflush :: proc(p: rawptr) ---
+ @(link_name="llvm.x86.sse2.lfence")
+ lfence :: proc() ---
+ @(link_name="llvm.x86.sse2.mfence")
+ mfence :: proc() ---
+ @(link_name="llvm.x86.sse2.pavg.b")
+ pavgb :: proc(a, b: u8x16) -> u8x16 ---
+ @(link_name="llvm.x86.sse2.pavg.w")
+ pavgw :: proc(a, b: u16x8) -> u16x8 ---
+ @(link_name="llvm.x86.sse2.pmadd.wd")
+ pmaddwd :: proc(a, b: i16x8) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.pmaxs.w")
+ pmaxsw :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.pmaxu.b")
+ pmaxub :: proc(a, b: u8x16) -> u8x16 ---
+ @(link_name="llvm.x86.sse2.pmins.w")
+ pminsw :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.pminu.b")
+ pminub :: proc(a, b: u8x16) -> u8x16 ---
+ @(link_name="llvm.x86.sse2.pmulh.w")
+ pmulhw :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.pmulhu.w")
+ pmulhuw :: proc(a, b: u16x8) -> u16x8 ---
+ @(link_name="llvm.x86.sse2.pmulu.dq")
+ pmuludq :: proc(a, b: u32x4) -> u64x2 ---
+ @(link_name="llvm.x86.sse2.psad.bw")
+ psadbw :: proc(a, b: u8x16) -> u64x2 ---
+ @(link_name="llvm.x86.sse2.pslli.w")
+ pslliw :: proc(a: i16x8, #const imm8: u32) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.psll.w")
+ psllw :: proc(a: i16x8, count: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.pslli.d")
+ psllid :: proc(a: i32x4, #const imm8: u32) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.psll.d")
+ pslld :: proc(a: i32x4, count: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.pslli.q")
+ pslliq :: proc(a: i64x2, #const imm8: u32) -> i64x2 ---
+ @(link_name="llvm.x86.sse2.psll.q")
+ psllq :: proc(a: i64x2, count: i64x2) -> i64x2 ---
+ @(link_name="llvm.x86.sse2.psrai.w")
+ psraiw :: proc(a: i16x8, #const imm8: u32) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.psra.w")
+ psraw :: proc(a: i16x8, count: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.psrai.d")
+ psraid :: proc(a: i32x4, #const imm8: u32) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.psra.d")
+ psrad :: proc(a: i32x4, count: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.psrli.w")
+ psrliw :: proc(a: i16x8, #const imm8: u32) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.psrl.w")
+ psrlw :: proc(a: i16x8, count: i16x8) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.psrli.d")
+ psrlid :: proc(a: i32x4, #const imm8: u32) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.psrl.d")
+ psrld :: proc(a: i32x4, count: i32x4) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.psrli.q")
+ psrliq :: proc(a: i64x2, #const imm8: u32) -> i64x2 ---
+ @(link_name="llvm.x86.sse2.psrl.q")
+ psrlq :: proc(a: i64x2, count: i64x2) -> i64x2 ---
+ @(link_name="llvm.x86.sse2.cvtdq2ps")
+ cvtdq2ps :: proc(a: i32x4) -> __m128 ---
+ @(link_name="llvm.x86.sse2.cvtps2dq")
+ cvtps2dq :: proc(a: __m128) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.maskmov.dqu")
+ maskmovdqu :: proc(a: i8x16, mask: i8x16, mem_addr: rawptr) ---
+ @(link_name="llvm.x86.sse2.packsswb.128")
+ packsswb :: proc(a, b: i16x8) -> i8x16 ---
+ @(link_name="llvm.x86.sse2.packssdw.128")
+ packssdw :: proc(a, b: i32x4) -> i16x8 ---
+ @(link_name="llvm.x86.sse2.packuswb.128")
+ packuswb :: proc(a, b: i16x8) -> u8x16 ---
+ @(link_name="llvm.x86.sse2.pmovmskb.128")
+ pmovmskb :: proc(a: i8x16) -> i32 ---
+ @(link_name="llvm.x86.sse2.max.sd")
+ maxsd :: proc(a, b: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.max.pd")
+ maxpd :: proc(a, b: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.min.sd")
+ minsd :: proc(a, b: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.min.pd")
+ minpd :: proc(a, b: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.sqrt.sd")
+ sqrtsd :: proc(a: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.sqrt.pd")
+ sqrtpd :: proc(a: __m128d) -> __m128d ---
+ @(link_name="llvm.x86.sse2.cmp.sd")
+ cmpsd :: proc(a, b: __m128d, imm8: i8) -> __m128d ---
+ @(link_name="llvm.x86.sse2.cmp.pd")
+ cmppd :: proc(a, b: __m128d, imm8: i8) -> __m128d ---
+ @(link_name="llvm.x86.sse2.comieq.sd")
+ comieqsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.comilt.sd")
+ comiltsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.comile.sd")
+ comilesd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.comigt.sd")
+ comigtsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.comige.sd")
+ comigesd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.comineq.sd")
+ comineqsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomieq.sd")
+ ucomieqsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomilt.sd")
+ ucomiltsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomile.sd")
+ ucomilesd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomigt.sd")
+ ucomigtsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomige.sd")
+ ucomigesd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.ucomineq.sd")
+ ucomineqsd :: proc(a, b: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.movmsk.pd")
+ movmskpd :: proc(a: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.cvtpd2ps")
+ cvtpd2ps :: proc(a: __m128d) -> __m128 ---
+ @(link_name="llvm.x86.sse2.cvtps2pd")
+ cvtps2pd :: proc(a: __m128) -> __m128d ---
+ @(link_name="llvm.x86.sse2.cvtpd2dq")
+ cvtpd2dq :: proc(a: __m128d) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.cvtsd2si")
+ cvtsd2si :: proc(a: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.cvtsd2ss")
+ cvtsd2ss :: proc(a, b: __m128d) -> __m128 ---
+ @(link_name="llvm.x86.sse2.cvtss2sd")
+ cvtss2sd :: proc(a, b: __m128) -> __m128d ---
+ @(link_name="llvm.x86.sse2.cvttpd2dq")
+ cvttpd2dq :: proc(a: __m128d) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.cvttsd2si")
+ cvttsd2si :: proc(a: __m128d) -> i32 ---
+ @(link_name="llvm.x86.sse2.cvttps2dq")
+ cvttps2dq :: proc(a: __m128) -> i32x4 ---
+ @(link_name="llvm.x86.sse2.storeu.dq")
+ storeudq :: proc(mem_addr: rawptr, a: __m128i) ---
+ @(link_name="llvm.x86.sse2.storeu.pd")
+ storeupd :: proc(mem_addr: rawptr, a: __m128d) ---
+
+ // amd64 only
+ @(link_name="llvm.x86.sse2.cvtsd2si64")
+ cvtsd2si64 :: proc(a: __m128d) -> i64 ---
+ @(link_name="llvm.x86.sse2.cvttsd2si64")
+ cvttsd2si64 :: proc(a: __m128d) -> i64 ---
+}
diff --git a/core/simd/x86/sse3.odin b/core/simd/x86/sse3.odin
new file mode 100644
index 000000000..7a3073c18
--- /dev/null
+++ b/core/simd/x86/sse3.odin
@@ -0,0 +1,68 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+import "core:simd"
+
+@(require_results, enable_target_feature="sse3")
+_mm_addsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return addsubps(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_addsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
+ return addsubpd(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_hadd_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
+ return haddpd(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_hadd_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return haddps(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_hsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
+ return hsubpd(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_hsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return hsubps(a, b)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
+ return transmute(__m128i)lddqu(mem_addr)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
+ return simd.shuffle(a, a, 0, 0)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
+ return _mm_load1_pd(mem_addr)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_movehdup_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return simd.shuffle(a, a, 1, 1, 3, 3)
+}
+@(require_results, enable_target_feature="sse3")
+_mm_moveldup_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return simd.shuffle(a, a, 0, 0, 2, 2)
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name = "llvm.x86.sse3.addsub.ps")
+ addsubps :: proc(a, b: __m128) -> __m128 ---
+ @(link_name = "llvm.x86.sse3.addsub.pd")
+ addsubpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
+ @(link_name = "llvm.x86.sse3.hadd.pd")
+ haddpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
+ @(link_name = "llvm.x86.sse3.hadd.ps")
+ haddps :: proc(a, b: __m128) -> __m128 ---
+ @(link_name = "llvm.x86.sse3.hsub.pd")
+ hsubpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
+ @(link_name = "llvm.x86.sse3.hsub.ps")
+ hsubps :: proc(a, b: __m128) -> __m128 ---
+ @(link_name = "llvm.x86.sse3.ldu.dq")
+ lddqu :: proc(mem_addr: rawptr) -> i8x16 ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/sse41.odin b/core/simd/x86/sse41.odin
new file mode 100644
index 000000000..b35be33f2
--- /dev/null
+++ b/core/simd/x86/sse41.odin
@@ -0,0 +1,352 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:simd"
+
+// SSE4 rounding constants
+_MM_FROUND_TO_NEAREST_INT :: 0x00
+_MM_FROUND_TO_NEG_INF :: 0x01
+_MM_FROUND_TO_POS_INF :: 0x02
+_MM_FROUND_TO_ZERO :: 0x03
+_MM_FROUND_CUR_DIRECTION :: 0x04
+_MM_FROUND_RAISE_EXC :: 0x00
+_MM_FROUND_NO_EXC :: 0x08
+_MM_FROUND_NINT :: 0x00
+_MM_FROUND_FLOOR :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_NEG_INF
+_MM_FROUND_CEIL :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_POS_INF
+_MM_FROUND_TRUNC :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_ZERO
+_MM_FROUND_RINT :: _MM_FROUND_RAISE_EXC | _MM_FROUND_CUR_DIRECTION
+_MM_FROUND_NEARBYINT :: _MM_FROUND_NO_EXC | _MM_FROUND_CUR_DIRECTION
+
+
+
+@(require_results, enable_target_feature="sse4.1")
+_mm_blendv_epi8 :: #force_inline proc "c" (a, b, mask: __m128i) -> __m128i {
+ return transmute(__m128i)pblendvb(transmute(i8x16)a, transmute(i8x16)b, transmute(i8x16)mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_blend_epi16 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
+ return transmute(__m128i)pblendw(transmute(i16x8)a, transmute(i16x8)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_blendv_pd :: #force_inline proc "c" (a, b, mask: __m128d) -> __m128d {
+ return blendvpd(a, b, mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_blendv_ps :: #force_inline proc "c" (a, b, mask: __m128) -> __m128 {
+ return blendvps(a, b, mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_blend_pd :: #force_inline proc "c" (a, b: __m128d, $IMM2: u8) -> __m128d {
+ return blendpd(a, b, IMM2)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_blend_ps :: #force_inline proc "c" (a, b: __m128, $IMM4: u8) -> __m128 {
+ return blendps(a, b, IMM4)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_extract_ps :: #force_inline proc "c" (a: __m128, $IMM8: u32) -> i32 {
+ return transmute(i32)simd.extract(a, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_extract_epi8 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 {
+ return i32(simd.extract(transmute(u8x16)a, IMM8))
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_extract_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 {
+ return simd.extract(transmute(i32x4)a, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_insert_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 {
+ return insertps(a, b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_insert_epi8 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)simd.replace(transmute(i8x16)a, IMM8, i8(i))
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_insert_epi32 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i {
+ return transmute(__m128i)simd.replace(transmute(i32x4)a, IMM8, i)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_max_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxsb(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_max_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxuw(transmute(u16x8)a, transmute(u16x8)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_max_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxsd(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_max_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaxud(transmute(u32x4)a, transmute(u32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_min_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminsb(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_min_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminuw(transmute(u16x8)a, transmute(u16x8)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_min_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminsd(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_min_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pminud(transmute(u32x4)a, transmute(u32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_packus_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)packusdw(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cmpeq_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_eq(transmute(i64x2)a, transmute(i64x2)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i8x16)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7)
+ return transmute(__m128i)i16x8(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i8x16)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3)
+ return transmute(__m128i)i32x4(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i8x16)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i16x8)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3)
+ return transmute(__m128i)i32x4(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i16x8)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepi32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(i32x4)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u8x16)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7)
+ return transmute(__m128i)i16x8(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u8x16)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3)
+ return transmute(__m128i)i32x4(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u8x16)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u16x8)a
+ y := simd.shuffle(x, x, 0, 1, 2, 3)
+ return transmute(__m128i)i32x4(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u16x8)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_cvtepu32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ x := transmute(u32x4)a
+ y := simd.shuffle(x, x, 0, 1)
+ return transmute(__m128i)i64x2(y)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_dp_pd :: #force_inline proc "c" (a, b: __m128d, $IMM8: u8) -> __m128d {
+ return dppd(a, b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_dp_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 {
+ return dpps(a, b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_floor_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
+ return simd.floor(a)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_floor_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return simd.floor(a)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_floor_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return roundsd(a, b, _MM_FROUND_FLOOR)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_floor_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return roundss(a, b, _MM_FROUND_FLOOR)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_ceil_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
+ return simd.ceil(a)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_ceil_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
+ return simd.ceil(a)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_ceil_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
+ return roundsd(a, b, _MM_FROUND_CEIL)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_ceil_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
+ return roundss(a, b, _MM_FROUND_CEIL)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_round_pd :: #force_inline proc "c" (a: __m128d, $ROUNDING: i32) -> __m128d {
+ return roundpd(a, ROUNDING)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_round_ps :: #force_inline proc "c" (a: __m128, $ROUNDING: i32) -> __m128 {
+ return roundps(a, ROUNDING)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_round_sd :: #force_inline proc "c" (a, b: __m128d, $ROUNDING: i32) -> __m128d {
+ return roundsd(a, b, ROUNDING)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_round_ss :: #force_inline proc "c" (a, b: __m128, $ROUNDING: i32) -> __m128 {
+ return roundss(a, b, ROUNDING)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_minpos_epu16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ return transmute(__m128i)phminposuw(transmute(u16x8)a)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_mul_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmuldq(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_mullo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.mul(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_mpsadbw_epu8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
+ return transmute(__m128i)mpsadbw(transmute(u8x16)a, transmute(u8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_testz_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
+ return ptestz(transmute(i64x2)a, transmute(i64x2)mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_testc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
+ return ptestc(transmute(i64x2)a, transmute(i64x2)mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
+ return ptestnzc(transmute(i64x2)a, transmute(i64x2)mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
+ return _mm_testz_si128(a, mask)
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
+ return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
+}
+@(require_results, enable_target_feature="sse4.1")
+_mm_test_mix_ones_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
+ return _mm_testnzc_si128(a, mask)
+}
+
+
+when ODIN_ARCH == .amd64 {
+ @(require_results, enable_target_feature="sse4.1")
+ _mm_extract_epi64 :: #force_inline proc "c" (a: __m128i, $IMM1: u32) -> i64 {
+ return simd.extract(transmute(i64x2)a, IMM1)
+ }
+
+ @(require_results, enable_target_feature="sse4.1")
+ _mm_insert_epi64 :: #force_inline proc "c" (a: __m128i, i: i64, $IMM1: u32) -> __m128i {
+ return transmute(__m128i)simd.replace(transmute(i64x2)a, IMM1, i)
+ }
+}
+
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name = "llvm.x86.sse41.pblendvb")
+ pblendvb :: proc(a, b: i8x16, mask: i8x16) -> i8x16 ---
+ @(link_name = "llvm.x86.sse41.blendvpd")
+ blendvpd :: proc(a, b, mask: __m128d) -> __m128d ---
+ @(link_name = "llvm.x86.sse41.blendvps")
+ blendvps :: proc(a, b, mask: __m128) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.blendpd")
+ blendpd :: proc(a, b: __m128d, #const imm2: u8) -> __m128d ---
+ @(link_name = "llvm.x86.sse41.blendps")
+ blendps :: proc(a, b: __m128, #const imm4: u8) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.pblendw")
+ pblendw :: proc(a: i16x8, b: i16x8, #const imm8: u8) -> i16x8 ---
+ @(link_name = "llvm.x86.sse41.insertps")
+ insertps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.pmaxsb")
+ pmaxsb :: proc(a, b: i8x16) -> i8x16 ---
+ @(link_name = "llvm.x86.sse41.pmaxuw")
+ pmaxuw :: proc(a, b: u16x8) -> u16x8 ---
+ @(link_name = "llvm.x86.sse41.pmaxsd")
+ pmaxsd :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name = "llvm.x86.sse41.pmaxud")
+ pmaxud :: proc(a, b: u32x4) -> u32x4 ---
+ @(link_name = "llvm.x86.sse41.pminsb")
+ pminsb :: proc(a, b: i8x16) -> i8x16 ---
+ @(link_name = "llvm.x86.sse41.pminuw")
+ pminuw :: proc(a, b: u16x8) -> u16x8 ---
+ @(link_name = "llvm.x86.sse41.pminsd")
+ pminsd :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name = "llvm.x86.sse41.pminud")
+ pminud :: proc(a, b: u32x4) -> u32x4 ---
+ @(link_name = "llvm.x86.sse41.packusdw")
+ packusdw :: proc(a, b: i32x4) -> u16x8 ---
+ @(link_name = "llvm.x86.sse41.dppd")
+ dppd :: proc(a, b: __m128d, #const imm8: u8) -> __m128d ---
+ @(link_name = "llvm.x86.sse41.dpps")
+ dpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.round.pd")
+ roundpd :: proc(a: __m128d, rounding: i32) -> __m128d ---
+ @(link_name = "llvm.x86.sse41.round.ps")
+ roundps :: proc(a: __m128, rounding: i32) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.round.sd")
+ roundsd :: proc(a, b: __m128d, rounding: i32) -> __m128d ---
+ @(link_name = "llvm.x86.sse41.round.ss")
+ roundss :: proc(a, b: __m128, rounding: i32) -> __m128 ---
+ @(link_name = "llvm.x86.sse41.phminposuw")
+ phminposuw :: proc(a: u16x8) -> u16x8 ---
+ @(link_name = "llvm.x86.sse41.pmuldq")
+ pmuldq :: proc(a, b: i32x4) -> i64x2 ---
+ @(link_name = "llvm.x86.sse41.mpsadbw")
+ mpsadbw :: proc(a, b: u8x16, #const imm8: u8) -> u16x8 ---
+ @(link_name = "llvm.x86.sse41.ptestz")
+ ptestz :: proc(a, mask: i64x2) -> i32 ---
+ @(link_name = "llvm.x86.sse41.ptestc")
+ ptestc :: proc(a, mask: i64x2) -> i32 ---
+ @(link_name = "llvm.x86.sse41.ptestnzc")
+ ptestnzc :: proc(a, mask: i64x2) -> i32 ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/sse42.odin b/core/simd/x86/sse42.odin
new file mode 100644
index 000000000..62b4f0478
--- /dev/null
+++ b/core/simd/x86/sse42.odin
@@ -0,0 +1,149 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:simd"
+
+_SIDD_UBYTE_OPS :: 0b0000_0000
+_SIDD_UWORD_OPS :: 0b0000_0001
+_SIDD_SBYTE_OPS :: 0b0000_0010
+_SIDD_SWORD_OPS :: 0b0000_0011
+
+_SIDD_CMP_EQUAL_ANY :: 0b0000_0000
+_SIDD_CMP_RANGES :: 0b0000_0100
+_SIDD_CMP_EQUAL_EACH :: 0b0000_1000
+_SIDD_CMP_EQUAL_ORDERED :: 0b0000_1100
+
+_SIDD_POSITIVE_POLARITY :: 0b0000_0000
+_SIDD_NEGATIVE_POLARITY :: 0b0001_0000
+_SIDD_MASKED_POSITIVE_POLARITY :: 0b0010_0000
+_SIDD_MASKED_NEGATIVE_POLARITY :: 0b0011_0000
+
+_SIDD_LEAST_SIGNIFICANT :: 0b0000_0000
+_SIDD_MOST_SIGNIFICANT :: 0b0100_0000
+
+_SIDD_BIT_MASK :: 0b0000_0000
+_SIDD_UNIT_MASK :: 0b0100_0000
+
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistrm :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> __m128i {
+ return transmute(__m128i)pcmpistrm128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistri :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistri128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistrz :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistriz128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistrc :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistric128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistrs :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistris128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistro :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistrio128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpistra :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
+ return pcmpistria128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestrm :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> __m128i {
+ return transmute(__m128i)pcmpestrm128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestri :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestri128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestrz :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestriz128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestrc :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestric128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestrs :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestris128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestro :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestrio128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpestra :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
+ return pcmpestria128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_crc32_u8 :: #force_inline proc "c" (crc: u32, v: u8) -> u32 {
+ return crc32_32_8(crc, v)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_crc32_u16 :: #force_inline proc "c" (crc: u32, v: u16) -> u32 {
+ return crc32_32_16(crc, v)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_crc32_u32 :: #force_inline proc "c" (crc: u32, v: u32) -> u32 {
+ return crc32_32_32(crc, v)
+}
+@(require_results, enable_target_feature="sse4.2")
+_mm_cmpgt_epi64 :: #force_inline proc "c" (a: __m128i, b: __m128i) -> __m128i {
+ return transmute(__m128i)simd.lanes_gt(transmute(i64x2)a, transmute(i64x2)b)
+}
+
+when ODIN_ARCH == .amd64 {
+ @(require_results, enable_target_feature="sse4.2")
+ _mm_crc32_u64 :: #force_inline proc "c" (crc: u64, v: u64) -> u64 {
+ return crc32_64_64(crc, v)
+ }
+}
+
+@(private, default_calling_convention="c")
+foreign _ {
+ // SSE 4.2 string and text comparison ops
+ @(link_name="llvm.x86.sse42.pcmpestrm128")
+ pcmpestrm128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> u8x16 ---
+ @(link_name="llvm.x86.sse42.pcmpestri128")
+ pcmpestri128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpestriz128")
+ pcmpestriz128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpestric128")
+ pcmpestric128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpestris128")
+ pcmpestris128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpestrio128")
+ pcmpestrio128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpestria128")
+ pcmpestria128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistrm128")
+ pcmpistrm128 :: proc(a, b: i8x16, #const imm8: i8) -> i8x16 ---
+ @(link_name="llvm.x86.sse42.pcmpistri128")
+ pcmpistri128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistriz128")
+ pcmpistriz128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistric128")
+ pcmpistric128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistris128")
+ pcmpistris128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistrio128")
+ pcmpistrio128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ @(link_name="llvm.x86.sse42.pcmpistria128")
+ pcmpistria128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
+ // SSE 4.2 CRC instructions
+ @(link_name="llvm.x86.sse42.crc32.32.8")
+ crc32_32_8 :: proc(crc: u32, v: u8) -> u32 ---
+ @(link_name="llvm.x86.sse42.crc32.32.16")
+ crc32_32_16 :: proc(crc: u32, v: u16) -> u32 ---
+ @(link_name="llvm.x86.sse42.crc32.32.32")
+ crc32_32_32 :: proc(crc: u32, v: u32) -> u32 ---
+
+ // AMD64 Only
+ @(link_name="llvm.x86.sse42.crc32.64.64")
+ crc32_64_64 :: proc(crc: u64, v: u64) -> u64 ---
+}
diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin
new file mode 100644
index 000000000..f11ef6774
--- /dev/null
+++ b/core/simd/x86/ssse3.odin
@@ -0,0 +1,140 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:intrinsics"
+import "core:simd"
+_ :: simd
+
+@(require_results, enable_target_feature="ssse3")
+_mm_abs_epi8 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ return transmute(__m128i)pabsb128(transmute(i8x16)a)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_abs_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ return transmute(__m128i)pabsw128(transmute(i16x8)a)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_abs_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
+ return transmute(__m128i)pabsd128(transmute(i32x4)a)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_shuffle_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pshufb128(transmute(u8x16)a, transmute(u8x16)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i {
+ shift :: IMM8
+
+ // If palignr is shifting the pair of vectors more than the size of two
+ // lanes, emit zero.
+ if shift > 32 {
+ return _mm_set1_epi8(0)
+ }
+ a, b := a, b
+ if shift > 16 {
+ a, b = _mm_set1_epi8(0), a
+ }
+
+ return transmute(__m128i)simd.shuffle(
+ transmute(i8x16)b,
+ transmute(i8x16)a,
+ 0 when shift > 32 else shift - 16 + 0 when shift > 16 else shift + 0,
+ 1 when shift > 32 else shift - 16 + 1 when shift > 16 else shift + 1,
+ 2 when shift > 32 else shift - 16 + 2 when shift > 16 else shift + 2,
+ 3 when shift > 32 else shift - 16 + 3 when shift > 16 else shift + 3,
+ 4 when shift > 32 else shift - 16 + 4 when shift > 16 else shift + 4,
+ 5 when shift > 32 else shift - 16 + 5 when shift > 16 else shift + 5,
+ 6 when shift > 32 else shift - 16 + 6 when shift > 16 else shift + 6,
+ 7 when shift > 32 else shift - 16 + 7 when shift > 16 else shift + 7,
+ 8 when shift > 32 else shift - 16 + 8 when shift > 16 else shift + 8,
+ 9 when shift > 32 else shift - 16 + 9 when shift > 16 else shift + 9,
+ 10 when shift > 32 else shift - 16 + 10 when shift > 16 else shift + 10,
+ 11 when shift > 32 else shift - 16 + 11 when shift > 16 else shift + 11,
+ 12 when shift > 32 else shift - 16 + 12 when shift > 16 else shift + 12,
+ 13 when shift > 32 else shift - 16 + 13 when shift > 16 else shift + 13,
+ 14 when shift > 32 else shift - 16 + 14 when shift > 16 else shift + 14,
+ 15 when shift > 32 else shift - 16 + 15 when shift > 16 else shift + 15,
+ )
+}
+
+
+@(require_results, enable_target_feature="ssse3")
+_mm_hadd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phaddw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_hadds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phaddsw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_hadd_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phaddd128(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_hsub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phsubw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_hsubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phsubsw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_hsub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)phsubd128(transmute(i32x4)a, transmute(i32x4)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_maddubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmaddubsw128(transmute(u8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_mulhrs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)pmulhrsw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_sign_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)psignb128(transmute(i8x16)a, transmute(i8x16)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_sign_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)psignw128(transmute(i16x8)a, transmute(i16x8)b)
+}
+@(require_results, enable_target_feature="ssse3")
+_mm_sign_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
+ return transmute(__m128i)psignd128(transmute(i32x4)a, transmute(i32x4)b)
+}
+
+
+
+@(private, default_calling_convention="c")
+foreign _ {
+ @(link_name = "llvm.x86.ssse3.pabs.b.128")
+ pabsb128 :: proc(a: i8x16) -> u8x16 ---
+ @(link_name = "llvm.x86.ssse3.pabs.w.128")
+ pabsw128 :: proc(a: i16x8) -> u16x8 ---
+ @(link_name = "llvm.x86.ssse3.pabs.d.128")
+ pabsd128 :: proc(a: i32x4) -> u32x4 ---
+ @(link_name = "llvm.x86.ssse3.pshuf.b.128")
+ pshufb128 :: proc(a, b: u8x16) -> u8x16 ---
+ @(link_name = "llvm.x86.ssse3.phadd.w.128")
+ phaddw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.phadd.sw.128")
+ phaddsw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.phadd.d.128")
+ phaddd128 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name = "llvm.x86.ssse3.phsub.w.128")
+ phsubw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.phsub.sw.128")
+ phsubsw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.phsub.d.128")
+ phsubd128 :: proc(a, b: i32x4) -> i32x4 ---
+ @(link_name = "llvm.x86.ssse3.pmadd.ub.sw.128")
+ pmaddubsw128 :: proc(a: u8x16, b: i8x16) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.pmul.hr.sw.128")
+ pmulhrsw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.psign.b.128")
+ psignb128 :: proc(a, b: i8x16) -> i8x16 ---
+ @(link_name = "llvm.x86.ssse3.psign.w.128")
+ psignw128 :: proc(a, b: i16x8) -> i16x8 ---
+ @(link_name = "llvm.x86.ssse3.psign.d.128")
+ psignd128 :: proc(a, b: i32x4) -> i32x4 ---
+}
\ No newline at end of file
diff --git a/core/simd/x86/types.odin b/core/simd/x86/types.odin
new file mode 100644
index 000000000..06a2cd41e
--- /dev/null
+++ b/core/simd/x86/types.odin
@@ -0,0 +1,57 @@
+//+build i386, amd64
+package simd_x86
+
+import "core:simd"
+
+bf16 :: u16
+
+__m128i :: #simd[2]i64
+__m128 :: #simd[4]f32
+__m128d :: #simd[2]f64
+
+__m256i :: #simd[4]i64
+__m256 :: #simd[8]f32
+__m256d :: #simd[4]f64
+
+__m512i :: #simd[8]i64
+__m512 :: #simd[16]f32
+__m512d :: #simd[8]f64
+
+__m128bh :: #simd[8]bf16
+__m256bh :: #simd[16]bf16
+__m512bh :: #simd[32]bf16
+
+
+/// The `__mmask64` type used in AVX-512 intrinsics, a 64-bit integer
+__mmask64 :: u64
+
+/// The `__mmask32` type used in AVX-512 intrinsics, a 32-bit integer
+__mmask32 :: u32
+
+/// The `__mmask16` type used in AVX-512 intrinsics, a 16-bit integer
+__mmask16 :: u16
+
+/// The `__mmask8` type used in AVX-512 intrinsics, a 8-bit integer
+__mmask8 :: u8
+
+/// The `_MM_CMPINT_ENUM` type used to specify comparison operations in AVX-512 intrinsics.
+_MM_CMPINT_ENUM :: i32
+
+/// The `MM_MANTISSA_NORM_ENUM` type used to specify mantissa normalized operations in AVX-512 intrinsics.
+_MM_MANTISSA_NORM_ENUM :: i32
+
+/// The `MM_MANTISSA_SIGN_ENUM` type used to specify mantissa signed operations in AVX-512 intrinsics.
+_MM_MANTISSA_SIGN_ENUM :: i32
+
+_MM_PERM_ENUM :: i32
+
+@(private) u8x16 :: simd.u8x16
+@(private) i8x16 :: simd.i8x16
+@(private) u16x8 :: simd.u16x8
+@(private) i16x8 :: simd.i16x8
+@(private) u32x4 :: simd.u32x4
+@(private) i32x4 :: simd.i32x4
+@(private) u64x2 :: simd.u64x2
+@(private) i64x2 :: simd.i64x2
+@(private) f32x4 :: simd.f32x4
+@(private) f64x2 :: simd.f64x2
diff --git a/core/slice/heap/heap.odin b/core/slice/heap/heap.odin
new file mode 100644
index 000000000..0a3f53efb
--- /dev/null
+++ b/core/slice/heap/heap.odin
@@ -0,0 +1,231 @@
+/*
+ Copyright 2022 Dale Weiler .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Dale Weiler: Initial implementation
+*/
+
+// Package implements a generic max heap in-place on a slice for any type.
+package heap
+
+/*
+ Constructs a max heap in slice given by data with comparator. A max heap is
+ a range of elements which has the following properties:
+
+ 1. With N = len(data), for all 0 < i < N, data[(i - 1) / 2] does not compare
+ less than data[i].
+
+ 2. A new element can be added using push in O(log n) time.
+
+ 3. The first element can be removed using pop in O(log n) time.
+
+ The comparator compares elements of type T and can be used to construct a
+ max heap (less than) or min heap (greater than) for T.
+*/
+make :: proc(data: []$T, less: proc(a, b: T) -> bool) {
+ // amoritize length lookup
+ length := len(data)
+ if length <= 1 do return
+
+ // start from data parent, no need to consider children
+ for start := (length - 2) / 2; start >= 0; start -= 1 {
+ sift_down(data, less, start)
+ }
+}
+
+/*
+ Inserts the element at the position len(data)-1 into the max heap with
+ comparator.
+
+ At most log(N) comparisons where N = len(data) will be performed.
+*/
+push :: proc(data: []$T, less: proc(a, b: T) -> bool) {
+ sift_up(data, less)
+}
+
+/*
+ Swaps the value in position data[0] and the value in data[len(data)-1] and
+ makes subrange [0, len(data)-1) into a heap. This has the effect of removing
+ the first element from the heap.
+
+ At most 2 * log(N) comparisons where N = len(data) will be performed.
+*/
+pop :: proc(data: []$T, less: proc(a, b: T) -> bool) {
+ length := len(data)
+ if length <= 1 do return
+
+ last := length
+
+ // create a hole at 0
+ top := data[0]
+ hole := floyd_sift_down(data, less)
+ last -= 1
+
+ if hole == last {
+ data[hole] = top
+ } else {
+ data[hole] = data[last]
+ hole += 1
+ data[last] = top
+ sift_up(data[:hole], less)
+ }
+}
+
+/*
+ Converts the max heap into a sorted range in ascending order. The resulting
+ slice will no longer be a heap after this.
+
+ At most 2 * N * log(N) comparisons where N = len(data) will be performed.
+*/
+sort :: proc(data: []$T, less: proc(a, b: T) -> bool) {
+ for n := len(data); n >= 1; n -= 1 {
+ pop(data[:n], less)
+ }
+}
+
+/*
+ Examines the slice and finds the largest range which is a max-heap. Elements
+ are compared with user-supplied comparison procedure.
+
+ This returns the upper bound of the largest range in the slice which is a
+ max heap. That is, the last index for which data is a max heap.
+
+ At most O(n) comparisons where N = len(data) will be performed.
+*/
+is_heap_until :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
+ length := len(data)
+ a := 0
+ b := 1
+ for b < length {
+ if less(data[a], data[b]) {
+ return b
+ }
+ b += 1
+ if b == length || less(data[a], data[b]) {
+ return b
+ }
+ a += 1
+ b = 2 * a + 1
+ }
+ return length
+}
+
+/*
+ Checks if a given slice is a max heap.
+
+ At most O(n) comparisons where N = len(data) will be performed.
+*/
+is_heap :: #force_inline proc(data: []$T, less: proc(a, b: T) -> bool) -> bool {
+ return is_heap_until(data, less) == len(data)
+}
+
+@(private="file")
+floyd_sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
+ length := len(data)
+ assert(length >= 2)
+
+ hole := 0
+ child := 0
+ index := 0
+ for {
+ index += child + 1
+ child = 2 * child + 1
+ if child + 1 < length && less(data[index], data[index + 1]) {
+ child += 1
+ index += 1
+ }
+
+ data[hole] = data[index]
+ hole = index
+
+ if child > (length - 2) / 2 {
+ return hole
+ }
+ }
+
+ unreachable()
+}
+
+@(private="file")
+sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool, start: int) {
+ start := start
+ child := start
+
+ // amoritize length lookup
+ length := len(data)
+
+ // left child of start is at 2 * start + 1
+ // right child of start is at 2 * start + 2
+ if length < 2 || (length - 2) / 2 < child {
+ return
+ }
+
+ child = 2 * child + 1
+
+ if child + 1 < length && less(data[child], data[child + 1]) {
+ // right child exists and is greater than left child
+ child += 1
+ }
+
+ // check if in heap order
+ if less(data[child], data[start]) {
+ // start is larger than its largest child
+ return
+ }
+
+ top := data[start]
+ for {
+ // not in heap order, swap parent with its largest child
+ data[start] = data[child]
+ start = child
+
+ if (length - 2) / 2 < child {
+ break
+ }
+
+ // recompute child based off updated parent
+ child = 2 * child + 1
+
+ if child + 1 < length && less(data[child], data[child + 1]) {
+ // right child exists and is greater than left child
+ child += 1
+ }
+
+ // check if we are in heap order
+ if less(data[child], top) {
+ break
+ }
+ }
+
+ data[start] = top
+}
+
+@(private="file")
+sift_up :: proc(data: []$T, less: proc(a, b: T) -> bool) {
+ // amoritize length lookup
+ length := len(data)
+
+ if length <= 1 do return
+
+ last := length
+ length = (length - 2) / 2
+ index := length
+ last -= 1
+ if less(data[index], data[last]) {
+ top := data[last]
+ for {
+ data[last] = data[index]
+ last = index
+ if length == 0 {
+ break
+ }
+ length = (length - 1) / 2
+ index = length
+ if !less(data[index], top) {
+ break
+ }
+ }
+ data[last] = top
+ }
+}
\ No newline at end of file
diff --git a/core/slice/map.odin b/core/slice/map.odin
index 1c5512ceb..9de00b174 100644
--- a/core/slice/map.odin
+++ b/core/slice/map.odin
@@ -2,11 +2,9 @@ package slice
import "core:intrinsics"
import "core:runtime"
-import "core:mem"
_ :: intrinsics
_ :: runtime
-_ :: mem
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
keys = make(type_of(keys), len(m), allocator)
@@ -52,7 +50,7 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
m := m
- rm := (^mem.Raw_Map)(&m)
+ rm := (^runtime.Raw_Map)(&m)
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 1dd777ccc..440cf643f 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -10,6 +10,53 @@ _ :: builtin
_ :: bits
_ :: mem
+/*
+ Turn a pointer and a length into a slice.
+*/
+from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
+ return ([^]T)(ptr)[:count]
+}
+
+/*
+ Turn a pointer and a length into a byte slice.
+*/
+bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
+ return ([^]byte)(ptr)[:byte_count]
+}
+
+/*
+ Turn a slice into a byte slice.
+
+ See `slice.reinterpret` to go the other way.
+*/
+to_bytes :: proc "contextless" (s: []$T) -> []byte {
+ return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
+}
+
+/*
+ Turn a slice of one type, into a slice of another type.
+
+ Only converts the type and length of the slice itself.
+ The length is rounded down to the nearest whole number of items.
+
+ ```
+ large_items := []i64{1, 2, 3, 4}
+ small_items := slice.reinterpret([]i32, large_items)
+ assert(len(small_items) == 8)
+ ```
+ ```
+ small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0}
+ large_items := slice.reinterpret([]i64, small_items)
+ assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
+ ```
+*/
+reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
+ bytes := to_bytes(s)
+ n := len(bytes) / size_of(U)
+ return ([^]U)(raw_data(bytes))[:n]
+}
+
swap :: proc(array: $T/[]$E, a, b: int) {
when size_of(E) > 8 {
@@ -20,7 +67,7 @@ swap :: proc(array: $T/[]$E, a, b: int) {
}
swap_between :: proc(a, b: $T/[]$E) {
- n := min(len(a), len(b))
+ n := builtin.min(len(a), len(b))
if n >= 0 {
ptr_swap_overlapping(&a[0], &b[0], size_of(E)*n)
}
@@ -123,6 +170,21 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp
return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
}
+/*
+ return the prefix length common between slices `a` and `b`.
+
+ slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1}) -> 1
+ slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3}) -> 3
+ slice.prefix_length([]u8{1, 2, 3, 4}, []u8{2, 3, 4}) -> 0
+*/
+prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) {
+ _len := builtin.min(len(a), len(b))
+
+ #no_bounds_check for n < _len && a[n] == b[n] {
+ n += 1
+ }
+ return
+}
has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) {
n := len(needle)
@@ -185,7 +247,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
return
}
-// copies slice into a new dynamic array
+// copies a slice into a new slice
clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
d := make([]E, len(a), allocator)
copy(d[:], a)
@@ -194,11 +256,12 @@ clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
// copies slice into a new dynamic array
-to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
+clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
d := make([dynamic]E, len(a), allocator)
copy(d[:], a)
return d
}
+to_dynamic :: clone_to_dynamic
// Converts slice into a dynamic array without cloning or allocating memory
into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
@@ -272,7 +335,7 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
return
}
-as_ptr :: proc(array: $T/[]$E) -> ^E {
+as_ptr :: proc(array: $T/[]$E) -> [^]E {
return raw_data(array)
}
@@ -303,6 +366,23 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
return r[:]
}
+scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> []V {
+ if len(s) == 0 { return {} }
+
+ res := make([]V, len(s), allocator)
+ p := as_ptr(s)
+ q := as_ptr(res)
+ r := initializer
+
+ for l := len(s); l > 0; l -= 1 {
+ r = f(r, p[0])
+ q[0] = r
+ p = p[1:]
+ q = q[1:]
+ }
+
+ return res
+}
min :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
@@ -326,15 +406,106 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
return
}
+min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_ordered(T) {
+ if len(s) != 0 {
+ min, max = s[0], s[0]
+ ok = true
+ for v in s[1:] {
+ min = builtin.min(min, v)
+ max = builtin.max(max, v)
+ }
+ }
+ return
+}
-dot_product :: proc(a, b: $S/[]$T) -> T
+any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ return true
+ }
+ }
+ return false
+}
+
+none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ return false
+ }
+ }
+ return true
+}
+
+all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ if len(s) == 0 {
+ return false
+ }
+ for v in s {
+ if v != value {
+ return false
+ }
+ }
+ return true
+}
+
+
+any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ for v in s {
+ if f(v) {
+ return true
+ }
+ }
+ return false
+}
+
+none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ for v in s {
+ if f(v) {
+ return false
+ }
+ }
+ return true
+}
+
+all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ if len(s) == 0 {
+ return false
+ }
+ for v in s {
+ if !f(v) {
+ return false
+ }
+ }
+ return true
+}
+
+
+count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ n += 1
+ }
+ }
+ return
+}
+
+count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
+ for v in s {
+ if f(v) {
+ n += 1
+ }
+ }
+ return
+}
+
+
+dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
where intrinsics.type_is_numeric(T) {
if len(a) != len(b) {
- panic("slice.dot_product: slices of unequal length")
+ return
}
- r: T
#no_bounds_check for _, i in a {
r += a[i] * b[i]
}
- return r
+ return r, true
}
diff --git a/core/slice/sort.odin b/core/slice/sort.odin
index d9755ad0e..b6e455056 100644
--- a/core/slice/sort.odin
+++ b/core/slice/sort.odin
@@ -1,10 +1,5 @@
package slice
-import "core:intrinsics"
-_ :: intrinsics
-
-ORD :: intrinsics.type_is_ordered
-
Ordering :: enum {
Less = -1,
Equal = 0,
@@ -38,7 +33,7 @@ cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
sort :: proc(data: $T/[]$E) where ORD(E) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort(data, 0, n, _max_depth(n))
+ _quick_sort_general(data, 0, n, _max_depth(n), struct{}{}, .Ordered)
}
}
}
@@ -48,7 +43,7 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort_less(data, 0, n, _max_depth(n), less)
+ _quick_sort_general(data, 0, n, _max_depth(n), less, .Less)
}
}
}
@@ -56,7 +51,33 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort_cmp(data, 0, n, _max_depth(n), cmp)
+ _quick_sort_general(data, 0, n, _max_depth(n), cmp, .Cmp)
+ }
+ }
+}
+
+// stable_sort sorts a slice
+stable_sort :: proc(data: $T/[]$E) where ORD(E) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, struct{}{}, .Ordered)
+ }
+ }
+}
+
+// stable_sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
+stable_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, less, .Less)
+ }
+ }
+}
+
+stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, cmp, .Cmp)
}
}
}
@@ -79,9 +100,10 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
return true
}
+is_sorted_by_cmp :: is_sorted_cmp
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
- if cmp(array[i], array[i-1]) == .Equal {
+ if cmp(array[i], array[i-1]) == .Less {
return false
}
}
@@ -140,489 +162,10 @@ is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K
return true
}
-
-
@(private)
-_max_depth :: proc(n: int) -> int { // 2*ceil(log2(n+1))
- depth: int
+_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1))
for i := n; i > 0; i >>= 1 {
depth += 1
}
return depth * 2
}
-
-@(private)
-_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int) #no_bounds_check {
- if data[m1] < data[m0] {
- swap(data, m1, m0)
- }
- if data[m2] < data[m1] {
- swap(data, m2, m1)
- if data[m1] < data[m0] {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2)
- median3(data, m, m-s, m+s)
- median3(data, hi-1, hi-1-s, hi-1-s*2)
- }
- median3(data, lo, m, hi-1)
-
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && data[a] < data[pivot]; a += 1 {
- }
- b := a
-
- for {
- for ; b < c && !(data[pivot] < data[b]); b += 1 { // data[b] <= pivot
- }
- for ; b < c && data[pivot] < data[c-1]; c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if !(data[pivot] < data[hi-1]) {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if !(data[b-1] < data[pivot]) {
- b -= 1
- dups += 1
- }
-
- if !(data[m] < data[pivot]) {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && !(data[b-1] < data[pivot]); b -= 1 {
- }
- for ; a < b && data[a] < data[pivot]; a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort(data, a, b)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b)
- if mlo-a < b-mhi {
- _quick_sort(data, a, mlo, max_depth)
- a = mhi
- } else {
- _quick_sort(data, mhi, b, max_depth)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. a && data[j] < data[j-1]; j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && data[first+child] < data[first+child+1] {
- child += 1
- }
- if !(data[first+root] < data[first+child]) {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first)
- }
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- if less(data[m1], data[m0]) {
- swap(data, m1, m0)
- }
- if less(data[m2], data[m1]) {
- swap(data, m2, m1)
- if less(data[m1], data[m0]) {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2, less)
- median3(data, m, m-s, m+s, less)
- median3(data, hi-1, hi-1-s, hi-1-s*2, less)
- }
- median3(data, lo, m, hi-1, less)
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && less(data[a], data[pivot]); a += 1 {
- }
- b := a
-
- for {
- for ; b < c && !less(data[pivot], data[b]); b += 1 { // data[b] <= pivot
- }
- for ; b < c && less(data[pivot], data[c-1]); c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if !less(data[pivot], data[hi-1]) {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if !less(data[b-1], data[pivot]) {
- b -= 1
- dups += 1
- }
-
- if !less(data[m], data[pivot]) {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && !less(data[b-1], data[pivot]); b -= 1 {
- }
- for ; a < b && less(data[a], data[pivot]); a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort_less(data, a, b, less)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b, less)
- if mlo-a < b-mhi {
- _quick_sort_less(data, a, mlo, max_depth, less)
- a = mhi
- } else {
- _quick_sort_less(data, mhi, b, max_depth, less)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. bool) #no_bounds_check {
- for i in a+1.. a && less(data[j], data[j-1]); j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && less(data[first+child], data[first+child+1]) {
- child += 1
- }
- if !less(data[first+root], data[first+child]) {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first, less)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first, less)
- }
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- if cmp(data[m1], data[m0]) == .Less {
- swap(data, m1, m0)
- }
- if cmp(data[m2], data[m1]) == .Less {
- swap(data, m2, m1)
- if cmp(data[m1], data[m0]) == .Less {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2, cmp)
- median3(data, m, m-s, m+s, cmp)
- median3(data, hi-1, hi-1-s, hi-1-s*2, cmp)
- }
- median3(data, lo, m, hi-1, cmp)
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && cmp(data[a], data[pivot]) == .Less; a += 1 {
- }
- b := a
-
- for {
- for ; b < c && cmp(data[pivot], data[b]) >= .Equal; b += 1 { // data[b] <= pivot
- }
- for ; b < c && cmp(data[pivot], data[c-1]) == .Less; c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if cmp(data[pivot], data[hi-1]) != .Less {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if cmp(data[b-1], data[pivot]) != .Less {
- b -= 1
- dups += 1
- }
-
- if cmp(data[m], data[pivot]) != .Less {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && cmp(data[b-1], data[pivot]) >= .Equal; b -= 1 {
- }
- for ; a < b && cmp(data[a], data[pivot]) == .Less; a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort_cmp(data, a, b, cmp)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b, cmp)
- if mlo-a < b-mhi {
- _quick_sort_cmp(data, a, mlo, max_depth, cmp)
- a = mhi
- } else {
- _quick_sort_cmp(data, mhi, b, max_depth, cmp)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. Ordering) #no_bounds_check {
- for i in a+1.. a && cmp(data[j], data[j-1]) == .Less; j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && cmp(data[first+child], data[first+child+1]) == .Less {
- child += 1
- }
- if cmp(data[first+root], data[first+child]) >= .Equal {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first, cmp)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first, cmp)
- }
-}
-
-
-
diff --git a/core/slice/sort_private.odin b/core/slice/sort_private.odin
new file mode 100644
index 000000000..d93d74bf9
--- /dev/null
+++ b/core/slice/sort_private.odin
@@ -0,0 +1,200 @@
+//+private
+package slice
+
+import "core:intrinsics"
+_ :: intrinsics
+
+ORD :: intrinsics.type_is_ordered
+
+Sort_Kind :: enum {
+ Ordered,
+ Less,
+ Cmp,
+}
+
+_quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+ less :: #force_inline proc(a, b: E, call: P) -> bool {
+ when KIND == .Ordered {
+ return a < b
+ } else when KIND == .Less {
+ return call(a, b)
+ } else when KIND == .Cmp {
+ return call(a, b) == .Less
+ } else {
+ #panic("unhandled Sort_Kind")
+ }
+ }
+
+ insertion_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+ for i in a+1.. a && less(data[j], data[j-1], call); j -= 1 {
+ swap(data, j, j-1)
+ }
+ }
+ }
+
+ heap_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+ sift_down :: proc(data: T, lo, hi, first: int, call: P) #no_bounds_check {
+ root := lo
+ for {
+ child := 2*root + 1
+ if child >= hi {
+ break
+ }
+ if child+1 < hi && less(data[first+child], data[first+child+1], call) {
+ child += 1
+ }
+ if !less(data[first+root], data[first+child], call) {
+ return
+ }
+ swap(data, first+root, first+child)
+ root = child
+ }
+ }
+
+
+ first, lo, hi := a, 0, b-a
+
+ for i := (hi-1)/2; i >= 0; i -= 1 {
+ sift_down(data, i, hi, first, call)
+ }
+
+ for i := hi-1; i >= 0; i -= 1 {
+ swap(data, first, first+i)
+ sift_down(data, lo, i, first, call)
+ }
+ }
+
+ median3 :: proc(data: T, m1, m0, m2: int, call: P) #no_bounds_check {
+ if less(data[m1], data[m0], call) {
+ swap(data, m1, m0)
+ }
+ if less(data[m2], data[m1], call) {
+ swap(data, m2, m1)
+ if less(data[m1], data[m0], call) {
+ swap(data, m1, m0)
+ }
+ }
+ }
+
+ do_pivot :: proc(data: T, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
+ m := int(uint(lo+hi)>>1)
+ if hi-lo > 40 {
+ s := (hi-lo)/8
+ median3(data, lo, lo+s, lo+s*2, call)
+ median3(data, m, m-s, m+s, call)
+ median3(data, hi-1, hi-1-s, hi-1-s*2, call)
+ }
+ median3(data, lo, m, hi-1, call)
+
+ pivot := lo
+ a, c := lo+1, hi-1
+
+
+ for ; a < c && less(data[a], data[pivot], call); a += 1 {
+ }
+ b := a
+
+ for {
+ for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
+ }
+ for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
+ }
+ if b >= c {
+ break
+ }
+
+ swap(data, b, c-1)
+ b += 1
+ c -= 1
+ }
+
+ protect := hi-c < 5
+ if !protect && hi-c < (hi-lo)/4 {
+ dups := 0
+ if !less(data[pivot], data[hi-1], call) {
+ swap(data, c, hi-1)
+ c += 1
+ dups += 1
+ }
+ if !less(data[b-1], data[pivot], call) {
+ b -= 1
+ dups += 1
+ }
+
+ if !less(data[m], data[pivot], call) {
+ swap(data, m, b-1)
+ b -= 1
+ dups += 1
+ }
+ protect = dups > 1
+ }
+ if protect {
+ for {
+ for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
+ }
+ for ; a < b && less(data[a], data[pivot], call); a += 1 {
+ }
+ if a >= b {
+ break
+ }
+ swap(data, a, b-1)
+ a += 1
+ b -= 1
+ }
+ }
+ swap(data, pivot, b-1)
+ return b-1, c
+ }
+
+
+ a, b, max_depth := a, b, max_depth
+
+ for b-a > 12 { // only use shell sort for lengths <= 12
+ if max_depth == 0 {
+ heap_sort(data, a, b, call)
+ return
+ }
+ max_depth -= 1
+ mlo, mhi := do_pivot(data, a, b, call)
+ if mlo-a < b-mhi {
+ _quick_sort_general(data, a, mlo, max_depth, call, KIND)
+ a = mhi
+ } else {
+ _quick_sort_general(data, mhi, b, max_depth, call, KIND)
+ b = mlo
+ }
+ }
+ if b-a > 1 {
+ // Shell short with gap 6
+ for i in a+6.. bool {
+ when KIND == .Ordered {
+ return a < b
+ } else when KIND == .Less {
+ return call(a, b)
+ } else when KIND == .Cmp {
+ return call(a, b) == .Less
+ } else {
+ #panic("unhandled Sort_Kind")
+ }
+ }
+
+ n := len(data)
+ for i in 1.. 0 && less(data[j], data[j-1], call); j -= 1 {
+ swap(data, j, j-1)
+ }
+ }
+}
diff --git a/core/sort/map.odin b/core/sort/map.odin
new file mode 100644
index 000000000..32f5e09a2
--- /dev/null
+++ b/core/sort/map.odin
@@ -0,0 +1,36 @@
+package sort
+
+import "core:intrinsics"
+import "core:runtime"
+import "core:slice"
+
+_ :: runtime
+_ :: slice
+
+map_entries_by_key :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(K) {
+ Entry :: struct {
+ hash: uintptr,
+ next: int,
+ key: K,
+ value: V,
+ }
+
+ header := runtime.__get_map_header(m)
+ entries := (^[dynamic]Entry)(&header.m.entries)
+ slice.sort_by_key(entries[:], proc(e: Entry) -> K { return e.key })
+ runtime.__dynamic_map_reset_entries(header, loc)
+}
+
+map_entries_by_value :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(V) {
+ Entry :: struct {
+ hash: uintptr,
+ next: int,
+ key: K,
+ value: V,
+ }
+
+ header := runtime.__get_map_header(m)
+ entries := (^[dynamic]Entry)(&header.m.entries)
+ slice.sort_by_key(entries[:], proc(e: Entry) -> V { return e.value })
+ runtime.__dynamic_map_reset_entries(header, loc)
+}
\ No newline at end of file
diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin
index 6b3a91b4c..65161a820 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -882,7 +882,9 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
return -1
}
- assert(len(lit) >= 2)
+ if len(lit) < 2 {
+ return
+ }
if lit[0] == '`' {
return lit[1:len(lit)-1], false, true
}
@@ -893,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
if s == `""` {
return "", false, true
}
+ s = s[1:len(s)-1]
if contains_rune(s, '\n') >= 0 {
return s, false, false
diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin
index 582049eee..9b59666f3 100644
--- a/core/strings/ascii_set.odin
+++ b/core/strings/ascii_set.odin
@@ -5,6 +5,7 @@ import "core:unicode/utf8"
Ascii_Set :: distinct [8]u32
+// create an ascii set of all unique characters in the string
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
return
}
+// returns true when the `c` byte is contained in the `as` ascii set
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 6952ba088..6de42804d 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -1,50 +1,69 @@
package strings
-import "core:mem"
+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
+*/
Builder :: struct {
buf: [dynamic]byte,
}
-make_builder_none :: proc(allocator := context.allocator) -> Builder {
+// return a builder, default length 0 / cap 16 are done through make
+builder_make_none :: proc(allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, allocator)}
}
-make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder {
+// return a builder, with a set length `len` and cap 16 byte buffer
+builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, allocator)}
}
-make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
+// return a builder, with a set length `len` byte buffer and a custom `cap`
+builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
}
-make_builder :: proc{
- make_builder_none,
- make_builder_len,
- make_builder_len_cap,
+// overload simple `builder_make_*` with or without len / cap parameters
+builder_make :: proc{
+ builder_make_none,
+ builder_make_len,
+ builder_make_len_cap,
}
-init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
+// initialize a builder, default length 0 / cap 16 are done through make
+// replaces the existing `buf`
+builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, allocator)
+ return b
}
-init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
+// initialize a builder, with a set length `len` and cap 16 byte buffer
+// replaces the existing `buf`
+builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, allocator)
+ return b
}
-init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
+// initialize a builder, with a set length `len` byte buffer and a custom `cap`
+// replaces the existing `buf`
+builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
b.buf = make([dynamic]byte, len, cap, allocator)
+ return b
}
-init_builder :: proc{
- init_builder_none,
- init_builder_len,
- init_builder_len_cap,
+// overload simple `builder_init_*` with or without len / ap parameters
+builder_init :: proc{
+ builder_init_none,
+ builder_init_len,
+ builder_init_len_cap,
}
@(private)
@@ -76,56 +95,85 @@ _builder_stream_vtable := &io.Stream_VTable{
},
}
+// return an `io.Stream` from a builder
to_stream :: proc(b: ^Builder) -> io.Stream {
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
}
+
+// return an `io.Writer` from a builder
to_writer :: proc(b: ^Builder) -> io.Writer {
return io.to_writer(to_stream(b))
}
-
-
-
-destroy_builder :: proc(b: ^Builder) {
+// delete and clear the builder byte buffer content
+builder_destroy :: proc(b: ^Builder) {
delete(b.buf)
clear(&b.buf)
}
-grow_builder :: proc(b: ^Builder, cap: int) {
+// reserve the builfer byte buffer to a specific cap, when it's higher than before
+builder_grow :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap)
}
-reset_builder :: proc(b: ^Builder) {
+// clear the builder byte buffer content
+builder_reset :: proc(b: ^Builder) {
clear(&b.buf)
}
-
-builder_from_slice :: proc(backing: []byte) -> Builder {
- s := transmute(mem.Raw_Slice)backing
- d := mem.Raw_Dynamic_Array{
+/*
+ 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"
+*/
+builder_from_bytes :: proc(backing: []byte) -> Builder {
+ s := transmute(runtime.Raw_Slice)backing
+ d := runtime.Raw_Dynamic_Array{
data = s.data,
len = 0,
cap = s.len,
- allocator = mem.nil_allocator(),
+ allocator = runtime.nil_allocator(),
}
return Builder{
buf = transmute([dynamic]byte)d,
}
}
+builder_from_slice :: builder_from_bytes
+
+// cast the builder byte buffer to a string and return it
to_string :: proc(b: Builder) -> string {
return string(b.buf[:])
}
+// return the length of the builder byte buffer
builder_len :: proc(b: Builder) -> int {
return len(b.buf)
}
+
+// return the cap of the builder byte buffer
builder_cap :: proc(b: Builder) -> int {
return cap(b.buf)
}
+
+// returns the space left in the builder byte buffer to use up
builder_space :: proc(b: Builder) -> int {
- return max(cap(b.buf), len(b.buf), 0)
+ return cap(b.buf) - len(b.buf)
}
+/*
+ appends a byte to the builder, returns the append diff
+
+ 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)
append(&b.buf, x)
@@ -133,6 +181,14 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
return n1-n0
}
+/*
+ appends a slice of bytes to the builder, returns the append diff
+
+ builder := strings.builder_make()
+ bytes := [?]byte { 'a', 'b', 'c' }
+ strings.write_bytes(&builder, bytes[:]) // 3
+ fmt.println(strings.to_string(builder)) // -> abc
+*/
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n0 := len(b.buf)
append(&b.buf, ..x)
@@ -140,142 +196,129 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
return n1-n0
}
-write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
+/*
+ appends a single rune into the builder, returns written rune size and an `io.Error`
+
+ 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
-write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
- return write_quoted_rune(to_writer(b), r)
-}
-
-@(private)
-_write_byte :: proc(w: io.Writer, c: byte) -> int {
- err := io.write_byte(w, c)
- return 1 if err == nil else 0
+ 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)
}
-write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
- quote := byte('\'')
- n += _write_byte(w, quote)
- buf, width := utf8.encode_rune(r)
- if width == 1 && r == utf8.RUNE_ERROR {
- n += _write_byte(w, '\\')
- n += _write_byte(w, 'x')
- n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
- n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
- } else {
- i, _ := io.write_escaped_rune(w, r, quote)
- n += i
- }
- n += _write_byte(w, quote)
- return
+/*
+ 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
+*/
+write_string :: proc(b: ^Builder, s: string) -> (n: int) {
+ n0 := len(b.buf)
+ append(&b.buf, s)
+ n1 := len(b.buf)
+ return n1-n0
}
-write_string :: proc{
- write_string_builder,
- write_string_writer,
-}
-
-write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
- return write_string_writer(to_writer(b), s)
-}
-
-write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
- n, _ = io.write(w, transmute([]byte)s)
- return
-}
-
-
-
-
+// pops and returns the last byte in the builder
+// returns 0 when the builder is empty
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0
}
+
r = b.buf[len(b.buf)-1]
- d := cast(^mem.Raw_Dynamic_Array)&b.buf
+ d := cast(^runtime.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-1, 0)
return
}
+// pops the last rune in the builder and returns the popped rune and its rune width
+// returns 0, 0 when the builder is empty
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
+ if len(b.buf) == 0 {
+ return 0, 0
+ }
+
r, width = utf8.decode_last_rune(b.buf[:])
- d := cast(^mem.Raw_Dynamic_Array)&b.buf
+ d := cast(^runtime.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-width, 0)
return
}
-
@(private)
DIGITS_LOWER := "0123456789abcdefx"
-write_quoted_string :: proc{
- write_quoted_string_builder,
- write_quoted_string_writer,
-}
+/*
+ append a quoted string into the builder, return the written byte size
-write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
+ 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
}
-@(deprecated="prefer io.write_quoted_string")
-write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
- n, _ = io.write_quoted_string(w, str, quote)
- return
-}
-write_encoded_rune :: proc{
- write_encoded_rune_builder,
- write_encoded_rune_writer,
-}
-
-write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
+// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
+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
}
-@(deprecated="prefer io.write_encoded_rune")
-write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
- n, _ = io.write_encoded_rune(w, r, write_quote)
- return
-}
-
-write_escaped_rune :: proc{
- write_escaped_rune_builder,
- write_escaped_rune_writer,
-}
-
-write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
+// 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`
+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
}
-@(deprecated="prefer io.write_escaped_rune")
-write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
- n, _ = io.write_escaped_rune(w, r, quote, html_safe)
- return
-}
-
-
+// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
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 `i` in `base` = 10 into the builder, returns the written amount of characters
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 `i` in `base` = 10 into the builder, returns the written amount of characters
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
return write_u64(b, u64(i), base)
}
+
+// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
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 b0d42b2eb..ab827490d 100644
--- a/core/strings/conversion.odin
+++ b/core/strings/conversion.odin
@@ -10,7 +10,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
}
b: Builder
- init_builder(&b, 0, 0, allocator)
+ builder_init(&b, 0, 0, allocator)
s := s
for c, i in s {
@@ -20,7 +20,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
_, w := utf8.decode_rune_in_string(s[i:])
if w == 1 {
- grow_builder(&b, len(s) + len(replacement))
+ builder_grow(&b, len(s) + len(replacement))
write_string(&b, s[:i])
s = s[i:]
break
@@ -58,30 +58,45 @@ 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`
+
+ strings.to_lower("test") -> test
+ strings.to_lower("Test") -> test
+*/
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
for r in s {
- write_rune_builder(&b, unicode.to_lower(r))
+ write_rune(&b, unicode.to_lower(r))
}
return to_string(b)
}
+
+/*
+ returns the input string `s` with all runes set to upper case
+ always allocates using the `allocator`
+
+ strings.to_lower("test") -> TEST
+ strings.to_lower("Test") -> TEST
+*/
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
for r in s {
- write_rune_builder(&b, unicode.to_upper(r))
+ write_rune(&b, unicode.to_upper(r))
}
return to_string(b)
}
-
-
-
+// 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)
}
+// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
@@ -101,7 +116,10 @@ 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
+*/
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 {
@@ -122,13 +140,14 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
}
}
-
to_lower_camel_case :: to_camel_case
+
+// converts the `s` string to "lowerCamelCase"
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -147,11 +166,13 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
}
to_upper_camel_case :: to_pascal_case
+
+// converts the `s` string to "PascalCase"
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -169,11 +190,20 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
+/*
+ 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
+
+ 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 {
s := s
s = trim_space(s)
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
w := to_writer(&b)
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower
@@ -208,31 +238,41 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
return to_string(b)
}
-
+/*
+ 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)
}
to_screaming_snake_case :: to_upper_snake_case
+
+// converts the `s` string to "SNAKE_CASE" with all runes uppercased
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator)
}
+// converts the `s` string to "kebab-case" with all runes lowercased
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator)
}
-to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
+// converts the `s` string to "KEBAB-CASE" with all runes uppercased
+to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator)
}
+// converts the `s` string to "Ada_case"
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_'
s := s
s = trim_space(s)
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
w := to_writer(&b)
prev, curr: rune
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index ff26d7dbb..1e9577e61 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -1,22 +1,27 @@
package strings
-import "core:mem"
+import "core:runtime"
+// 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
+// `allocator` is used to allocate the actual `Intern_Entry` strings
Intern :: struct {
- allocator: mem.Allocator,
+ allocator: runtime.Allocator,
entries: map[string]^Intern_Entry,
}
+// initialize the entries map and set the allocator for the string entries
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)
}
+// free the map and all its content allocated using the `.allocator`
intern_destroy :: proc(m: ^Intern) {
for _, value in m.entries {
free(value, m.allocator)
@@ -24,15 +29,22 @@ intern_destroy :: proc(m: ^Intern) {
delete(m.entries)
}
+// 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
intern_get :: proc(m: ^Intern, text: string) -> string {
entry := _intern_get_entry(m, text)
#no_bounds_check return string(entry.str[:entry.len])
}
+
+// 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
intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
entry := _intern_get_entry(m, text)
return cstring(&entry.str[0])
}
+// looks up wether the `text` string exists in the map, returns the entry
+// sets & allocates the entry if it wasnt set yet
_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev
@@ -42,7 +54,8 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_
}
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
- new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator))
+ ptr, _ := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator)
+ new_entry := (^Intern_Entry)(ptr)
new_entry.len = len(text)
copy(new_entry.str[:new_entry.len], text)
diff --git a/core/strings/reader.odin b/core/strings/reader.odin
index ba266c0b5..9b2e10b68 100644
--- a/core/strings/reader.odin
+++ b/core/strings/reader.odin
@@ -3,46 +3,60 @@ package strings
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`
+*/
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
+// init the reader to the string `s`
reader_init :: proc(r: ^Reader, s: string) {
r.s = s
r.i = 0
r.prev_rune = -1
}
+// returns a stream from the reader data
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r
s.stream_vtable = _reader_vtable
return
}
+// init a reader to the string `s` and return an io.Reader
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
reader_init(r, s)
rr, _ := io.to_reader(reader_to_stream(r))
return rr
}
+
+// init a reader to the string `s` and return an io.Reader_At
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
}
+
+// init a reader to the string `s` and return an io.Byte_Reader
to_byte_reader :: proc(r: ^Reader, s: string) -> io.Byte_Reader {
reader_init(r, s)
rr, _ := io.to_byte_reader(reader_to_stream(r))
return rr
}
+
+// init a reader to the string `s` and return an io.Rune_Reader
to_rune_reader :: proc(r: ^Reader, s: string) -> io.Rune_Reader {
reader_init(r, s)
rr, _ := io.to_rune_reader(reader_to_stream(r))
return rr
}
-
+// remaining length of the reader
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0
@@ -50,10 +64,13 @@ reader_length :: proc(r: ^Reader) -> int {
return int(i64(len(r.s)) - r.i)
}
+// returns the string length stored by the reader
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s))
}
+// reads len(p) bytes into the slice from the string in the reader
+// returns `n` amount of read bytes and an io.Error
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF
@@ -63,6 +80,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
r.i += i64(n)
return
}
+
+// 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
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset
@@ -76,6 +96,8 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
}
return
}
+
+// reads and returns a single byte - error when out of bounds
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
@@ -85,6 +107,8 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.i += 1
return b, nil
}
+
+// decreases the reader offset - error when below 0
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
@@ -93,6 +117,8 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
r.i -= 1
return nil
}
+
+// 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) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1
@@ -107,6 +133,9 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
r.i += i64(size)
return
}
+
+// decreases the reader offset by the last rune
+// can only be used once and after a valid read_rune call
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
@@ -118,6 +147,8 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
r.prev_rune = -1
return nil
}
+
+// seeks the reader offset to a wanted offset
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1
abs: i64
@@ -138,6 +169,8 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
r.i = abs
return abs, nil
}
+
+// writes the string content left to read into the io.Writer `w`
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)) {
@@ -157,7 +190,6 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
return
}
-
@(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 a8199e0cf..603917698 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,17 +1,28 @@
+// simple procedures to manipulate UTF-8 encoded strings
package strings
import "core:io"
import "core:mem"
+import "core:slice"
import "core:unicode"
import "core:unicode/utf8"
+// returns a clone of the string `s` allocated using the `allocator`
clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string {
- c := make([]byte, len(s)+1, allocator, loc)
+ c := make([]byte, len(s), allocator, loc)
copy(c, s)
- c[len(s)] = 0
return string(c[:len(s)])
}
+// returns a clone of the string `s` allocated using the `allocator`
+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
+}
+
+// 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
clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring {
c := make([]byte, len(s)+1, allocator, loc)
copy(c, s)
@@ -19,27 +30,35 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
return cstring(&c[0])
}
+// returns a string from a byte pointer `ptr` and byte length `len`
+// the string is valid as long as the parameters stay alive
string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
return transmute(string)mem.Raw_String{ptr, len}
}
+// returns a string from a byte pointer `ptr and byte length `len`
+// searches for a nul byte from 0.. string {
s := transmute(string)mem.Raw_String{ptr, len}
s = truncate_to_byte(s, 0)
return s
}
-
+// returns the raw ^byte start of the string `str`
ptr_from_string :: proc(str: string) -> ^byte {
d := transmute(mem.Raw_String)str
return d.data
}
+// returns the transmute of string `str` to a cstring
+// not safe since the origin string may not contain a nul byte
unsafe_string_to_cstring :: proc(str: string) -> cstring {
d := transmute(mem.Raw_String)str
return cstring(d.data)
}
+// 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
truncate_to_byte :: proc(str: string, b: byte) -> string {
n := index_byte(str, b)
if n < 0 {
@@ -47,6 +66,9 @@ truncate_to_byte :: proc(str: string, b: byte) -> string {
}
return str[:n]
}
+
+// 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
truncate_to_rune :: proc(str: string, r: rune) -> string {
n := index_rune(str, r)
if n < 0 {
@@ -55,20 +77,28 @@ truncate_to_rune :: proc(str: string, r: rune) -> string {
return str[:n]
}
+// returns a cloned string of the byte array `s` using the `allocator`
+// appends a leading nul 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)])
}
+
+// returns a clone of the cstring `s` using the `allocator` as a string
clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string {
return clone(string(s), allocator, loc)
}
+
+// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator`
+// same to `string_from_ptr` but allocates
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
clone_from :: proc{
clone,
clone_from_bytes,
@@ -76,6 +106,8 @@ clone_from :: proc{
clone_from_ptr,
}
+// 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
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)
@@ -83,11 +115,12 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
}
// Compares two strings, returning a value representing which one comes first lexiographically.
-// -1 for `a`; 1 for `b`, or 0 if they are equal.
+// -1 for `lhs`; 1 for `rhs`, 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
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r {
@@ -97,20 +130,48 @@ contains_rune :: proc(s: string, r: rune) -> int {
return -1
}
+/*
+ returns true when the string `substr` is contained inside the string `s`
+
+ 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
+*/
contains_any :: proc(s, chars: string) -> bool {
return index_any(s, chars) >= 0
}
+/*
+ returns the utf8 rune count of the string `s`
+ 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
+ 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
loop: for s != "" && t != "" {
@@ -154,15 +215,71 @@ equal_fold :: proc(u, v: string) -> bool {
return s == t
}
+/*
+ return the prefix length common between strings `a` and `b`.
+
+ 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))
+
+ // Scan for matches including partial codepoints.
+ #no_bounds_check for n < _len && a[n] == b[n] {
+ n += 1
+ }
+
+ // Now scan to ignore partial codepoints.
+ if n > 0 {
+ s := a[:n]
+ n = 0
+ for {
+ r0, w := utf8.decode_rune(s[n:])
+ if r0 != utf8.RUNE_ERROR {
+ n += w
+ } else {
+ break
+ }
+ }
+ }
+ return
+}
+
+/*
+ return true when the string `prefix` is contained at the start of the string `s`
+
+ 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
+
+ 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`
+ 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 {
return ""
@@ -182,6 +299,33 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
return string(b)
}
+join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) {
+ if len(a) == 0 {
+ return "", nil
+ }
+
+ n := len(sep) * (len(a) - 1)
+ for s in a {
+ n += len(s)
+ }
+
+ b := make([]byte, n, allocator) or_return
+ i := copy(b, a[0])
+ for s in a[1:] {
+ i += copy(b[i:], sep)
+ i += copy(b[i:], s)
+ }
+ return string(b), nil
+}
+
+/*
+ returns a combined string from the slice of strings `a` without a seperator
+ allocates the string using the `allocator`
+
+
+ a := [?]string { "a", "b", "c" }
+ b := strings.concatenate(a[:]) -> "abc"
+*/
concatenate :: proc(a: []string, allocator := context.allocator) -> string {
if len(a) == 0 {
return ""
@@ -199,32 +343,75 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
return string(b)
}
+concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
+ if len(a) == 0 {
+ return "", nil
+ }
+
+ n := 0
+ for s in a {
+ n += len(s)
+ }
+ b := make([]byte, n, allocator) or_return
+ i := 0
+ for s in a {
+ i += copy(b[i:], 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 with `rune_offset`.
+ If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`.
+
+ 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
- l := utf8.rune_count_in_string(s)
+ context.allocator = allocator
- if rune_offset >= l { return "" }
+ // If we signal that we want the entire remainder (length <= 0) *and*
+ // the offset is zero, then we can early out by cloning the input
if rune_offset == 0 && rune_length <= 0 {
- return clone(s, allocator)
+ return clone(s)
}
- if rune_length == 0 { rune_length = l }
+ // We need to know if we have enough runes to cover offset + length.
+ rune_count := utf8.rune_count_in_string(s)
+
+ // We're asking for a substring starting after the end of the input string.
+ // That's just an empty string.
+ if rune_offset >= rune_count {
+ return ""
+ }
+
+ // If we don't specify the length of the substring, use the remainder.
+ if rune_length <= 0 {
+ rune_length = rune_count - rune_offset
+ }
+
+ // We don't yet know how many bytes we need exactly.
+ // But we do know it's bounded by the number of runes * 4 bytes,
+ // and can be no more than the size of the input string.
bytes_needed := min(rune_length * 4, len(s))
- buf := make([]u8, bytes_needed, allocator)
+ buf := make([]u8, bytes_needed)
byte_offset := 0
- for i := 0; i < l; i += 1 {
+ for i := 0; i < rune_count; i += 1 {
_, w := utf8.decode_rune_in_string(s)
+
+ // If the rune is part of the substring, copy it to the output buffer.
if i >= rune_offset {
for j := 0; j < w; j += 1 {
buf[byte_offset+j] = s[j]
}
byte_offset += w
}
+
+ // We're done if we reach the end of the input string, *or*
+ // if we've reached a specified length in runes.
if rune_length > 0 {
if i == rune_offset + rune_length - 1 { break }
}
@@ -281,28 +468,61 @@ _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]
+ ```
+*/
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]
+ ```
+*/
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`
+
+ 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`
+
+ 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)
}
-
@private
-_split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: string, ok: bool) {
- s, n := s, n
-
- if n == 0 {
+_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
+ // stop once the string is empty or nil
+ if s == nil || len(s^) == 0 {
return
}
@@ -313,51 +533,190 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: stri
return
}
- if n < 0 {
- n = count(s^, sep) + 1
- }
-
- n -= 1
-
- i := 0
- for ; i < n; i += 1 {
- m := index(s^, sep)
- if m < 0 {
- break
- }
+ m := index(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = res != ""
+ s^ = s[len(s):]
+ } else {
res = s[:m+sep_save]
ok = true
s^ = s[m+len(sep):]
- return
}
- res = s[:]
- ok = res != ""
- s^ = s[len(s):]
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
+ 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
+ }
+*/
+split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
+ m := index_byte(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = res != ""
+ s^ = {}
+ } else {
+ res = s[:m]
+ ok = true
+ s^ = s[m+1:]
+ }
+ return
+}
+
+/*
+ split the ^string `s` by the seperator string `sep` in an iterator fashion
+ consumes the original string till 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
+ }
+*/
split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
- return _split_iterator(s, sep, 0, -1)
+ return _split_iterator(s, sep, 0)
}
-split_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
- return _split_iterator(s, sep, 0, n)
-}
+/*
+ split the ^string `s` after every seperator string `sep` in an iterator fashion
+ consumes the original string till 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
+ }
+*/
split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
- return _split_iterator(s, sep, len(sep), -1)
-}
-
-split_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
- return _split_iterator(s, sep, len(sep), n)
+ return _split_iterator(s, sep, len(sep))
}
+@(private)
+_trim_cr :: proc(s: string) -> string {
+ n := len(s)
+ if n > 0 {
+ if s[n-1] == '\r' {
+ return s[:n-1]
+ }
+ }
+ return s
+}
+/*
+ split the string `s` at every line break '\n'
+ return an allocated slice of strings
+ 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"
+ lines := _split(s, sep, 0, -1, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+/*
+ split the string `s` at every line break '\n' for `n` parts
+ return an allocated slice of strings
+ 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"
+ lines := _split(s, sep, 0, n, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ return an allocated slice of strings
+
+ 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"
+ lines := _split(s, sep, len(sep), -1, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ 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
+
+ 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"
+ lines := _split(s, sep, len(sep), n, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+
+/*
+ split the string `s` at every line break '\n'
+ returns the current split string every iteration till 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
+ }
+*/
+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
+
+ 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
+ }
+*/
+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
+
+ 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 {
if s[i] == c {
@@ -367,7 +726,15 @@ index_byte :: proc(s: string, c: byte) -> int {
return -1
}
-// Returns -1 if c is not present
+/*
+ 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
+
+ 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 {
if s[i] == c {
@@ -378,9 +745,50 @@ last_index_byte :: proc(s: string, c: byte) -> int {
}
+/*
+ returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found
+ avoids invalid runes
+
+ 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 {
+ case 0 <= r && r < utf8.RUNE_SELF:
+ return index_byte(s, byte(r))
+
+ case r == utf8.RUNE_ERROR:
+ for c, i in s {
+ if c == utf8.RUNE_ERROR {
+ return i
+ }
+ }
+ return -1
+
+ case !utf8.valid_rune(r):
+ return -1
+ }
+
+ b, w := utf8.encode_rune(r)
+ return index(s, string(b[:w]))
+}
@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
+*/
index :: proc(s, substr: string) -> int {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
@@ -431,6 +839,14 @@ 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
+*/
last_index :: proc(s, substr: string) -> int {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
@@ -479,7 +895,15 @@ last_index :: proc(s, substr: string) -> int {
return -1
}
-// index_any returns the index of the first char of `chars` found in `s`. -1 if not found.
+/*
+ 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
+*/
index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -504,14 +928,24 @@ index_any :: proc(s, chars: string) -> int {
}
}
- for c in chars {
- if i := index_rune(s, c); i >= 0 {
+ for c, i in s {
+ if index_rune(chars, c) >= 0 {
return i
}
}
return -1
}
+/*
+ returns the index of any first char of `chars` found in `s`, -1 if not found
+ iterates the string in reverse
+
+ strings.index_any("test", "s") -> 2
+ strings.index_any("test", "se") -> 2
+ strings.index_any("test", "et") -> 1
+ strings.index_any("test", "set") -> 3
+ strings.index_any("test", "x") -> -1
+*/
last_index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -561,6 +995,16 @@ last_index_any :: proc(s, chars: string) -> int {
return -1
}
+/*
+ returns the count of the string `substr` found in the string `s`
+ returns the rune_count + 1 of the string `s` on empty `substr`
+
+ 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
return rune_count(s) + 1
@@ -596,7 +1040,12 @@ 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
+ strings.repeat("abc", 2) -> "abcabc"
+*/
repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
if count < 0 {
panic("strings: negative repeat count")
@@ -613,11 +1062,28 @@ 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
+
+ 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)
}
-// if n < 0, no limit on the number of replacements
+/*
+ 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
+
+ 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 {
was_allocation = false
@@ -658,17 +1124,35 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
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
+
+ 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
+
+ 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)
}
@(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 ' '
is_ascii_space :: proc(r: rune) -> bool {
if r < utf8.RUNE_SELF {
return _ascii_space[u8(r)]
@@ -676,6 +1160,7 @@ is_ascii_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is any asci or utf8 based whitespace
is_space :: proc(r: rune) -> bool {
if r < 0x2000 {
switch r {
@@ -694,10 +1179,24 @@ is_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is a nul byte
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
+
+ call :: proc(r: rune) -> bool {
+ return r == 'a'
+ }
+ 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
+*/
index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
for r, i in s {
if p(r) == truth {
@@ -707,6 +1206,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
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 {
@@ -716,6 +1216,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
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; {
@@ -728,6 +1229,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
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; {
@@ -739,7 +1241,17 @@ 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
+ find :: proc(r: rune) -> bool {
+ return r != 'i'
+ }
+ strings.trim_left_proc("testing", find) -> "ing"
+*/
trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := index_proc(s, p, false)
if i == -1 {
@@ -748,29 +1260,10 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
return s[i:]
}
-
-index_rune :: proc(s: string, r: rune) -> int {
- switch {
- case 0 <= r && r < utf8.RUNE_SELF:
- return index_byte(s, byte(r))
-
- case r == utf8.RUNE_ERROR:
- for c, i in s {
- if c == utf8.RUNE_ERROR {
- return i
- }
- }
- return -1
-
- case !utf8.valid_rune(r):
- return -1
- }
-
- b, w := utf8.encode_rune(r)
- return index(s, string(b[:w]))
-}
-
-
+/*
+ trims the input string `s` until the procedure `p` with state returns false
+ returns an empty string when no match was found at all
+*/
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)
if i == -1 {
@@ -779,6 +1272,16 @@ 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
+
+ find :: proc(r: rune) -> bool {
+ return r != 't'
+ }
+ strings.trim_left_proc("testing", find) -> "test"
+*/
trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := last_index_proc(s, p, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -790,6 +1293,10 @@ 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
+*/
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)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -801,7 +1308,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
is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
if state == nil {
return false
@@ -815,7 +1322,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
return false
}
-
+// trims the `cutset` string from the `s` string
trim_left :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -824,6 +1331,7 @@ trim_left :: proc(s: string, cutset: string) -> string {
return trim_left_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string from the right
trim_right :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -832,35 +1340,48 @@ trim_right :: proc(s: string, cutset: string) -> string {
return trim_right_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string, both from left and right
trim :: proc(s: string, cutset: string) -> string {
return trim_right(trim_left(s, cutset), cutset)
}
+// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t"
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"
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"
trim_space :: proc(s: string) -> string {
return trim_right_space(trim_left_space(s))
}
-
+// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00"
trim_left_null :: proc(s: string) -> string {
return trim_left_proc(s, is_null)
}
+// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing"
trim_right_null :: proc(s: string) -> string {
return trim_right_proc(s, is_null)
}
+// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing"
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
+
+ strings.trim_prefix("testing", "test") -> "ing"
+ strings.trim_prefix("testing", "abc") -> "testing"
+*/
trim_prefix :: proc(s, prefix: string) -> string {
if has_prefix(s, prefix) {
return s[len(prefix):]
@@ -868,6 +1389,13 @@ 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
+
+ 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) {
return s[:len(s)-len(suffix)]
@@ -875,148 +1403,157 @@ trim_suffix :: proc(s, suffix: string) -> string {
return s
}
-split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator := context.allocator) -> []string #no_bounds_check {
+/*
+ splits the input string `s` by all possible `substrs` []string
+ returns the allocated []string, nil on any empty substring or no matches
+
+ 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) -> (buf: []string) #no_bounds_check {
if s == "" || len(substrs) <= 0 {
- return nil
+ return
}
- sublen := len(substrs[0])
-
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return
+ }
}
- shared := len(s) - sublen
+ // TODO maybe remove duplicate substrs
+ // sort substrings by string size, largest to smallest
+ temp_substrs := slice.clone(substrs, context.temp_allocator)
+ slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
+ return len(a) > len(b)
+ })
- if shared <= 0 {
- return nil
- }
+ substrings_found: int
+ temp := s
- // number, index, last
- n, i, l := 0, 0, 0
-
- // count results
- first_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- n += 1
- }
-
- i += sublen
- l = i
+ // count substr results found in string
+ first_pass: for len(temp) > 0 {
+ for substr in temp_substrs {
+ size := len(substr)
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ substrings_found += 1
+ temp = temp[size:]
continue first_pass
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- n += 1
+ // skip when no results
+ if substrings_found < 1 {
+ return
}
- if n < 1 {
- // no results
- return nil
- }
+ buf = make([]string, substrings_found + 1, allocator)
+ buf_index: int
+ temp = s
+ temp_old := temp
- buf := make([]string, n, allocator)
-
- n, i, l = 0, 0, 0
-
- // slice results
- second_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- buf[n] = s[l:i]
- n += 1
- }
-
- i += sublen
- l = i
+ // gather results in the same fashion
+ second_pass: for len(temp) > 0 {
+ for substr in temp_substrs {
+ size := len(substr)
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ buf[buf_index] = temp_old[:len(temp_old) - len(temp)]
+ buf_index += 1
+ temp = temp[size:]
+ temp_old = temp
continue second_pass
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- buf[n] = s[l:]
- }
+ buf[buf_index] = temp_old[:]
return buf
}
+// state for the split multi iterator
+Split_Multi :: struct {
+ temp: string,
+ temp_old: string,
+ substrs: []string,
+}
+// returns split multi state with sorted `substrs`
+split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi {
+ // sort substrings, largest to smallest
+ temp_substrs := slice.clone(substrs, context.temp_allocator)
+ slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
+ return len(a) > len(b)
+ })
-
-split_multi_iterator :: proc(s: ^string, substrs: []string, skip_empty := false) -> (string, bool) #no_bounds_check {
- if s == nil || s^ == "" || len(substrs) <= 0 {
- return "", false
+ return {
+ temp = s,
+ temp_old = s,
+ substrs = temp_substrs,
}
+}
- sublen := len(substrs[0])
+/*
+ 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
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ splits := [?]string { "---", "~~~", ".", "_", "," }
+ state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:])
+ for str in strings.split_multi_iterate(&state) {
+ fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last]
}
-
- shared := len(s) - sublen
-
- if shared <= 0 {
- return "", false
- }
-
- // index, last
- i, l := 0, 0
-
- loop: for i <= shared {
+*/
+split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check {
+ pass: for len(temp) > 0 {
for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- res := s[l:i]
- s^ = s[i:]
- return res, true
- }
+ size := len(substr)
- i += sublen
- l = i
-
- continue loop
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ res = temp_old[:len(temp_old) - len(temp)]
+ temp = temp[size:]
+ temp_old = temp
+ ok = true
+ return
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- res := s[l:]
- s^ = s[len(s):]
- return res, true
+ // allow last iteration
+ if temp_old != "" {
+ res = temp_old[:]
+ ok = true
+ temp_old = ""
}
- return "", false
+ return
}
-
-
-
-
-
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
// Adjacent invalid bytes are only replaced once
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s
b: Builder
- init_builder(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator)
has_error := false
cursor := 0
@@ -1045,7 +1582,13 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ returns a reversed version of the `s` string
+ a := "abcxyz"
+ b := strings.reverse(a)
+ fmt.eprintln(a, b) // abcxyz zyxcba
+*/
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s
n := len(str)
@@ -1061,18 +1604,25 @@ 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
+
+ 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 {
panic("tab size must be positive")
}
-
if s == "" {
return ""
}
b: Builder
- init_builder(&b, allocator)
+ builder_init(&b, allocator)
writer := to_writer(&b)
str := s
column: int
@@ -1104,7 +1654,16 @@ 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
+ 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)
if i == -1 {
@@ -1131,8 +1690,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
pad_len := rune_count(pad)
b: Builder
- init_builder(&b, allocator)
- grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, allocator)
+ builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b)
@@ -1154,8 +1713,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
pad_len := rune_count(pad)
b: Builder
- init_builder(&b, allocator)
- grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, allocator)
+ builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b)
@@ -1176,8 +1735,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
pad_len := rune_count(pad)
b: Builder
- init_builder(&b, allocator)
- grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, allocator)
+ builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
w := to_writer(&b)
@@ -1288,8 +1847,102 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
}
if start >= 0 {
- append(&substrings, s[start : end])
+ append(&substrings, s[start : len(s)])
}
return substrings[:]
}
+
+
+// `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
+fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
+ start, end := -1, -1
+ for r, offset in s {
+ end = offset
+ if unicode.is_space(r) {
+ if start >= 0 {
+ field = s[start : end]
+ ok = true
+ s^ = s[end:]
+ return
+ }
+ } else {
+ if start < 0 {
+ start = end
+ }
+ }
+ }
+
+ // if either of these are true, the string did not contain any characters
+ if end < 0 || start < 0 {
+ return "", false
+ }
+
+ field = s[start:]
+ ok = true
+ s^ = s[len(s):]
+ return
+}
+
+// `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.
+levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int {
+ LEVENSHTEIN_DEFAULT_COSTS: []int : {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63,
+ }
+
+ m, n := utf8.rune_count_in_string(a), utf8.rune_count_in_string(b)
+
+ if m == 0 {
+ return n
+ }
+ if n == 0 {
+ return m
+ }
+
+ costs: []int
+
+ if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
+ costs = make([]int, n + 1, allocator)
+ for k in 0..=n {
+ costs[k] = k
+ }
+ } else {
+ costs = LEVENSHTEIN_DEFAULT_COSTS
+ }
+
+ defer if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
+ delete(costs, allocator)
+ }
+
+ i: int
+ for c1 in a {
+ costs[0] = i + 1
+ corner := i
+ j: int
+ for c2 in b {
+ upper := costs[j + 1]
+ if c1 == c2 {
+ costs[j + 1] = corner
+ } else {
+ t := upper if upper < corner else corner
+ costs[j + 1] = (costs[j] if costs[j] < t else t) + 1
+ }
+
+ corner = upper
+ j += 1
+ }
+
+ i += 1
+ }
+
+ return costs[n]
+}
diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin
index 21dcea178..0900a6544 100644
--- a/core/sync/atomic.odin
+++ b/core/sync/atomic.odin
@@ -2,167 +2,44 @@ package sync
import "core:intrinsics"
-Ordering :: enum {
- Relaxed, // Monotonic
- Release,
- Acquire,
- Acquire_Release,
- Sequentially_Consistent,
-}
-
-strongest_failure_ordering_table := [Ordering]Ordering{
- .Relaxed = .Relaxed,
- .Release = .Relaxed,
- .Acquire = .Acquire,
- .Acquire_Release = .Acquire,
- .Sequentially_Consistent = .Sequentially_Consistent,
-}
-
-strongest_failure_ordering :: #force_inline proc(order: Ordering) -> Ordering {
- return strongest_failure_ordering_table[order]
-}
-
-fence :: #force_inline proc($order: Ordering) {
- when order == .Relaxed { #panic("there is no such thing as a relaxed fence") }
- else when order == .Release { intrinsics.atomic_fence_rel() }
- else when order == .Acquire { intrinsics.atomic_fence_acq() }
- else when order == .Acquire_Release { intrinsics.atomic_fence_acqrel() }
- else when order == .Sequentially_Consistent { intrinsics.atomic_fence() }
- else { #panic("unknown order") }
+cpu_relax :: intrinsics.cpu_relax
+
+/*
+Atomic_Memory_Order :: enum {
+ Relaxed = 0, // Unordered
+ Consume = 1, // Monotonic
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
}
+*/
+Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
-atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) {
- when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) }
- else when order == .Release { intrinsics.atomic_store_rel(dst, val) }
- else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) }
- else when order == .Acquire { #panic("there is not such thing as an acquire store") }
- else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") }
- else { #panic("unknown order") }
-}
-
-atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) }
- else when order == .Acquire { return intrinsics.atomic_load_acq(dst) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) }
- else when order == .Release { #panic("there is no such thing as a release load") }
- else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") }
- else { #panic("unknown order") }
-}
-
-atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-
-atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_add_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_and_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_or_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) }
- else { #panic("unknown order") }
-}
+atomic_thread_fence :: intrinsics.atomic_thread_fence
+atomic_signal_fence :: intrinsics.atomic_signal_fence
+atomic_store :: intrinsics.atomic_store
+atomic_store_explicit :: intrinsics.atomic_store_explicit
+atomic_load :: intrinsics.atomic_load
+atomic_load_explicit :: intrinsics.atomic_load_explicit
+atomic_add :: intrinsics.atomic_add
+atomic_add_explicit :: intrinsics.atomic_add_explicit
+atomic_sub :: intrinsics.atomic_sub
+atomic_sub_explicit :: intrinsics.atomic_sub_explicit
+atomic_and :: intrinsics.atomic_and
+atomic_and_explicit :: intrinsics.atomic_and_explicit
+atomic_nand :: intrinsics.atomic_nand
+atomic_nand_explicit :: intrinsics.atomic_nand_explicit
+atomic_or :: intrinsics.atomic_or
+atomic_or_explicit :: intrinsics.atomic_or_explicit
+atomic_xor :: intrinsics.atomic_xor
+atomic_xor_explicit :: intrinsics.atomic_xor_explicit
+atomic_exchange :: intrinsics.atomic_exchange
+atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
+// Returns value and optional ok boolean
+atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
+atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
+atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
+atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
\ No newline at end of file
diff --git a/core/sync/barrier.odin b/core/sync/barrier.odin
deleted file mode 100644
index 997fde82d..000000000
--- a/core/sync/barrier.odin
+++ /dev/null
@@ -1,81 +0,0 @@
-package sync
-
-
-// A barrier enabling multiple threads to synchronize the beginning of some computation
-/*
- * Example:
- *
- * package example
- *
- * import "core:fmt"
- * import "core:sync"
- * import "core:thread"
- *
- * barrier := &sync.Barrier{};
- *
- * main :: proc() {
- * fmt.println("Start");
- *
- * THREAD_COUNT :: 4;
- * threads: [THREAD_COUNT]^thread.Thread;
- *
- * sync.barrier_init(barrier, THREAD_COUNT);
- * defer sync.barrier_destroy(barrier);
- *
- *
- * for _, i in threads {
- * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
- * // Same messages will be printed together but without any interleaving
- * fmt.println("Getting ready!");
- * sync.barrier_wait(barrier);
- * fmt.println("Off their marks they go!");
- * });
- * }
- *
- * for t in threads {
- * thread.destroy(t); // join and free thread
- * }
- * fmt.println("Finished");
- * }
- *
- */
-Barrier :: struct {
- mutex: Blocking_Mutex,
- cond: Condition,
- index: int,
- generation_id: int,
- thread_count: int,
-}
-
-barrier_init :: proc(b: ^Barrier, thread_count: int) {
- blocking_mutex_init(&b.mutex)
- condition_init(&b.cond, &b.mutex)
- b.index = 0
- b.generation_id = 0
- b.thread_count = thread_count
-}
-
-barrier_destroy :: proc(b: ^Barrier) {
- blocking_mutex_destroy(&b.mutex)
- condition_destroy(&b.cond)
-}
-
-// Block the current thread until all threads have rendezvoused
-// Barrier can be reused after all threads rendezvoused once, and can be used continuously
-barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
- blocking_mutex_lock(&b.mutex)
- defer blocking_mutex_unlock(&b.mutex)
- local_gen := b.generation_id
- b.index += 1
- if b.index < b.thread_count {
- for local_gen == b.generation_id && b.index < b.thread_count {
- condition_wait_for(&b.cond)
- }
- return false
- }
-
- b.index = 0
- b.generation_id += 1
- condition_broadcast(&b.cond)
- return true
-}
diff --git a/core/sync/channel.odin b/core/sync/channel.odin
deleted file mode 100644
index 82b9504f4..000000000
--- a/core/sync/channel.odin
+++ /dev/null
@@ -1,889 +0,0 @@
-package sync
-
-import "core:mem"
-import "core:time"
-import "core:intrinsics"
-import "core:math/rand"
-
-_, _ :: time, rand
-
-Channel_Direction :: enum i8 {
- Both = 0,
- Send = +1,
- Recv = -1,
-}
-
-Channel :: struct($T: typeid, $Direction := Channel_Direction.Both) {
- using _internal: ^Raw_Channel,
-}
-
-channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_destroy :: proc(ch: $C/Channel($T, $D)) {
- raw_channel_destroy(ch._internal)
-}
-
-channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
- res._internal = ch._internal
- return
-}
-
-channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
- res._internal = ch._internal
- return
-}
-
-
-channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.len if ch._internal != nil else 0
-}
-channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.cap if ch._internal != nil else 0
-}
-
-
-channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
- msg := msg
- _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc)
-}
-channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
- msg := msg
- return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc)
-}
-
-channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
- c := ch._internal
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- mutex_lock(&c.mutex)
- raw_channel_recv_impl(c, &msg, loc)
- mutex_unlock(&c.mutex)
- return
-}
-channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c != nil && mutex_try_lock(&c.mutex) {
- if c.len > 0 {
- raw_channel_recv_impl(c, &msg, loc)
- ok = true
- }
- mutex_unlock(&c.mutex)
- }
- return
-}
-channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
- res: T
- res, ok = channel_try_recv(ch, loc)
- if ok && msg != nil {
- msg^ = res
- }
- return
-}
-
-
-channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
- return ch._internal == nil
-}
-channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
- c := ch._internal
- return c != nil && !c.closed
-}
-
-
-channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal == b._internal
-}
-channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal != b._internal
-}
-
-
-channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
- return raw_channel_can_send(ch._internal)
-}
-channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
- return raw_channel_can_recv(ch._internal)
-}
-
-
-channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
- c := ch._internal
- if c == nil {
- return -1
- }
- if intrinsics.atomic_load(&c.closed) {
- return -1
- }
- return intrinsics.atomic_load(&c.len)
-}
-
-
-channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
- raw_channel_close(ch._internal, loc)
-}
-
-
-channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c == nil {
- return
- }
-
- if !c.closed || c.len > 0 {
- msg, ok = channel_recv(ch), true
- }
- return
-}
-channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
- raw_channel_drain(ch._internal)
-}
-
-
-channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
- for msg in channel_iterator(src) {
- channel_send(dst, msg)
- }
-}
-
-
-Raw_Channel_Wait_Queue :: struct {
- next: ^Raw_Channel_Wait_Queue,
- state: ^uintptr,
-}
-
-
-Raw_Channel :: struct {
- closed: bool,
- ready: bool, // ready to recv
- data_offset: u16, // data is stored at the end of this data structure
- elem_size: u32,
- len, cap: int,
- read, write: int,
- mutex: Mutex,
- cond: Condition,
- allocator: mem.Allocator,
-
- sendq: ^Raw_Channel_Wait_Queue,
- recvq: ^Raw_Channel_Wait_Queue,
-}
-
-raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- val.next = head^
- head^ = val
-}
-raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- p := head
- for p^ != nil && p^ != val {
- p = &p^.next
- }
- if p != nil {
- p^ = p^.next
- }
-}
-
-
-raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
- assert(int(u32(elem_size)) == elem_size)
-
- s := size_of(Raw_Channel)
- s = mem.align_forward_int(s, elem_align)
- data_offset := uintptr(s)
- s += elem_size * max(cap, 1)
-
- a := max(elem_align, align_of(Raw_Channel))
-
- c := (^Raw_Channel)(mem.alloc(s, a))
- if c == nil {
- return nil
- }
-
- c.data_offset = u16(data_offset)
- c.elem_size = u32(elem_size)
- c.len, c.cap = 0, max(cap, 0)
- c.read, c.write = 0, 0
- mutex_init(&c.mutex)
- condition_init(&c.cond, &c.mutex)
- c.allocator = context.allocator
- c.closed = false
-
- return c
-}
-
-
-raw_channel_destroy :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- context.allocator = c.allocator
- intrinsics.atomic_store(&c.closed, true)
-
- condition_destroy(&c.cond)
- mutex_destroy(&c.mutex)
- free(c)
-}
-
-raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
- if c == nil {
- panic(message="cannot close nil channel", loc=loc)
- }
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
- intrinsics.atomic_store(&c.closed, true)
-
- // Release readers and writers
- raw_channel_wait_queue_broadcast(c.recvq)
- raw_channel_wait_queue_broadcast(c.sendq)
- condition_broadcast(&c.cond)
-}
-
-
-
-raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
- send :: proc(c: ^Raw_Channel, src: rawptr) {
- data := uintptr(c) + uintptr(c.data_offset)
- dst := data + uintptr(c.write * int(c.elem_size))
- mem.copy(rawptr(dst), src, int(c.elem_size))
- c.len += 1
- c.write = (c.write + 1) % max(c.cap, 1)
- }
-
- switch {
- case c == nil:
- panic(message="cannot send message; channel is nil", loc=loc)
- case c.closed:
- panic(message="cannot send message; channel is closed", loc=loc)
- }
-
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
-
- if c.cap > 0 {
- if !block && c.len >= c.cap {
- return false
- }
-
- for c.len >= c.cap {
- condition_wait_for(&c.cond)
- }
- } else if c.len > 0 { // TODO(bill): determine correct behaviour
- if !block {
- return false
- }
- condition_wait_for(&c.cond)
- } else if c.len == 0 && !block {
- return false
- }
-
- send(c, msg)
- condition_signal(&c.cond)
- raw_channel_wait_queue_signal(c.recvq)
-
- return true
-}
-
-raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
- recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
- if c.len < 1 {
- panic(message="cannot recv message; channel is empty", loc=loc)
- }
- c.len -= 1
-
- data := uintptr(c) + uintptr(c.data_offset)
- src := data + uintptr(c.read * int(c.elem_size))
- mem.copy(dst, rawptr(src), int(c.elem_size))
- c.read = (c.read + 1) % max(c.cap, 1)
- }
-
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- intrinsics.atomic_store(&c.ready, true)
- for c.len < 1 {
- raw_channel_wait_queue_signal(c.sendq)
- condition_wait_for(&c.cond)
- }
- intrinsics.atomic_store(&c.ready, false)
- recv(c, res, loc)
- if c.cap > 0 {
- if c.len == c.cap - 1 {
- // NOTE(bill): Only signal on the last one
- condition_signal(&c.cond)
- }
- } else {
- condition_signal(&c.cond)
- }
-}
-
-
-raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- switch {
- case c.closed:
- ok = false
- case c.cap > 0:
- ok = c.ready && c.len < c.cap
- case:
- ok = c.ready && c.len == 0
- }
- mutex_unlock(&c.mutex)
- return
-}
-raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- ok = c.len > 0
- mutex_unlock(&c.mutex)
- return
-}
-
-
-raw_channel_drain :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- mutex_lock(&c.mutex)
- c.len = 0
- c.read = 0
- c.write = 0
- mutex_unlock(&c.mutex)
-}
-
-
-
-MAX_SELECT_CHANNELS :: 64
-SELECT_MAX_TIMEOUT :: max(time.Duration)
-
-Select_Command :: enum {
- Recv,
- Send,
-}
-
-Select_Channel :: struct {
- channel: ^Raw_Channel,
- command: Select_Command,
-}
-
-
-
-select :: proc(channels: ..Select_Channel) -> (index: int) {
- return select_timeout(SELECT_MAX_TIMEOUT, ..channels)
-}
-select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- wait_state: uintptr = 0
- for _, i in channels {
- q := &queues[i]
- q.state = &wait_state
- }
-
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q)
- }
- }
- raw_channel_wait_queue_wait_on(&wait_state, timeout)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q)
- }
- }
-
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
- if count == 0 && timeout == SELECT_MAX_TIMEOUT {
- index = -1
- return
- }
-
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
-
- return
-}
-
-select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
-
- if msg != nil {
- channel_send(channels[index], msg)
- }
-
- return
-}
-
-select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.sendq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.sendq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try :: proc(channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- index = -1
- if raw_channel_can_recv(channels[0]) {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
- switch len(channels) {
- case 0:
- return -1
- case 1:
- if raw_channel_can_send(channels[0]) {
- return 0
- }
- return -1
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- ok: bool
- if msg, ok = channel_try_recv(channels[0]); ok {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
- return
-}
-
-select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- index = -1
- switch len(channels) {
- case 0:
- return
- case 1:
- if channel_try_send(channels[0], msg) {
- index = 0
- }
- return
- }
-
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- channel_send(channels[index], msg)
- return
-}
-
diff --git a/core/sync/channel_unix.odin b/core/sync/channel_unix.odin
deleted file mode 100644
index d6bac2d71..000000000
--- a/core/sync/channel_unix.odin
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build linux, darwin, freebsd
-package sync
-
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- // stub
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
diff --git a/core/sync/channel_windows.odin b/core/sync/channel_windows.odin
deleted file mode 100644
index 5d469ffff..000000000
--- a/core/sync/channel_windows.odin
+++ /dev/null
@@ -1,33 +0,0 @@
-package sync
-
-import "core:intrinsics"
-import win32 "core:sys/windows"
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- ms: win32.DWORD = win32.INFINITE
- if max(time.Duration) != SELECT_MAX_TIMEOUT {
- ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000)
- }
-
- v := intrinsics.atomic_load(state)
- for v == 0 {
- win32.WaitOnAddress(state, &v, size_of(state^), ms)
- v = intrinsics.atomic_load(state)
- }
- intrinsics.atomic_store(state, 0)
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressSingle(x.state)
- }
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressAll(x.state)
- }
-}
diff --git a/core/sync/sync2/extended.odin b/core/sync/extended.odin
similarity index 57%
rename from core/sync/sync2/extended.odin
rename to core/sync/extended.odin
index d6a99fe04..49d296c90 100644
--- a/core/sync/sync2/extended.odin
+++ b/core/sync/extended.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -16,8 +16,7 @@ wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
return
}
- mutex_lock(&wg.mutex)
- defer mutex_unlock(&wg.mutex)
+ guard(&wg.mutex)
atomic_add(&wg.counter, delta)
if wg.counter < 0 {
@@ -36,8 +35,7 @@ wait_group_done :: proc(wg: ^Wait_Group) {
}
wait_group_wait :: proc(wg: ^Wait_Group) {
- mutex_lock(&wg.mutex)
- defer mutex_unlock(&wg.mutex)
+ guard(&wg.mutex)
if wg.counter != 0 {
cond_wait(&wg.cond, &wg.mutex)
@@ -51,8 +49,7 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
if duration <= 0 {
return false
}
- mutex_lock(&wg.mutex)
- defer mutex_unlock(&wg.mutex)
+ guard(&wg.mutex)
if wg.counter != 0 {
if !cond_wait_with_timeout(&wg.cond, &wg.mutex, duration) {
@@ -67,44 +64,41 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
-// A barrier enabling multiple threads to synchronize the beginning of some computation
/*
- * Example:
- *
- * package example
- *
- * import "core:fmt"
- * import "core:sync"
- * import "core:thread"
- *
- * barrier := &sync.Barrier{}
- *
- * main :: proc() {
- * fmt.println("Start")
- *
- * THREAD_COUNT :: 4
- * threads: [THREAD_COUNT]^thread.Thread
- *
- * sync.barrier_init(barrier, THREAD_COUNT)
- * defer sync.barrier_destroy(barrier)
- *
- *
- * for _, i in threads {
- * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
- * // Same messages will be printed together but without any interleaving
- * fmt.println("Getting ready!")
- * sync.barrier_wait(barrier)
- * fmt.println("Off their marks they go!")
- * })
- * }
- *
- * for t in threads {
- * thread.destroy(t) // join and free thread
- * }
- * fmt.println("Finished")
- * }
- *
- */
+A barrier enabling multiple threads to synchronize the beginning of some computation
+
+Example:
+ package example
+
+ import "core:fmt"
+ import "core:sync"
+ import "core:thread"
+
+ barrier := &sync.Barrier{}
+
+ main :: proc() {
+ fmt.println("Start")
+
+ THREAD_COUNT :: 4
+ threads: [THREAD_COUNT]^thread.Thread
+
+ sync.barrier_init(barrier, THREAD_COUNT)
+
+ for _, i in threads {
+ threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
+ // Same messages will be printed together but without any interleaving
+ fmt.println("Getting ready!")
+ sync.barrier_wait(barrier)
+ fmt.println("Off their marks they go!")
+ })
+ }
+
+ for t in threads {
+ thread.destroy(t) // join and free thread
+ }
+ fmt.println("Finished")
+ }
+*/
Barrier :: struct {
mutex: Mutex,
cond: Cond,
@@ -122,8 +116,7 @@ barrier_init :: proc(b: ^Barrier, thread_count: int) {
// Block the current thread until all threads have rendezvoused
// Barrier can be reused after all threads rendezvoused once, and can be used continuously
barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
- mutex_lock(&b.mutex)
- defer mutex_unlock(&b.mutex)
+ guard(&b.mutex)
local_gen := b.generation_id
b.index += 1
if b.index < b.thread_count {
@@ -149,10 +142,10 @@ Auto_Reset_Event :: struct {
}
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_load_relaxed(&e.status)
+ old_status := atomic_load_explicit(&e.status, .Relaxed)
for {
new_status := old_status + 1 if old_status < 1 else 1
- if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok {
+ if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok {
break
}
@@ -163,7 +156,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
}
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_sub_acquire(&e.status, 1)
+ old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 {
sema_wait(&e.sema)
}
@@ -177,14 +170,14 @@ Ticket_Mutex :: struct {
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add_relaxed(&m.ticket, 1)
- for ticket != atomic_load_acquire(&m.serving) {
+ ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
+ for ticket != atomic_load_explicit(&m.serving, .Acquire) {
cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add_relaxed(&m.serving, 1)
+ atomic_add_explicit(&m.serving, 1, .Relaxed)
}
@(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
@@ -199,18 +192,18 @@ Benaphore :: struct {
}
benaphore_lock :: proc(b: ^Benaphore) {
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0)
+ v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema)
}
}
@@ -230,7 +223,7 @@ Recursive_Benaphore :: struct {
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
if tid != b.owner {
sema_wait(&b.sema)
}
@@ -243,10 +236,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
- atomic_add_acquire(&b.counter, 1)
+ atomic_add_explicit(&b.counter, 1, .Acquire)
}
- if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 {
+ if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 {
return false
}
// inside the lock
@@ -263,7 +256,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
if recursion == 0 {
b.owner = 0
}
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
if recursion == 0 {
sema_post(&b.sema)
}
@@ -292,16 +285,71 @@ Once :: struct {
once_do :: proc(o: ^Once, fn: proc()) {
@(cold)
do_slow :: proc(o: ^Once, fn: proc()) {
- mutex_lock(&o.m)
- defer mutex_unlock(&o.m)
+ guard(&o.m)
if !o.done {
fn()
- atomic_store_release(&o.done, true)
+ atomic_store_explicit(&o.done, true, .Release)
}
}
- if atomic_load_acquire(&o.done) == false {
+ if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}
+
+
+
+// A Parker is an associated token which is initially not present:
+// * The `park` procedure blocks the current thread unless or until the token
+// is available, at which point the token is consumed.
+// * The `park_with_timeout` procedures works the same as `park` but only
+// blocks for the specified duration.
+// * The `unpark` procedure automatically makes the token available if it
+// was not already.
+Parker :: struct {
+ state: Futex,
+}
+
+// Blocks the current thread until the token is made available.
+//
+// Assumes this is only called by the thread that owns the Parker.
+park :: proc(p: ^Parker) {
+ EMPTY :: 0
+ NOTIFIED :: 1
+ PARKED :: max(u32)
+ if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
+ return
+ }
+ for {
+ futex_wait(&p.state, PARKED)
+ if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok {
+ return
+ }
+ }
+}
+
+// Blocks the current thread until the token is made available, but only
+// for a limited duration.
+//
+// Assumes this is only called by the thread that owns the Parker
+park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
+ EMPTY :: 0
+ NOTIFIED :: 1
+ PARKED :: max(u32)
+ if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
+ return
+ }
+ futex_wait_with_timeout(&p.state, PARKED, duration)
+ atomic_exchange_explicit(&p.state, EMPTY, .Acquire)
+}
+
+// Automatically makes thee token available if it was not already.
+unpark :: proc(p: ^Parker) {
+ EMPTY :: 0
+ NOTIFIED :: 1
+ PARKED :: max(Futex)
+ if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
+ futex_signal(&p.state)
+ }
+}
\ No newline at end of file
diff --git a/core/sync/sync2/futex_darwin.odin b/core/sync/futex_darwin.odin
similarity index 96%
rename from core/sync/sync2/futex_darwin.odin
rename to core/sync/futex_darwin.odin
index 9dad8d375..a106faa9c 100644
--- a/core/sync/sync2/futex_darwin.odin
+++ b/core/sync/futex_darwin.odin
@@ -1,6 +1,6 @@
//+private
//+build darwin
-package sync2
+package sync
import "core:c"
import "core:time"
@@ -8,7 +8,7 @@ import "core:time"
foreign import System "System.framework"
foreign System {
- __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ms: u32) -> c.int ---
+ __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int ---
__ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int ---
__ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int ---
}
diff --git a/core/sync/futex_freebsd.odin b/core/sync/futex_freebsd.odin
new file mode 100644
index 000000000..07fba82a8
--- /dev/null
+++ b/core/sync/futex_freebsd.odin
@@ -0,0 +1,71 @@
+//+private
+//+build freebsd
+package sync
+
+import "core:c"
+import "core:time"
+
+UMTX_OP_WAIT :: 2
+UMTX_OP_WAKE :: 3
+
+ETIMEDOUT :: 60
+
+foreign import libc "system:c"
+
+foreign libc {
+ _umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
+ __error :: proc "c" () -> ^c.int ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+ timeout := [2]i64{14400, 0} // 4 hours
+ for {
+ res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
+
+ if res != -1 {
+ return true
+ }
+
+ if __error()^ == ETIMEDOUT {
+ continue
+ }
+
+ panic("_futex_wait failure")
+ }
+ unreachable()
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+ if duration <= 0 {
+ return false
+ }
+
+ timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)}
+
+ res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
+ if res != -1 {
+ return true
+ }
+
+ if __error()^ == ETIMEDOUT {
+ return false
+ }
+
+ panic("_futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+ res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
+
+ if res == -1 {
+ panic("_futex_signal failure")
+ }
+}
+
+_futex_broadcast :: proc(f: ^Futex) {
+ res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
+
+ if res == -1 {
+ panic("_futex_broadcast failure")
+ }
+}
diff --git a/core/sync/sync2/futex_linux.odin b/core/sync/futex_linux.odin
similarity index 89%
rename from core/sync/sync2/futex_linux.odin
rename to core/sync/futex_linux.odin
index 1bd41c7cf..c429a9d64 100644
--- a/core/sync/sync2/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -1,10 +1,11 @@
//+private
//+build linux
-package sync2
+package sync
import "core:c"
import "core:time"
import "core:intrinsics"
+import "core:sys/unix"
FUTEX_WAIT :: 0
FUTEX_WAKE :: 1
@@ -13,12 +14,6 @@ FUTEX_PRIVATE_FLAG :: 128
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
-foreign import libc "system:c"
-
-foreign libc {
- __errno_location :: proc "c" () -> ^c.int ---
-}
-
ESUCCESS :: 0
EINTR :: -4
EAGAIN :: -11
@@ -34,7 +29,7 @@ get_errno :: proc(r: int) -> int {
}
internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
- code := int(intrinsics.syscall(202, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
+ code := int(intrinsics.syscall(unix.SYS_futex, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
return get_errno(code)
}
diff --git a/core/sync/futex_openbsd.odin b/core/sync/futex_openbsd.odin
new file mode 100644
index 000000000..6ac9d3efb
--- /dev/null
+++ b/core/sync/futex_openbsd.odin
@@ -0,0 +1,78 @@
+//+private
+//+build openbsd
+package sync
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+FUTEX_WAIT :: 1
+FUTEX_WAKE :: 2
+
+FUTEX_PRIVATE_FLAG :: 128
+
+FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
+FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+
+foreign import libc "system:c"
+
+foreign libc {
+ @(link_name="futex")
+ _unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+ res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ return false
+ }
+
+ panic("futex_wait failure")
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+ if duration <= 0 {
+ return false
+ }
+
+ timespec_t :: struct {
+ tv_sec: c.long,
+ tv_nsec: c.long,
+ }
+
+ res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, ×pec_t{
+ tv_sec = (c.long)(duration/1e9),
+ tv_nsec = (c.long)(duration%1e9),
+ })
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ return false
+ }
+
+ panic("futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+ res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
+
+ if res == -1 {
+ panic("futex_wake_single failure")
+ }
+}
+
+_futex_broadcast :: proc(f: ^Futex) {
+ res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
+
+ if res == -1 {
+ panic("_futex_wake_all failure")
+ }
+}
diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin
new file mode 100644
index 000000000..a32935143
--- /dev/null
+++ b/core/sync/futex_wasm.odin
@@ -0,0 +1,36 @@
+//+private
+//+build wasm32, wasm64
+package sync
+
+import "core:intrinsics"
+import "core:time"
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+ s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
+ return s != 0
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+ s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
+ return s != 0
+
+}
+
+_futex_signal :: proc(f: ^Futex) {
+ loop: for {
+ s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
+ if s >= 1 {
+ return
+ }
+ }
+}
+
+_futex_broadcast :: proc(f: ^Futex) {
+ loop: for {
+ s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
+ if s >= 0 {
+ return
+ }
+ }
+}
+
diff --git a/core/sync/sync2/futex_windows.odin b/core/sync/futex_windows.odin
similarity index 56%
rename from core/sync/sync2/futex_windows.odin
rename to core/sync/futex_windows.odin
index 200a119ff..ce662ba9e 100644
--- a/core/sync/sync2/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -1,32 +1,32 @@
//+private
//+build windows
-package sync2
+package sync
import "core:time"
foreign import Synchronization "system:Synchronization.lib"
-
@(default_calling_convention="stdcall")
foreign Synchronization {
- WaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: u32) -> b32 ---
WakeByAddressSingle :: proc(Address: rawptr) ---
WakeByAddressAll :: proc(Address: rawptr) ---
}
-
+foreign import Ntdll "system:Ntdll.lib"
+@(default_calling_convention="stdcall")
+foreign Ntdll {
+ RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 ---
+}
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
expect := expect
- return bool(WaitOnAddress(f, &expect, size_of(expect), ~u32(0)))
+ return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil)
}
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect
- timeout := u32(0)
- if duration > 0 {
- timeout = u32(duration/1e6)
- }
- return bool(WaitOnAddress(f, &expect, size_of(expect), timeout))
+ // NOTE(bill): for some bizarre reason, this has be a negative number
+ timeout := -i64(duration / 100)
+ return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout)
}
_futex_signal :: proc(f: ^Futex) {
diff --git a/core/sync/sync2/primitives.odin b/core/sync/primitives.odin
similarity index 92%
rename from core/sync/sync2/primitives.odin
rename to core/sync/primitives.odin
index d9e013664..bfbdc6f9b 100644
--- a/core/sync/sync2/primitives.odin
+++ b/core/sync/primitives.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -29,12 +29,12 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool {
return _mutex_try_lock(m)
}
-// Example:
-//
-// if mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=mutex_unlock)
mutex_guard :: proc(m: ^Mutex) -> bool {
mutex_lock(m)
@@ -80,25 +80,24 @@ rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_shared_lock(rw)
}
-
-// Example:
-//
-// if rw_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if rw_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=rw_mutex_unlock)
rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_lock(m)
return true
}
-// Example:
-//
-// if rw_mutex_shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if rw_mutex_shared_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=rw_mutex_shared_unlock)
rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_shared_lock(m)
@@ -127,13 +126,12 @@ recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
return _recursive_mutex_try_lock(m)
}
-
-// Example:
-//
-// if recursive_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if recursive_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=recursive_mutex_unlock)
recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool {
recursive_mutex_lock(m)
@@ -197,7 +195,7 @@ sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
Futex :: distinct u32
futex_wait :: proc(f: ^Futex, expected: u32) {
- if u32(atomic_load(f)) != expected {
+ if u32(atomic_load_explicit(f, .Acquire)) != expected {
return
}
@@ -206,7 +204,7 @@ futex_wait :: proc(f: ^Futex, expected: u32) {
// returns true if the wait happened within the duration, false if it exceeded the time duration
futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
- if u32(atomic_load(f)) != expected {
+ if u32(atomic_load_explicit(f, .Acquire)) != expected {
return true
}
if duration <= 0 {
diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/primitives_atomic.odin
similarity index 69%
rename from core/sync/sync2/primitives_atomic.odin
rename to core/sync/primitives_atomic.odin
index a13b73a99..a0f08c412 100644
--- a/core/sync/sync2/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
new_state := curr_state // Make a copy of it
spin_lock: for spin in 0.. bool {
- _, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked)
+ _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
return ok
}
-
-// Example:
-//
-// if atomic_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_mutex_unlock)
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_lock(m)
@@ -193,25 +191,24 @@ atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
return false
}
-
-// Example:
-//
-// if atomic_rw_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_rw_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_rw_mutex_unlock)
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_lock(m)
return true
}
-// Example:
-//
-// if atomic_rw_mutex_shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_rw_mutex_shared_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_rw_mutex_shared_unlock)
atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_shared_lock(m)
@@ -270,13 +267,12 @@ atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
return true
}
-
-// Example:
-//
-// if atomic_recursive_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_recursive_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_recursive_mutex_unlock)
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_lock(m)
@@ -285,118 +281,39 @@ atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
-
-@(private="file")
-Queue_Item :: struct {
- next: ^Queue_Item,
- futex: Futex,
-}
-
-@(private="file")
-queue_item_wait :: proc(item: ^Queue_Item) {
- for atomic_load_acquire(&item.futex) == 0 {
- futex_wait(&item.futex, 0)
- cpu_relax()
- }
-}
-@(private="file")
-queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool {
- start := time.tick_now()
- for atomic_load_acquire(&item.futex) == 0 {
- remaining := duration - time.tick_since(start)
- if remaining < 0 {
- return false
- }
- if !futex_wait_with_timeout(&item.futex, 0, remaining) {
- return false
- }
- cpu_relax()
- }
- return true
-}
-@(private="file")
-queue_item_signal :: proc(item: ^Queue_Item) {
- atomic_store_release(&item.futex, 1)
- futex_signal(&item.futex)
-}
-
-
// Atomic_Cond implements a condition variable, a rendezvous point for threads
// waiting for signalling the occurence of an event
//
// An Atomic_Cond must not be copied after first use
Atomic_Cond :: struct {
- queue_mutex: Atomic_Mutex,
- queue_head: ^Queue_Item,
- pending: bool,
+ state: Futex,
}
atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
- waiter := &Queue_Item{}
+ state := u32(atomic_load_explicit(&c.state, .Relaxed))
+ unlock(m)
+ futex_wait(&c.state, state)
+ lock(m)
- atomic_mutex_lock(&c.queue_mutex)
- waiter.next = c.queue_head
- c.queue_head = waiter
-
- atomic_store(&c.pending, true)
- atomic_mutex_unlock(&c.queue_mutex)
-
- atomic_mutex_unlock(m)
- queue_item_wait(waiter)
- atomic_mutex_lock(m)
}
atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
- waiter := &Queue_Item{}
-
- atomic_mutex_lock(&c.queue_mutex)
- waiter.next = c.queue_head
- c.queue_head = waiter
-
- atomic_store(&c.pending, true)
- atomic_mutex_unlock(&c.queue_mutex)
-
- atomic_mutex_unlock(m)
- ok = queue_item_wait_with_timeout(waiter, duration)
- atomic_mutex_lock(m)
+ state := u32(atomic_load_explicit(&c.state, .Relaxed))
+ unlock(m)
+ ok = futex_wait_with_timeout(&c.state, state, duration)
+ lock(m)
return
}
atomic_cond_signal :: proc(c: ^Atomic_Cond) {
- if !atomic_load(&c.pending) {
- return
- }
-
- atomic_mutex_lock(&c.queue_mutex)
- waiter := c.queue_head
- if c.queue_head != nil {
- c.queue_head = c.queue_head.next
- }
- atomic_store(&c.pending, c.queue_head != nil)
- atomic_mutex_unlock(&c.queue_mutex)
-
- if waiter != nil {
- queue_item_signal(waiter)
- }
+ atomic_add_explicit(&c.state, 1, .Release)
+ futex_signal(&c.state)
}
atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
- if !atomic_load(&c.pending) {
- return
- }
-
- atomic_store(&c.pending, false)
-
- atomic_mutex_lock(&c.queue_mutex)
- waiters := c.queue_head
- c.queue_head = nil
- atomic_mutex_unlock(&c.queue_mutex)
-
- for waiters != nil {
- queue_item_signal(waiters)
- waiters = waiters.next
- }
+ atomic_add_explicit(&c.state, 1, .Release)
+ futex_broadcast(&c.state)
}
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
@@ -404,30 +321,28 @@ atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
//
// An Atomic_Sema must not be copied after first use
Atomic_Sema :: struct {
- mutex: Atomic_Mutex,
- cond: Atomic_Cond,
- count: int,
+ count: Futex,
}
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
- atomic_mutex_lock(&s.mutex)
- defer atomic_mutex_unlock(&s.mutex)
-
- s.count += count
- atomic_cond_signal(&s.cond)
+ atomic_add_explicit(&s.count, Futex(count), .Release)
+ if count == 1 {
+ futex_signal(&s.count)
+ } else {
+ futex_broadcast(&s.count)
+ }
}
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
- atomic_mutex_lock(&s.mutex)
- defer atomic_mutex_unlock(&s.mutex)
-
- for s.count == 0 {
- atomic_cond_wait(&s.cond, &s.mutex)
- }
-
- s.count -= 1
- if s.count > 0 {
- atomic_cond_signal(&s.cond)
+ for {
+ original_count := atomic_load_explicit(&s.count, .Relaxed)
+ for original_count == 0 {
+ futex_wait(&s.count, u32(original_count))
+ original_count = s.count
+ }
+ if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) {
+ return
+ }
}
}
@@ -435,25 +350,21 @@ atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration)
if duration <= 0 {
return false
}
- atomic_mutex_lock(&s.mutex)
- defer atomic_mutex_unlock(&s.mutex)
-
- start := time.tick_now()
+ for {
+ original_count := atomic_load_explicit(&s.count, .Relaxed)
+ for start := time.tick_now(); original_count == 0; /**/ {
+ remaining := duration - time.tick_since(start)
+ if remaining < 0 {
+ return false
+ }
- for s.count == 0 {
- remaining := duration - time.tick_since(start)
- if remaining < 0 {
- return false
+ if !futex_wait_with_timeout(&s.count, u32(original_count), remaining) {
+ return false
+ }
+ original_count = s.count
}
-
- if !atomic_cond_wait_with_timeout(&s.cond, &s.mutex, remaining) {
- return false
+ if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) {
+ return true
}
}
-
- s.count -= 1
- if s.count > 0 {
- atomic_cond_signal(&s.cond)
- }
- return true
}
diff --git a/core/sync/primitives_darwin.odin b/core/sync/primitives_darwin.odin
new file mode 100644
index 000000000..726113ae7
--- /dev/null
+++ b/core/sync/primitives_darwin.odin
@@ -0,0 +1,18 @@
+//+build darwin
+//+private
+package sync
+
+import "core:c"
+import "core:intrinsics"
+
+foreign import pthread "System.framework"
+
+_current_thread_id :: proc "contextless" () -> int {
+ tid: u64
+ // NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
+ // For older versions there is `syscall(SYS_thread_selfid)`, but not really
+ // the same thing apparently.
+ foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
+ pthread_threadid_np(nil, &tid)
+ return int(tid)
+}
diff --git a/core/sync/primitives_freebsd.odin b/core/sync/primitives_freebsd.odin
new file mode 100644
index 000000000..2d7cbf18d
--- /dev/null
+++ b/core/sync/primitives_freebsd.odin
@@ -0,0 +1,15 @@
+//+build freebsd
+//+private
+package sync
+
+import "core:c"
+
+foreign import dl "system:dl"
+
+foreign dl {
+ pthread_getthreadid_np :: proc "c" () -> c.int ---
+}
+
+_current_thread_id :: proc "contextless" () -> int {
+ return int(pthread_getthreadid_np())
+}
diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin
new file mode 100644
index 000000000..ba17c2eb5
--- /dev/null
+++ b/core/sync/primitives_internal.odin
@@ -0,0 +1,133 @@
+//+private
+package sync
+
+import "core:time"
+
+_Sema :: struct {
+ atomic: Atomic_Sema,
+}
+
+_sema_post :: proc(s: ^Sema, count := 1) {
+ atomic_sema_post(&s.impl.atomic, count)
+}
+
+_sema_wait :: proc(s: ^Sema) {
+ atomic_sema_wait(&s.impl.atomic)
+}
+
+_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
+ return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
+}
+
+
+_Recursive_Mutex :: struct {
+ owner: Futex,
+ recursion: i32,
+}
+
+_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+ tid := Futex(current_thread_id())
+ for {
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return
+ }
+
+ futex_wait(&m.impl.owner, u32(prev_owner))
+ }
+}
+
+_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+ m.impl.recursion -= 1
+ if m.impl.recursion != 0 {
+ return
+ }
+ atomic_exchange_explicit(&m.impl.owner, 0, .Release)
+
+ futex_signal(&m.impl.owner)
+ // outside the lock
+
+}
+
+_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+ tid := Futex(current_thread_id())
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return true
+ }
+ return false
+}
+
+
+when ODIN_OS != .Windows {
+ _Mutex :: struct {
+ mutex: Atomic_Mutex,
+ }
+
+ _mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+ }
+
+ _mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+ }
+
+ _mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+ }
+
+ _Cond :: struct {
+ cond: Atomic_Cond,
+ }
+
+ _cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+ }
+
+ _cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+ }
+
+ _cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+ }
+
+ _cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+ }
+
+
+ _RW_Mutex :: struct {
+ mutex: Atomic_RW_Mutex,
+ }
+
+ _rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
+ }
+}
\ No newline at end of file
diff --git a/core/sync/primitives_linux.odin b/core/sync/primitives_linux.odin
new file mode 100644
index 000000000..1e75891df
--- /dev/null
+++ b/core/sync/primitives_linux.odin
@@ -0,0 +1,9 @@
+//+build linux
+//+private
+package sync
+
+import "core:sys/unix"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return unix.sys_gettid()
+}
diff --git a/core/sync/primitives_openbsd.odin b/core/sync/primitives_openbsd.odin
new file mode 100644
index 000000000..4072a14e8
--- /dev/null
+++ b/core/sync/primitives_openbsd.odin
@@ -0,0 +1,9 @@
+//+build openbsd
+//+private
+package sync
+
+import "core:os"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return os.current_thread_id()
+}
diff --git a/core/sync/primitives_wasm.odin b/core/sync/primitives_wasm.odin
new file mode 100644
index 000000000..ac36404d9
--- /dev/null
+++ b/core/sync/primitives_wasm.odin
@@ -0,0 +1,8 @@
+//+private
+//+build wasm32, wasm64
+package sync
+
+_current_thread_id :: proc "contextless" () -> int {
+ // TODO(bill): _current_thread_id for wasm32
+ return 0
+}
\ No newline at end of file
diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/primitives_windows.odin
similarity index 99%
rename from core/sync/sync2/primitives_windows.odin
rename to core/sync/primitives_windows.odin
index ca7a5c9ee..055167892 100644
--- a/core/sync/sync2/primitives_windows.odin
+++ b/core/sync/primitives_windows.odin
@@ -1,6 +1,6 @@
//+build windows
//+private
-package sync2
+package sync
import "core:time"
import win32 "core:sys/windows"
diff --git a/core/sync/sync.odin b/core/sync/sync.odin
deleted file mode 100644
index 05c86a868..000000000
--- a/core/sync/sync.odin
+++ /dev/null
@@ -1,123 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-cpu_relax :: #force_inline proc "contextless" () {
- intrinsics.cpu_relax()
-}
-
-Condition_Mutex_Ptr :: union{^Mutex, ^Blocking_Mutex}
-
-
-Ticket_Mutex :: struct {
- ticket: u64,
- serving: u64,
-}
-
-ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
- atomic_store(&m.ticket, 0, .Relaxed)
- atomic_store(&m.serving, 0, .Relaxed)
-}
-
-ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add(&m.ticket, 1, .Relaxed)
- for ticket != atomic_load(&m.serving, .Acquire) {
- intrinsics.cpu_relax()
- }
-}
-
-ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add(&m.serving, 1, .Relaxed)
-}
-
-
-Benaphore :: struct {
- counter: int,
- sema: Semaphore,
-}
-
-benaphore_init :: proc(b: ^Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-benaphore_destroy :: proc(b: ^Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-benaphore_lock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- semaphore_wait_for(&b.sema)
- }
-}
-
-benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- return v == 0
-}
-
-benaphore_unlock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- semaphore_post(&b.sema)
- }
-}
-
-Recursive_Benaphore :: struct {
- counter: int,
- owner: int,
- recursion: int,
- sema: Semaphore,
-}
-
-recursive_benaphore_init :: proc(b: ^Recursive_Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-recursive_benaphore_destroy :: proc(b: ^Recursive_Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- if tid != b.owner {
- semaphore_wait_for(&b.sema)
- }
- }
- // inside the lock
- b.owner = tid
- b.recursion += 1
-}
-
-recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
- tid := current_thread_id()
- if b.owner == tid {
- intrinsics.atomic_add_acq(&b.counter, 1)
- } else {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- if v != 0 {
- return false
- }
- // inside the lock
- b.owner = tid
- }
- b.recursion += 1
- return true
-}
-
-recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- assert(tid == b.owner)
- b.recursion -= 1
- recursion := b.recursion
- if recursion == 0 {
- b.owner = 0
- }
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- if recursion == 0 {
- semaphore_post(&b.sema)
- }
- }
- // outside the lock
-}
diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin
deleted file mode 100644
index fe19f17c8..000000000
--- a/core/sync/sync2/atomic.odin
+++ /dev/null
@@ -1,79 +0,0 @@
-package sync2
-
-import "core:intrinsics"
-
-cpu_relax :: intrinsics.cpu_relax
-
-atomic_fence :: intrinsics.atomic_fence
-atomic_fence_acquire :: intrinsics.atomic_fence_acq
-atomic_fence_release :: intrinsics.atomic_fence_rel
-atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel
-
-atomic_store :: intrinsics.atomic_store
-atomic_store_release :: intrinsics.atomic_store_rel
-atomic_store_relaxed :: intrinsics.atomic_store_relaxed
-atomic_store_unordered :: intrinsics.atomic_store_unordered
-
-atomic_load :: intrinsics.atomic_load
-atomic_load_acquire :: intrinsics.atomic_load_acq
-atomic_load_relaxed :: intrinsics.atomic_load_relaxed
-atomic_load_unordered :: intrinsics.atomic_load_unordered
-
-atomic_add :: intrinsics.atomic_add
-atomic_add_acquire :: intrinsics.atomic_add_acq
-atomic_add_release :: intrinsics.atomic_add_rel
-atomic_add_acqrel :: intrinsics.atomic_add_acqrel
-atomic_add_relaxed :: intrinsics.atomic_add_relaxed
-atomic_sub :: intrinsics.atomic_sub
-atomic_sub_acquire :: intrinsics.atomic_sub_acq
-atomic_sub_release :: intrinsics.atomic_sub_rel
-atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel
-atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed
-atomic_and :: intrinsics.atomic_and
-atomic_and_acquire :: intrinsics.atomic_and_acq
-atomic_and_release :: intrinsics.atomic_and_rel
-atomic_and_acqrel :: intrinsics.atomic_and_acqrel
-atomic_and_relaxed :: intrinsics.atomic_and_relaxed
-atomic_nand :: intrinsics.atomic_nand
-atomic_nand_acquire :: intrinsics.atomic_nand_acq
-atomic_nand_release :: intrinsics.atomic_nand_rel
-atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel
-atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed
-atomic_or :: intrinsics.atomic_or
-atomic_or_acquire :: intrinsics.atomic_or_acq
-atomic_or_release :: intrinsics.atomic_or_rel
-atomic_or_acqrel :: intrinsics.atomic_or_acqrel
-atomic_or_relaxed :: intrinsics.atomic_or_relaxed
-atomic_xor :: intrinsics.atomic_xor
-atomic_xor_acquire :: intrinsics.atomic_xor_acq
-atomic_xor_release :: intrinsics.atomic_xor_rel
-atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel
-atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed
-
-atomic_exchange :: intrinsics.atomic_xchg
-atomic_exchange_acquire :: intrinsics.atomic_xchg_acq
-atomic_exchange_release :: intrinsics.atomic_xchg_rel
-atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel
-atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_strong :: intrinsics.atomic_cxchg
-atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq
-atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel
-atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel
-atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed
-atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed
-atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq
-atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed
-atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak
-atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq
-atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel
-atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel
-atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed
-atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed
-atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq
-atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed
-atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed
diff --git a/core/sync/sync2/primitives_darwin.odin b/core/sync/sync2/primitives_darwin.odin
deleted file mode 100644
index 66995bd01..000000000
--- a/core/sync/sync2/primitives_darwin.odin
+++ /dev/null
@@ -1,57 +0,0 @@
-//+build darwin
-//+private
-package sync2
-
-import "core:c"
-import "core:time"
-import "core:intrinsics"
-
-foreign import pthread "System.framework"
-
-_current_thread_id :: proc "contextless" () -> int {
- tid: u64
- // NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
- // For older versions there is `syscall(SYS_thread_selfid)`, but not really
- // the same thing apparently.
- foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
- pthread_threadid_np(nil, &tid)
- return int(tid)
-}
-
-
-
-_Mutex :: struct {
- mutex: Atomic_Mutex,
-}
-
-_mutex_lock :: proc(m: ^Mutex) {
- atomic_mutex_lock(&m.impl.mutex)
-}
-
-_mutex_unlock :: proc(m: ^Mutex) {
- atomic_mutex_unlock(&m.impl.mutex)
-}
-
-_mutex_try_lock :: proc(m: ^Mutex) -> bool {
- return atomic_mutex_try_lock(&m.impl.mutex)
-}
-
-_Cond :: struct {
- cond: Atomic_Cond,
-}
-
-_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
- atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
-}
-
-_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
- return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
-}
-
-_cond_signal :: proc(c: ^Cond) {
- atomic_cond_signal(&c.impl.cond)
-}
-
-_cond_broadcast :: proc(c: ^Cond) {
- atomic_cond_broadcast(&c.impl.cond)
-}
diff --git a/core/sync/sync2/primitives_internal.odin b/core/sync/sync2/primitives_internal.odin
deleted file mode 100644
index ae7e2599c..000000000
--- a/core/sync/sync2/primitives_internal.odin
+++ /dev/null
@@ -1,184 +0,0 @@
-//+private
-package sync2
-
-when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
- _Recursive_Mutex :: struct {
- owner: Futex,
- recursion: i32,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := Futex(current_thread_id())
- for {
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return
- }
-
- futex_wait(&m.impl.owner, u32(prev_owner))
- }
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- m.impl.recursion -= 1
- if m.impl.recursion != 0 {
- return
- }
- atomic_exchange_release(&m.impl.owner, 0)
-
- futex_signal(&m.impl.owner)
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := Futex(current_thread_id())
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return true
- }
- return false
- }
-} else {
- _Recursive_Mutex :: struct {
- owner: int,
- recursion: int,
- mutex: Mutex,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- if tid != m.impl.owner {
- mutex_lock(&m.impl.mutex)
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- assert(tid == m.impl.owner)
- m.impl.recursion -= 1
- recursion := m.impl.recursion
- if recursion == 0 {
- m.impl.owner = 0
- }
- if recursion == 0 {
- mutex_unlock(&m.impl.mutex)
- }
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := current_thread_id()
- if m.impl.owner == tid {
- return mutex_try_lock(&m.impl.mutex)
- }
- if !mutex_try_lock(&m.impl.mutex) {
- return false
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- return true
- }
-}
-
-
-when ODIN_OS != "windows" {
- RW_Mutex_State :: distinct uint
- RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2
- RW_Mutex_State_Is_Writing :: RW_Mutex_State(1)
- RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1
- RW_Mutex_State_Reader :: RW_Mutex_State(1)< bool {
- if mutex_try_lock(&rw.impl.mutex) {
- state := atomic_load(&rw.impl.state)
- if state & RW_Mutex_State_Reader_Mask == 0 {
- _ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing)
- return true
- }
-
- mutex_unlock(&rw.impl.mutex)
- }
- return false
- }
-
- _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
- state := atomic_load(&rw.impl.state)
- for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- ok: bool
- state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return
- }
- }
-
- mutex_lock(&rw.impl.mutex)
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- }
-
- _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
- state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader)
-
- if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
- (state & RW_Mutex_State_Is_Writing != 0) {
- sema_post(&rw.impl.sema)
- }
- }
-
- _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
- state := atomic_load(&rw.impl.state)
- if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return true
- }
- }
- if mutex_try_lock(&rw.impl.mutex) {
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- return true
- }
-
- return false
- }
-
-}
\ No newline at end of file
diff --git a/core/sync/sync2/primitives_linux.odin b/core/sync/sync2/primitives_linux.odin
deleted file mode 100644
index 4c81295bd..000000000
--- a/core/sync/sync2/primitives_linux.odin
+++ /dev/null
@@ -1,10 +0,0 @@
-//+build linux
-//+private
-package sync2
-
-import "core:intrinsics"
-
-_current_thread_id :: proc "contextless" () -> int {
- SYS_GETTID :: 186
- return int(intrinsics.syscall(SYS_GETTID))
-}
diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin
deleted file mode 100644
index 8d2c3986d..000000000
--- a/core/sync/sync2/primitives_pthreads.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-//+build linux, freebsd
-//+private
-package sync2
-
-import "core:time"
-import "core:sys/unix"
-
-_Mutex_State :: enum i32 {
- Unlocked = 0,
- Locked = 1,
- Waiting = 2,
-}
-_Mutex :: struct {
- pthread_mutex: unix.pthread_mutex_t,
-}
-
-_mutex_lock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_lock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_unlock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_try_lock :: proc(m: ^Mutex) -> bool {
- err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex)
- return err == 0
-}
-
-_Cond :: struct {
- pthread_cond: unix.pthread_cond_t,
-}
-
-_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
- err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-
-_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
- tv_sec := i64(duration/1e9)
- tv_nsec := i64(duration%1e9)
- err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, &{tv_sec, tv_nsec})
- return err == 0
-}
-
-
-_cond_signal :: proc(c: ^Cond) {
- err := unix.pthread_cond_signal(&c.impl.pthread_cond)
- assert(err == 0)
-}
-
-_cond_broadcast :: proc(c: ^Cond) {
- err := unix.pthread_cond_broadcast(&c.impl.pthread_cond)
- assert(err == 0)
-}
diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sync2/sema_internal.odin
deleted file mode 100644
index 64fc4ed96..000000000
--- a/core/sync/sync2/sema_internal.odin
+++ /dev/null
@@ -1,72 +0,0 @@
-package sync2
-
-import "core:time"
-
-
-when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
- _Sema :: struct {
- count: Futex,
- }
-
- _sema_post :: proc(s: ^Sema, count := 1) {
- atomic_add(&s.impl.count, Futex(count))
- if count == 1 {
- futex_signal(&s.impl.count)
- } else {
- futex_broadcast(&s.impl.count)
- }
- }
-
- _sema_wait :: proc(s: ^Sema) {
- for {
- original_count := atomic_load(&s.impl.count)
- for original_count == 0 {
- futex_wait(&s.impl.count, u32(original_count))
- original_count = s.impl.count
- }
- if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
- return
- }
- }
- }
-
- _sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
- if duration <= 0 {
- return false
- }
- for {
-
- original_count := atomic_load(&s.impl.count)
- for start := time.tick_now(); original_count == 0; /**/ {
- remaining := duration - time.tick_since(start)
- if remaining < 0 {
- return false
- }
-
- if !futex_wait_with_timeout(&s.impl.count, u32(original_count), remaining) {
- return false
- }
- original_count = s.impl.count
- }
- if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
- return true
- }
- }
- }
-} else {
- _Sema :: struct {
- wg: Wait_Group,
- }
-
- _sema_post :: proc(s: ^Sema, count := 1) {
- wait_group_add(&s.impl.wg, count)
- }
-
- _sema_wait :: proc(s: ^Sema) {
- wait_group_wait(&s.impl.wg)
- }
-
- _sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
- return wait_group_wait_with_timeout(&s.impl.wg, duration)
- }
-}
\ No newline at end of file
diff --git a/core/sync/sync_darwin.odin b/core/sync/sync_darwin.odin
deleted file mode 100644
index f3bb4d5a3..000000000
--- a/core/sync/sync_darwin.odin
+++ /dev/null
@@ -1,54 +0,0 @@
-package sync
-
-import "core:sys/darwin"
-
-import "core:c"
-
-foreign import pthread "System.framework"
-
-current_thread_id :: proc "contextless" () -> int {
- tid: u64
- // NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
- // For older versions there is `syscall(SYS_thread_selfid)`, but not really
- // the same thing apparently.
- foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
- pthread_threadid_np(nil, &tid)
- return int(tid)
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: darwin.semaphore_t,
-}
-// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
-// See core/sys/unix/pthread_linux.odin/pthread_t.
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count))
- assert(res == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_destroy(ct, s.handle)
- assert(res == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- SYS_GETTID :: 186;
- return int(intrinsics.syscall(SYS_GETTID));
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0);
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0);
- s.handle = {};
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- SYS_GETTID :: 186
- return int(intrinsics.syscall(SYS_GETTID))
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-Blocking_Mutex :: struct {
- handle: unix.pthread_mutex_t,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
- attrs: unix.pthread_mutexattr_t
- assert(unix.pthread_mutexattr_init(&attrs) == 0)
- defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error
-
- assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_destroy(&m.handle) == 0)
- m.handle = {}
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_lock(&m.handle) == 0)
-}
-
-// Returns false if someone else holds the lock.
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-
-// Blocks until signalled, and then lets past exactly
-// one thread.
-Condition :: struct {
- handle: unix.pthread_cond_t,
- mutex: Condition_Mutex_Ptr,
-
- // NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
- // This means that you may signal the condition before anyone is waiting to cause the
- // next thread that tries to wait to just pass by uninterrupted, without sleeping.
- // Without this, signalling a condition will only wake up a thread which is already waiting,
- // but not one that is about to wait, which can cause your program to become out of sync in
- // ways that are hard to debug or fix.
- flag: bool, // atomically mutated
-}
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
- attrs: unix.pthread_condattr_t
- if unix.pthread_condattr_init(&attrs) != 0 {
- return false
- }
- defer unix.pthread_condattr_destroy(&attrs) // ignores destruction error
-
- c.flag = false
- c.mutex = mutex
- return unix.pthread_cond_init(&c.handle, &attrs) == 0
-}
-
-condition_destroy :: proc(c: ^Condition) {
- assert(unix.pthread_cond_destroy(&c.handle) == 0)
- c.handle = {}
-}
-
-// Awaken exactly one thread who is waiting on the condition
-condition_signal :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- }
- return false
-}
-
-// Awaken all threads who are waiting on the condition
-condition_broadcast :: proc(c: ^Condition) -> bool {
- return unix.pthread_cond_broadcast(&c.handle) == 0
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
-
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-
-
-thread_yield :: proc() {
- unix.sched_yield()
-}
diff --git a/core/sync/sync2/sync_util.odin b/core/sync/sync_util.odin
similarity index 93%
rename from core/sync/sync2/sync_util.odin
rename to core/sync/sync_util.odin
index 853c3c685..4add9064d 100644
--- a/core/sync/sync2/sync_util.odin
+++ b/core/sync/sync_util.odin
@@ -1,12 +1,11 @@
-package sync2
+package sync
-
-// Example:
-//
-// if guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if guard(&m) {
+ ...
+ }
+*/
guard :: proc{
mutex_guard,
rw_mutex_guard,
@@ -17,13 +16,12 @@ guard :: proc{
atomic_recursive_mutex_guard,
atomic_rw_mutex_guard,
}
-
-// Example:
-//
-// if shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if shared_guard(&m) {
+ ...
+ }
+*/
shared_guard :: proc{
rw_mutex_shared_guard,
atomic_rw_mutex_shared_guard,
diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin
deleted file mode 100644
index 0a7cf71b2..000000000
--- a/core/sync/sync_windows.odin
+++ /dev/null
@@ -1,180 +0,0 @@
-// +build windows
-package sync
-
-import win32 "core:sys/windows"
-import "core:time"
-
-current_thread_id :: proc "contextless" () -> int {
- return int(win32.GetCurrentThreadId())
-}
-
-
-// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
-// Posting to the semaphore increases the count by one, or the provided amount.
-Semaphore :: struct {
- _handle: win32.HANDLE,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- win32.CloseHandle(s._handle)
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- win32.ReleaseSemaphore(s._handle, i32(count), nil)
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- // NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
- result := win32.WaitForSingleObject(s._handle, win32.INFINITE)
- assert(result != win32.WAIT_FAILED)
-}
-
-
-Mutex :: struct {
- _critical_section: win32.CRITICAL_SECTION,
-}
-
-
-mutex_init :: proc(m: ^Mutex, spin_count := 0) {
- win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count))
-}
-
-mutex_destroy :: proc(m: ^Mutex) {
- win32.DeleteCriticalSection(&m._critical_section)
-}
-
-mutex_lock :: proc(m: ^Mutex) {
- win32.EnterCriticalSection(&m._critical_section)
-}
-
-mutex_try_lock :: proc(m: ^Mutex) -> bool {
- return bool(win32.TryEnterCriticalSection(&m._critical_section))
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- win32.LeaveCriticalSection(&m._critical_section)
-}
-
-Blocking_Mutex :: struct {
- _handle: win32.SRWLOCK,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- win32.InitializeSRWLock(&m._handle)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- //
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- win32.AcquireSRWLockExclusive(&m._handle)
-}
-
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&m._handle))
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- win32.ReleaseSRWLockExclusive(&m._handle)
-}
-
-
-// Blocks until signalled.
-// When signalled, awakens exactly one waiting thread.
-Condition :: struct {
- _handle: win32.CONDITION_VARIABLE,
-
- mutex: Condition_Mutex_Ptr,
-}
-
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- assert(mutex != nil)
- win32.InitializeConditionVariable(&c._handle)
- c.mutex = mutex
- return true
-}
-
-condition_destroy :: proc(c: ^Condition) {
- //
-}
-
-condition_signal :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeConditionVariable(&c._handle)
- return true
-}
-
-condition_broadcast :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeAllConditionVariable(&c._handle)
- return true
-}
-
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0)
- }
- return false
-}
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000)
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0)
- }
- return false
-}
-
-
-
-
-RW_Lock :: struct {
- _handle: win32.SRWLOCK,
-}
-
-rw_lock_init :: proc(l: ^RW_Lock) {
- l._handle = win32.SRWLOCK_INIT
-}
-rw_lock_destroy :: proc(l: ^RW_Lock) {
- //
-}
-rw_lock_read :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockShared(&l._handle)
-}
-rw_lock_try_read :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockShared(&l._handle))
-}
-rw_lock_write :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockExclusive(&l._handle)
-}
-rw_lock_try_write :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&l._handle))
-}
-rw_lock_read_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockShared(&l._handle)
-}
-rw_lock_write_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockExclusive(&l._handle)
-}
-
-
-thread_yield :: proc() {
- win32.SwitchToThread()
-}
-
diff --git a/core/sync/wait_group.odin b/core/sync/wait_group.odin
deleted file mode 100644
index 63d882ed1..000000000
--- a/core/sync/wait_group.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-Wait_Group :: struct {
- counter: int,
- mutex: Blocking_Mutex,
- cond: Condition,
-}
-
-wait_group_init :: proc(wg: ^Wait_Group) {
- wg.counter = 0
- blocking_mutex_init(&wg.mutex)
- condition_init(&wg.cond, &wg.mutex)
-}
-
-
-wait_group_destroy :: proc(wg: ^Wait_Group) {
- condition_destroy(&wg.cond)
- blocking_mutex_destroy(&wg.mutex)
-}
-
-wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
- if delta == 0 {
- return
- }
-
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- intrinsics.atomic_add(&wg.counter, delta)
- if wg.counter < 0 {
- panic("sync.Wait_Group negative counter")
- }
- if wg.counter == 0 {
- condition_broadcast(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
-wait_group_done :: proc(wg: ^Wait_Group) {
- wait_group_add(wg, -1)
-}
-
-wait_group_wait :: proc(wg: ^Wait_Group) {
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- if wg.counter != 0 {
- condition_wait_for(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
diff --git a/core/sys/cpu/cpu.odin b/core/sys/cpu/cpu.odin
deleted file mode 100644
index b99fe01d8..000000000
--- a/core/sys/cpu/cpu.odin
+++ /dev/null
@@ -1,33 +0,0 @@
-package sys_cpu
-
-Cache_Line_Pad :: struct {_: [_cache_line_size]byte};
-
-initialized: bool;
-
-x86: struct {
- _: Cache_Line_Pad,
- has_aes: bool, // AES hardware implementation (AES NI)
- has_adx: bool, // Multi-precision add-carry instruction extensions
- has_avx: bool, // Advanced vector extension
- has_avx2: bool, // Advanced vector extension 2
- has_bmi1: bool, // Bit manipulation instruction set 1
- has_bmi2: bool, // Bit manipulation instruction set 2
- has_erms: bool, // Enhanced REP for MOVSB and STOSB
- has_fma: bool, // Fused-multiply-add instructions
- has_os_xsave: bool, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
- has_pclmulqdq: bool, // PCLMULQDQ instruction - most often used for AES-GCM
- has_popcnt: bool, // Hamming weight instruction POPCNT.
- has_rdrand: bool, // RDRAND instruction (on-chip random number generator)
- has_rdseed: bool, // RDSEED instruction (on-chip random number generator)
- has_sse2: bool, // Streaming SIMD extension 2 (always available on amd64)
- has_sse3: bool, // Streaming SIMD extension 3
- has_ssse3: bool, // Supplemental streaming SIMD extension 3
- has_sse41: bool, // Streaming SIMD extension 4 and 4.1
- has_sse42: bool, // Streaming SIMD extension 4 and 4.2
- _: Cache_Line_Pad,
-};
-
-
-init :: proc() {
- _init();
-}
diff --git a/core/sys/cpu/cpu_x86.odin b/core/sys/cpu/cpu_x86.odin
deleted file mode 100644
index 8f3560a87..000000000
--- a/core/sys/cpu/cpu_x86.odin
+++ /dev/null
@@ -1,67 +0,0 @@
-//+build 386, amd64
-package sys_cpu
-
-_cache_line_size :: 64;
-
-cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) {
- return expand_to_tuple(asm(u32, u32) -> struct{eax, ebc, ecx, edx: u32} {
- "cpuid",
- "={ax},={bx},={cx},={dx},{ax},{cx}",
- }(ax, cx));
-}
-
-xgetbv :: proc() -> (eax, edx: u32) {
- return expand_to_tuple(asm(u32) -> struct{eax, edx: u32} {
- "xgetbv",
- "={ax},={dx},{cx}",
- }(0));
-}
-
-_init :: proc() {
- is_set :: proc(hwc: u32, value: u32) -> bool {
- return hwc&value != 0;
- }
-
- initialized = true;
-
- max_id, _, _, _ := cpuid(0, 0);
-
- if max_id < 1 {
- return;
- }
-
- _, _, ecx1, edx1 := cpuid(1, 0);
-
- x86.has_sse2 = is_set(26, edx1);
-
- x86.has_sse3 = is_set(0, ecx1);
- x86.has_pclmulqdq = is_set(1, ecx1);
- x86.has_ssse3 = is_set(9, ecx1);
- x86.has_fma = is_set(12, ecx1);
- x86.has_sse41 = is_set(19, ecx1);
- x86.has_sse42 = is_set(20, ecx1);
- x86.has_popcnt = is_set(23, ecx1);
- x86.has_aes = is_set(25, ecx1);
- x86.has_os_xsave = is_set(27, ecx1);
- x86.has_rdrand = is_set(30, ecx1);
-
- os_supports_avx := false;
- if x86.has_os_xsave {
- eax, _ := xgetbv();
- os_supports_avx = is_set(1, eax) && is_set(2, eax);
- }
-
- x86.has_avx = is_set(28, ecx1) && os_supports_avx;
-
- if max_id < 7 {
- return;
- }
-
- _, ebx7, _, _ := cpuid(7, 0);
- x86.has_bmi1 = is_set(3, ebx7);
- x86.has_avx2 = is_set(5, ebx7) && os_supports_avx;
- x86.has_bmi2 = is_set(8, ebx7);
- x86.has_erms = is_set(9, ebx7);
- x86.has_rdseed = is_set(18, ebx7);
- x86.has_adx = is_set(19, ebx7);
-}
diff --git a/core/sys/darwin/xnu_system_call_helpers.odin b/core/sys/darwin/xnu_system_call_helpers.odin
new file mode 100644
index 000000000..94fe0bf47
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_helpers.odin
@@ -0,0 +1,168 @@
+package darwin
+
+import "core:strings"
+import "core:c"
+
+// this package uses the sys prefix for the proc names to indicate that these aren't native syscalls but directly call such
+sys_write_string :: proc (fd: c.int, message: string) -> bool {
+ return syscall_write(fd, strings.ptr_from_string(message), cast(u64)len(message))
+}
+
+Offset_From :: enum c.int {
+ SEEK_SET = 0, // the offset is set to offset bytes.
+ SEEK_CUR = 1, // the offset is set to its current location plus offset bytes.
+ SEEK_END = 2, // the offset is set to the size of the file plus offset bytes.
+ SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset.
+ SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
+}
+
+Open_Flags_Enum :: enum u8 {
+ RDONLY, /* open for reading only */
+ WRONLY, /* open for writing only */
+ RDWR, /* open for reading and writing */
+
+ NONBLOCK, /* no delay */
+ APPEND, /* set append mode */
+ CREAT, /* create if nonexistant */
+ TRUNC, /* truncate to zero length */
+ EXCL, /* error if already exists */
+ SHLOCK, /* open with shared file lock */
+ EXLOCK, /* open with exclusive file lock */
+ DIRECTORY, /* restrict open to only directories */
+ NOFOLLOW, /* don't follow symlinks */
+ SYMLINK, /* allow open of a symlink */
+ EVTONLY, /* descriptor requested for event notifications only */
+ CLOEXEC, /* causes the descriptor to be closed if you use any of the exec like functions */
+ NOFOLLOW_ANY, /* no symlinks allowed in path */
+}
+Open_Flags :: bit_set[Open_Flags_Enum; u16]
+
+Permission_Enum :: enum u8 {
+ /* For owner */
+ PERMISSION_OWNER_READ, /* R for owner */
+ PERMISSION_OWNER_WRITE, /* W for owner */
+ PERMISSION_OWNER_EXECUTE, /* X for owner */
+ //IRWXU, /* RWX mask for owner */
+
+ /* For group */
+ PERMISSION_GROUP_READ, /* R for group */
+ PERMISSION_GROUP_WRITE, /* W for group */
+ PERMISSION_GROUP_EXECUTE, /* X for group */
+ //IRWXG, /* RWX mask for group */
+
+ /* For other */
+ PERMISSION_OTHER_READ, /* R for other */
+ PERMISSION_OTHER_WRITE, /* W for other */
+ PERMISSION_OTHER_EXECUTE, /* X for other */
+ //IRWXO, /* RWX mask for other */
+
+ /* Special */
+ PERMISSION_SET_USER_ON_EXECUTION, /* set user id on execution */
+ PERMISSION_SET_GROUP_ON_EXECUTION, /* set group id on execution */
+
+ /* ?? */
+ PERMISSION_ISVTX, /* save swapped text even after use */
+}
+Permission :: bit_set[Permission_Enum; u16]
+
+PERMISSION_NONE_NONE :: Permission{}
+PERMISSION_OWNER_ALL :: Permission{.PERMISSION_OWNER_READ, .PERMISSION_OWNER_WRITE, .PERMISSION_OWNER_EXECUTE}
+PERMISSION_GROUP_ALL :: Permission{.PERMISSION_GROUP_READ, .PERMISSION_GROUP_WRITE, .PERMISSION_GROUP_EXECUTE}
+PERMISSION_OTHER_ALL :: Permission{.PERMISSION_OTHER_READ, .PERMISSION_OTHER_WRITE, .PERMISSION_OTHER_EXECUTE}
+PERMISSION_ALL_ALL :: PERMISSION_OWNER_ALL | PERMISSION_GROUP_ALL | PERMISSION_OTHER_ALL
+
+_sys_permission_mode :: #force_inline proc (mode: Permission) -> u32 {
+ cflags: u32 = 0
+
+ cflags |= PERMISSION_MASK_IRUSR * u32(Permission.PERMISSION_OWNER_READ in mode)
+ cflags |= PERMISSION_MASK_IWUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IRGRP * u32(Permission.PERMISSION_GROUP_READ in mode)
+ cflags |= PERMISSION_MASK_IWGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+ cflags |= PERMISSION_MASK_IROTH * u32(Permission.PERMISSION_OTHER_READ in mode)
+ cflags |= PERMISSION_MASK_IWOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+
+ return cflags
+}
+
+sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
+
+ cmode: u32 = 0
+ cflags: u32 = 0
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+
+ cflags = _sys_permission_mode(mode)
+
+ cmode |= OPEN_FLAG_RDONLY * u32(Open_Flags.RDONLY in oflag)
+ cmode |= OPEN_FLAG_WRONLY * u32(Open_Flags.WRONLY in oflag)
+ cmode |= OPEN_FLAG_RDWR * u32(Open_Flags.RDWR in oflag)
+ cmode |= OPEN_FLAG_NONBLOCK * u32(Open_Flags.NONBLOCK in oflag)
+ cmode |= OPEN_FLAG_CREAT * u32(Open_Flags.CREAT in oflag)
+ cmode |= OPEN_FLAG_APPEND * u32(Open_Flags.APPEND in oflag)
+ cmode |= OPEN_FLAG_TRUNC * u32(Open_Flags.TRUNC in oflag)
+ cmode |= OPEN_FLAG_EXCL * u32(Open_Flags.EXCL in oflag)
+ cmode |= OPEN_FLAG_SHLOCK * u32(Open_Flags.SHLOCK in oflag)
+ cmode |= OPEN_FLAG_EXLOCK * u32(Open_Flags.EXLOCK in oflag)
+ cmode |= OPEN_FLAG_DIRECTORY * u32(Open_Flags.DIRECTORY in oflag)
+ cmode |= OPEN_FLAG_NOFOLLOW * u32(Open_Flags.NOFOLLOW in oflag)
+ cmode |= OPEN_FLAG_SYMLINK * u32(Open_Flags.SYMLINK in oflag)
+ cmode |= OPEN_FLAG_EVTONLY * u32(Open_Flags.EVTONLY in oflag)
+ cmode |= OPEN_FLAG_CLOEXEC * u32(Open_Flags.CLOEXEC in oflag)
+ cmode |= OPEN_FLAG_NOFOLLOW_ANY * u32(Open_Flags.NOFOLLOW_ANY in oflag)
+
+ result := syscall_open(cpath, cmode, cflags)
+ state := result != -1
+
+ if state && cflags != 0 {
+ state = (syscall_fchmod(result, cflags) != -1)
+ }
+
+ return result * cast(c.int)state, state
+}
+
+sys_mkdir :: proc(path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cflags := _sys_permission_mode(mode)
+ return syscall_mkdir(cpath, cflags) != -1
+}
+
+sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cflags := _sys_permission_mode(mode)
+ return syscall_mkdir_at(fd, cpath, cflags) != -1
+}
+
+sys_rmdir :: proc(path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cflags := _sys_permission_mode(mode)
+ return syscall_rmdir(cpath, cflags) != -1
+}
+
+sys_rename :: proc(path: string, new_path: string) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
+ return syscall_rename(cpath, cnpath) != -1
+}
+
+sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
+ return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
+}
+
+sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
+ return syscall_lseek(fd, offset, cast(c.int)whence)
+}
+
+sys_chmod :: proc(path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cmode := _sys_permission_mode(mode)
+ return syscall_chmod(cpath, cmode) != -1
+}
+
+sys_lstat :: proc(path: string, status: ^stat) -> bool {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ return syscall_lstat(cpath, status) != -1
+}
diff --git a/core/sys/darwin/xnu_system_call_numbers.odin b/core/sys/darwin/xnu_system_call_numbers.odin
new file mode 100644
index 000000000..b90373fdc
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_numbers.odin
@@ -0,0 +1,558 @@
+package darwin
+
+unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
+ return uintptr(number) + uintptr(0x2000000)
+}
+
+System_Call_Number :: enum uintptr {
+ /* 0 syscall */
+ exit = 1,
+ fork = 2,
+ read = 3,
+ write = 4,
+ open = 5,
+ close = 6,
+ wait4 = 7,
+ /* 8 old creat */
+ link = 9,
+ unlink = 10,
+ /* 11 old execv */
+ chdir = 12,
+ fchdir = 13,
+ mknod = 14,
+ chmod = 15,
+ chown = 16,
+ /* 17 old break */
+ getfsstat = 18,
+ /* 19 old lseek */
+ getpid = 20,
+ /* 21 old mount */
+ /* 22 old umount */
+ setuid = 23,
+ getuid = 24,
+ geteuid = 25,
+ ptrace = 26,
+ recvmsg = 27,
+ sendmsg = 28,
+ recvfrom = 29,
+ accept = 30,
+ getpeername = 31,
+ getsockname = 32,
+ access = 33,
+ chflags = 34,
+ fchflags = 35,
+ sync = 36,
+ kill = 37,
+ /* 38 old stat */
+ getppid = 39,
+ /* 40 old lstat */
+ dup = 41,
+ pipe = 42,
+ getegid = 43,
+ /* 44 old profil */
+ /* 45 old ktrace */
+ sigaction = 46,
+ getgid = 47,
+ sigprocmask = 48,
+ getlogin = 49,
+ setlogin = 50,
+ acct = 51,
+ sigpending = 52,
+ sigaltstack = 53,
+ ioctl = 54,
+ reboot = 55,
+ revoke = 56,
+ symlink = 57,
+ readlink = 58,
+ execve = 59,
+ umask = 60,
+ chroot = 61,
+ /* 62 old fstat */
+ /* 63 used internally and reserved */
+ /* getpagesize = 64, invalid */
+ msync = 65,
+ vfork = 66,
+ /* 67 old vread */
+ /* 68 old vwrite */
+ /* 69 old sbrk */
+ /* 70 old sstk */
+ /* 71 old mmap */
+ /* 72 old vadvise */
+ munmap = 73,
+ mprotect = 74,
+ madvise = 75,
+ /* 76 old vhangup */
+ /* 77 old vlimit */
+ mincore = 78,
+ getgroups = 79,
+ setgroups = 80,
+ getpgrp = 81,
+ setpgid = 82,
+ setitimer = 83,
+ /* 84 old wait */
+ swapon = 85,
+ getitimer = 86,
+ /* 87 old gethostname */
+ /* 88 old sethostname */
+ getdtablesize = 89,
+ dup2 = 90,
+ /* 91 old getdopt */
+ fcntl = 92,
+ select = 93,
+ /* 94 old setdopt */
+ fsync = 95,
+ setpriority = 96,
+ socket = 97,
+ connect = 98,
+ /* 99 old accept */
+ getpriority = 100,
+ /* 101 old send */
+ /* 102 old recv */
+ /* 103 old sigreturn */
+ bind = 104,
+ setsockopt = 105,
+ listen = 106,
+ /* 107 old vtimes */
+ /* 108 old sigvec */
+ /* 109 old sigblock */
+ /* 110 old sigsetmask */
+ sigsuspend = 111,
+ /* 112 old sigstack */
+ /* 113 old recvmsg */
+ /* 114 old sendmsg */
+ /* 115 old vtrace */
+ gettimeofday = 116,
+ getrusage = 117,
+ getsockopt = 118,
+ /* 119 old resuba */
+ readv = 120,
+ writev = 121,
+ settimeofday = 122,
+ fchown = 123,
+ fchmod = 124,
+ /* 125 old recvfrom */
+ setreuid = 126,
+ setregid = 127,
+ rename = 128,
+ /* 129 old truncate */
+ /* 130 old ftruncate */
+ flock = 131,
+ mkfifo = 132,
+ sendto = 133,
+ shutdown = 134,
+ socketpair = 135,
+ mkdir = 136,
+ rmdir = 137,
+ utimes = 138,
+ futimes = 139,
+ adjtime = 140,
+ /* 141 old getpeername */
+ gethostuuid = 142,
+ /* 143 old sethostid */
+ /* 144 old getrlimit */
+ /* 145 old setrlimit */
+ /* 146 old killpg */
+ setsid = 147,
+ /* 148 old setquota */
+ /* 149 old qquota */
+ /* 150 old getsockname */
+ getpgid = 151,
+ setprivexec = 152,
+ pread = 153,
+ pwrite = 154,
+ nfssvc = 155,
+ /* 156 old getdirentries */
+ statfs = 157,
+ fstatfs = 158,
+ unmount = 159,
+ /* 160 old async_daemon */
+ getfh = 161,
+ /* 162 old getdomainname */
+ /* 163 old setdomainname */
+ /* 164 */
+ quotactl = 165,
+ /* 166 old exportfs */
+ mount = 167,
+ /* 168 old ustat */
+ csops = 169,
+ csops_audittoken = 170,
+ /* 171 old wait3 */
+ /* 172 old rpause */
+ waitid = 173,
+ /* 174 old getdents */
+ /* 175 old gc_control */
+ /* 176 old add_profil */
+ kdebug_typefilter = 177,
+ kdebug_trace_string = 178,
+ kdebug_trace64 = 179,
+ kdebug_trace = 180,
+ setgid = 181,
+ setegid = 182,
+ seteuid = 183,
+ sigreturn = 184,
+ /* 185 old chud */
+ thread_selfcounts = 186,
+ fdatasync = 187,
+ stat = 188,
+ fstat = 189,
+ lstat = 190,
+ pathconf = 191,
+ fpathconf = 192,
+ /* 193 old getfsstat */
+ getrlimit = 194,
+ setrlimit = 195,
+ getdirentries = 196,
+ mmap = 197,
+ /* 198 old __syscall */
+ lseek = 199,
+ truncate = 200,
+ ftruncate = 201,
+ sysctl = 202,
+ mlock = 203,
+ munlock = 204,
+ undelete = 205,
+ /* 206 old ATsocket */
+ /* 207 old ATgetmsg */
+ /* 208 old ATputmsg */
+ /* 209 old ATsndreq */
+ /* 210 old ATsndrsp */
+ /* 211 old ATgetreq */
+ /* 212 old ATgetrsp */
+ /* 213 Reserved for AppleTalk */
+ /* 214 */
+ /* 215 */
+ open_dprotected_np = 216,
+ fsgetpath_ext = 217,
+ /* 218 old lstatv */
+ /* 219 old fstatv */
+ getattrlist = 220,
+ setattrlist = 221,
+ getdirentriesattr = 222,
+ exchangedata = 223,
+ /* 224 old checkuseraccess or fsgetpath */
+ searchfs = 225,
+ delete = 226,
+ copyfile = 227,
+ fgetattrlist = 228,
+ fsetattrlist = 229,
+ poll = 230,
+ /* 231 old watchevent */
+ /* 232 old waitevent */
+ /* 233 old modwatch */
+ getxattr = 234,
+ fgetxattr = 235,
+ setxattr = 236,
+ fsetxattr = 237,
+ removexattr = 238,
+ fremovexattr = 239,
+ listxattr = 240,
+ flistxattr = 241,
+ fsctl = 242,
+ initgroups = 243,
+ posix_spawn = 244,
+ ffsctl = 245,
+ /* 246 */
+ nfsclnt = 247,
+ fhopen = 248,
+ /* 249 */
+ minherit = 250,
+ semsys = 251,
+ msgsys = 252,
+ shmsys = 253,
+ semctl = 254,
+ semget = 255,
+ semop = 256,
+ /* 257 old semconfig */
+ msgctl = 258,
+ msgget = 259,
+ msgsnd = 260,
+ msgrcv = 261,
+ shmat = 262,
+ shmctl = 263,
+ shmdt = 264,
+ shmget = 265,
+ shm_open = 266,
+ shm_unlink = 267,
+ sem_open = 268,
+ sem_close = 269,
+ sem_unlink = 270,
+ sem_wait = 271,
+ sem_trywait = 272,
+ sem_post = 273,
+ sysctlbyname = 274,
+ /* 275 old sem_init */
+ /* 276 old sem_destroy */
+ open_extended = 277,
+ umask_extended = 278,
+ stat_extended = 279,
+ lstat_extended = 280,
+ fstat_extended = 281,
+ chmod_extended = 282,
+ fchmod_extended = 283,
+ access_extended = 284,
+ settid = 285,
+ gettid = 286,
+ setsgroups = 287,
+ getsgroups = 288,
+ setwgroups = 289,
+ getwgroups = 290,
+ mkfifo_extended = 291,
+ mkdir_extended = 292,
+ identitysvc = 293,
+ shared_region_check_np = 294,
+ /* 295 old shared_region_map_np */
+ vm_pressure_monitor = 296,
+ psynch_rw_longrdlock = 297,
+ psynch_rw_yieldwrlock = 298,
+ psynch_rw_downgrade = 299,
+ psynch_rw_upgrade = 300,
+ psynch_mutexwait = 301,
+ psynch_mutexdrop = 302,
+ psynch_cvbroad = 303,
+ psynch_cvsignal = 304,
+ psynch_cvwait = 305,
+ psynch_rw_rdlock = 306,
+ psynch_rw_wrlock = 307,
+ psynch_rw_unlock = 308,
+ psynch_rw_unlock2 = 309,
+ getsid = 310,
+ settid_with_pid = 311,
+ psynch_cvclrprepost = 312,
+ aio_fsync = 313,
+ aio_return = 314,
+ aio_suspend = 315,
+ aio_cancel = 316,
+ aio_error = 317,
+ aio_read = 318,
+ aio_write = 319,
+ lio_listio = 320,
+ /* 321 old __pthread_cond_wait */
+ iopolicysys = 322,
+ process_policy = 323,
+ mlockall = 324,
+ munlockall = 325,
+ /* 326 */
+ issetugid = 327,
+ __pthread_kill = 328,
+ __pthread_sigmask = 329,
+ __sigwait = 330,
+ __disable_threadsignal = 331,
+ __pthread_markcancel = 332,
+ __pthread_canceled = 333,
+ __semwait_signal = 334,
+ /* 335 old utrace */
+ proc_info = 336,
+ sendfile = 337,
+ stat64 = 338,
+ fstat64 = 339,
+ lstat64 = 340,
+ stat64_extended = 341,
+ lstat64_extended = 342,
+ fstat64_extended = 343,
+ getdirentries64 = 344,
+ statfs64 = 345,
+ fstatfs64 = 346,
+ getfsstat64 = 347,
+ __pthread_chdir = 348,
+ __pthread_fchdir = 349,
+ audit = 350,
+ auditon = 351,
+ /* 352 */
+ getauid = 353,
+ setauid = 354,
+ /* 355 old getaudit */
+ /* 356 old setaudit */
+ getaudit_addr = 357,
+ setaudit_addr = 358,
+ auditctl = 359,
+ bsdthread_create = 360,
+ bsdthread_terminate = 361,
+ kqueue = 362,
+ kevent = 363,
+ lchown = 364,
+ /* 365 old stack_snapshot */
+ bsdthread_register = 366,
+ workq_open = 367,
+ workq_kernreturn = 368,
+ kevent64 = 369,
+ __old_semwait_signal = 370,
+ __old_semwait_signal_nocancel = 371,
+ thread_selfid = 372,
+ ledger = 373,
+ kevent_qos = 374,
+ kevent_id = 375,
+ /* 376 */
+ /* 377 */
+ /* 378 */
+ /* 379 */
+ __mac_execve = 380,
+ __mac_syscall = 381,
+ __mac_get_file = 382,
+ __mac_set_file = 383,
+ __mac_get_link = 384,
+ __mac_set_link = 385,
+ __mac_get_proc = 386,
+ __mac_set_proc = 387,
+ __mac_get_fd = 388,
+ __mac_set_fd = 389,
+ __mac_get_pid = 390,
+ /* 391 */
+ /* 392 */
+ /* 393 */
+ pselect = 394,
+ pselect_nocancel = 395,
+ read_nocancel = 396,
+ write_nocancel = 397,
+ open_nocancel = 398,
+ close_nocancel = 399,
+ wait4_nocancel = 400,
+ recvmsg_nocancel = 401,
+ sendmsg_nocancel = 402,
+ recvfrom_nocancel = 403,
+ accept_nocancel = 404,
+ msync_nocancel = 405,
+ fcntl_nocancel = 406,
+ select_nocancel = 407,
+ fsync_nocancel = 408,
+ connect_nocancel = 409,
+ sigsuspend_nocancel = 410,
+ readv_nocancel = 411,
+ writev_nocancel = 412,
+ sendto_nocancel = 413,
+ pread_nocancel = 414,
+ pwrite_nocancel = 415,
+ waitid_nocancel = 416,
+ poll_nocancel = 417,
+ msgsnd_nocancel = 418,
+ msgrcv_nocancel = 419,
+ sem_wait_nocancel = 420,
+ aio_suspend_nocancel = 421,
+ __sigwait_nocancel = 422,
+ __semwait_signal_nocancel = 423,
+ __mac_mount = 424,
+ __mac_get_mount = 425,
+ __mac_getfsstat = 426,
+ fsgetpath = 427,
+ audit_session_self = 428,
+ audit_session_join = 429,
+ fileport_makeport = 430,
+ fileport_makefd = 431,
+ audit_session_port = 432,
+ pid_suspend = 433,
+ pid_resume = 434,
+ pid_hibernate = 435,
+ pid_shutdown_sockets = 436,
+ /* 437 old shared_region_slide_np */
+ shared_region_map_and_slide_np = 438,
+ kas_info = 439,
+ memorystatus_control = 440,
+ guarded_open_np = 441,
+ guarded_close_np = 442,
+ guarded_kqueue_np = 443,
+ change_fdguard_np = 444,
+ usrctl = 445,
+ proc_rlimit_control = 446,
+ connectx = 447,
+ disconnectx = 448,
+ peeloff = 449,
+ socket_delegate = 450,
+ telemetry = 451,
+ proc_uuid_policy = 452,
+ memorystatus_get_level = 453,
+ system_override = 454,
+ vfs_purge = 455,
+ sfi_ctl = 456,
+ sfi_pidctl = 457,
+ coalition = 458,
+ coalition_info = 459,
+ necp_match_policy = 460,
+ getattrlistbulk = 461,
+ clonefileat = 462,
+ openat = 463,
+ openat_nocancel = 464,
+ renameat = 465,
+ faccessat = 466,
+ fchmodat = 467,
+ fchownat = 468,
+ fstatat = 469,
+ fstatat64 = 470,
+ linkat = 471,
+ unlinkat = 472,
+ readlinkat = 473,
+ symlinkat = 474,
+ mkdirat = 475,
+ getattrlistat = 476,
+ proc_trace_log = 477,
+ bsdthread_ctl = 478,
+ openbyid_np = 479,
+ recvmsg_x = 480,
+ sendmsg_x = 481,
+ thread_selfusage = 482,
+ csrctl = 483,
+ guarded_open_dprotected_np = 484,
+ guarded_write_np = 485,
+ guarded_pwrite_np = 486,
+ guarded_writev_np = 487,
+ renameatx_np = 488,
+ mremap_encrypted = 489,
+ netagent_trigger = 490,
+ stack_snapshot_with_config = 491,
+ microstackshot = 492,
+ grab_pgo_data = 493,
+ persona = 494,
+ /* 495 */
+ mach_eventlink_signal = 496,
+ mach_eventlink_wait_until = 497,
+ mach_eventlink_signal_wait_until = 498,
+ work_interval_ctl = 499,
+ getentropy = 500,
+ necp_open = 501,
+ necp_client_action = 502,
+ nexus_open = 503, // for those who are intressted http://newosxbook.com/bonus/vol1ch16.html
+ nexus_register = 504,
+ nexus_deregister = 505,
+ nexus_create = 506,
+ nexus_destroy = 507,
+ nexus_get_opt = 508,
+ nexus_set_opt = 509,
+ channel_open = 510,
+ channel_get_info = 511,
+ channel_sync = 512,
+ channel_get_opt = 513,
+ channel_set_opt = 514,
+ ulock_wait = 515,
+ ulock_wake = 516,
+ fclonefileat = 517,
+ fs_snapshot = 518,
+ register_uexc_handler = 519,
+ terminate_with_payload = 520,
+ abort_with_payload = 521,
+ necp_session_open = 522,
+ necp_session_action = 523,
+ setattrlistat = 524,
+ net_qos_guideline = 525,
+ fmount = 526,
+ ntp_adjtime = 527,
+ ntp_gettime = 528,
+ os_fault_with_payload = 529,
+ kqueue_workloop_ctl = 530,
+ mach_bridge_remote_time = 531,
+ coalition_ledger = 532,
+ log_data = 533,
+ memorystatus_available_memory = 534,
+ objc_bp_assist_cfg_np = 535,
+ shared_region_map_and_slide_2_np = 536,
+ pivot_root = 537,
+ task_inspect_for_pid = 538,
+ task_read_for_pid = 539,
+ preadv = 540,
+ pwritev = 541,
+ preadv_nocancel = 542,
+ pwritev_nocancel = 543,
+ ulock_wait2 = 544,
+ proc_info_extended_id = 545,
+ tracker_action = 546,
+ debug_syscall_reject = 547,
+ MAXSYSCALL = 548,
+ /* invalid = 63, */
+}
diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin
new file mode 100644
index 000000000..685f75ffa
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_wrappers.odin
@@ -0,0 +1,419 @@
+package darwin
+
+import "core:c"
+import "core:intrinsics"
+
+/* flock */
+LOCK_SH :: 1 /* shared lock */
+LOCK_EX :: 2 /* exclusive lock */
+LOCK_NB :: 4 /* don't block when locking */
+LOCK_UN :: 8 /* unlock */
+
+/* sys/unistd.h for access */
+F_OK :: c.int(0) /* test for existence of file */
+X_OK :: c.int((1 << 0)) /* test for execute or search permission */
+W_OK :: c.int((1 << 1)) /* test for write permission */
+R_OK :: c.int((1 << 2)) /* test for read permission */
+
+/* copyfile flags */
+COPYFILE_ACL :: (1 << 0)
+COPYFILE_STAT :: (1 << 1)
+COPYFILE_XATTR :: (1 << 2)
+COPYFILE_DATA :: (1 << 3)
+
+COPYFILE_SECURITY :: (COPYFILE_STAT | COPYFILE_ACL)
+COPYFILE_METADATA :: (COPYFILE_SECURITY | COPYFILE_XATTR)
+COPYFILE_ALL :: (COPYFILE_METADATA | COPYFILE_DATA)
+
+/* syslimits.h */
+PATH_MAX :: 1024 /* max bytes in pathname */
+
+/* param.h */
+MAXPATHLEN :: PATH_MAX
+
+/* proc_info.h */
+DARWIN_PROC_PIDPATHINFO_SIZE :: MAXPATHLEN
+DARWIN_PROC_PIDPATHINFO :: 11
+
+DARWIN_PROC_ALL_PIDS :: c.int(1)
+DARWIN_PROC_PGRP_ONLY :: c.int(2)
+DARWIN_PROC_TTY_ONLY :: c.int(3)
+DARWIN_PROC_UID_ONLY :: c.int(4)
+DARWIN_PROC_RUID_ONLY :: c.int(5)
+DARWIN_PROC_PPID_ONLY :: c.int(6)
+DARWIN_PROC_KDBG_ONLY :: c.int(7)
+
+DARWIN_PROC_INFO_CALL_LISTPIDS :: c.int(0x1)
+DARWIN_PROC_INFO_CALL_PIDINFO :: c.int(0x2)
+DARWIN_PROC_INFO_CALL_PIDFDINFO :: c.int(0x3)
+DARWIN_PROC_INFO_CALL_KERNMSGBUF :: c.int(0x4)
+DARWIN_PROC_INFO_CALL_SETCONTROL :: c.int(0x5)
+DARWIN_PROC_INFO_CALL_PIDFILEPORTINFO :: c.int(0x6)
+DARWIN_PROC_INFO_CALL_TERMINATE :: c.int(0x7)
+DARWIN_PROC_INFO_CALL_DIRTYCONTROL :: c.int(0x8)
+DARWIN_PROC_INFO_CALL_PIDRUSAGE :: c.int(0x9)
+DARWIN_PROC_INFO_CALL_PIDORIGINATORINFO :: c.int(0xa)
+DARWIN_PROC_INFO_CALL_LISTCOALITIONS :: c.int(0xb)
+DARWIN_PROC_INFO_CALL_CANUSEFGHW :: c.int(0xc)
+DARWIN_PROC_INFO_CALL_PIDDYNKQUEUEINFO :: c.int(0xd)
+DARWIN_PROC_INFO_CALL_UDATA_INFO :: c.int(0xe)
+
+/* mmap flags */
+MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
+MAP_FILE :: 0x0000 /* map from file (default) */
+MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
+MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
+MAP_PRIVATE :: 0x0002 /* [MF|SHM] changes are private */
+MAP_SHARED :: 0x0001 /* [MF|SHM] share changes */
+MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
+MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
+MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
+
+/* mmap prot flags */
+PROT_NONE :: 0x00 /* [MC2] no permissions */
+PROT_READ :: 0x01 /* [MC2] pages can be read */
+PROT_WRITE :: 0x02 /* [MC2] pages can be written */
+PROT_EXEC :: 0x04 /* [MC2] pages can be executed */
+
+/* For owner Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXU :: 0o000700 /* RWX mask for owner */
+PERMISSION_MASK_IRUSR :: 0o000400 /* R for owner */
+PERMISSION_MASK_IWUSR :: 0o000200 /* W for owner */
+PERMISSION_MASK_IXUSR :: 0o000100 /* X for owner */
+
+/* For group Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXG :: 0o000070 /* RWX mask for group */
+PERMISSION_MASK_IRGRP :: 0o000040 /* R for group */
+PERMISSION_MASK_IWGRP :: 0o000020 /* W for group */
+PERMISSION_MASK_IXGRP :: 0o000010 /* X for group */
+
+/* For other Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXO :: 0o000007 /* RWX mask for other */
+PERMISSION_MASK_IROTH :: 0o000004 /* R for other */
+PERMISSION_MASK_IWOTH :: 0o000002 /* W for other */
+PERMISSION_MASK_IXOTH :: 0o000001 /* X for other */
+
+/* Special Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_ISUID :: 0o004000 /* set user id on execution */
+PERMISSION_MASK_ISGID :: 0o002000 /* set group id on execution */
+PERMISSION_MASK_ISVTX :: 0o001000 /* save swapped text even after use */
+
+OPEN_FLAG_RDONLY :: 0x0000 /* open for reading only */
+OPEN_FLAG_WRONLY :: 0x0001 /* open for writing only */
+OPEN_FLAG_RDWR :: 0x0002 /* open for reading and writing */
+
+/* mask for above rd/wr/rdwr flags */
+MASK_ACCMODE :: 0x0003
+
+OPEN_FLAG_NONBLOCK :: 0x00000004 /* no delay */
+OPEN_FLAG_APPEND :: 0x00000008 /* set append mode */
+OPEN_FLAG_CREAT :: 0x00000200 /* create if nonexistant */
+OPEN_FLAG_TRUNC :: 0x00000400 /* truncate to zero length */
+OPEN_FLAG_EXCL :: 0x00000800 /* error if already exists */
+OPEN_FLAG_SHLOCK :: 0x00000010 /* open with shared file lock */
+OPEN_FLAG_EXLOCK :: 0x00000020 /* open with exclusive file lock */
+OPEN_FLAG_DIRECTORY :: 0x00100000 /* restrict open to only directories */
+OPEN_FLAG_NOFOLLOW :: 0x00000100 /* don't follow symlinks */
+OPEN_FLAG_SYMLINK :: 0x00200000 /* allow open of a symlink */
+OPEN_FLAG_EVTONLY :: 0x00008000 /* descriptor requested for event notifications only */
+OPEN_FLAG_CLOEXEC :: 0x01000000 /* causes the descriptor to be closed if you use any of the exec like functions */
+OPEN_FLAG_NOFOLLOW_ANY :: 0x20000000 /* no symlinks allowed in path */
+
+/* bsd/sys/param.h */
+DARWIN_MAXCOMLEN :: 16
+
+/*--==========================================================================--*/
+
+__darwin_ino64_t :: u64
+__darwin_time_t :: u32
+__darwin_dev_t :: i32
+__darwin_mode_t :: u16
+__darwin_off_t :: i64
+__darwin_blkcnt_t :: i64
+__darwin_blksize_t :: i32
+__darwin_pid_t :: i32
+__darwin_suseconds_t :: i32
+
+time_t :: __darwin_time_t
+dev_t :: __darwin_dev_t
+mode_t :: u16
+nlink_t :: u16
+uid_t :: u16
+gid_t :: u16
+off_t :: __darwin_off_t
+blkcnt_t :: __darwin_blkcnt_t
+blksize_t :: __darwin_blksize_t
+pid_t :: __darwin_pid_t
+
+stat :: __DARWIN_STRUCT_STAT64
+timeval :: _STRUCT_TIMEVAL
+
+/*--==========================================================================--*/
+
+/* sys/stat.h */
+__DARWIN_STRUCT_STAT64 :: struct {
+ st_dev: dev_t, /* [XSI] ID of device containing file */
+ st_mode: mode_t, /* [XSI] Mode of file (see below) */
+ st_nlink: nlink_t, /* [XSI] Number of hard links */
+ st_ino: __darwin_ino64_t, /* [XSI] File serial number */
+ st_uid_t: uid_t, /* [XSI] User ID of the file */
+ st_gid_t: gid_t, /* [XSI] Group ID of the file */
+ st_rdev: dev_t, /* [XSI] Device ID */
+
+ // __DARWIN_STRUCT_STAT64_TIMES
+ st_atime: time_t, /* [XSI] Time of last access */
+ st_atimensec: i32, /* nsec of last access */
+ st_mtime: time_t, /* [XSI] Last data modification time */
+ st_mtimensec: i32, /* last data modification nsec */
+ st_ctime: time_t, /* [XSI] Time of last status change */
+ st_ctimensec: u32, /* nsec of last status change */
+ st_birthtime: time_t, /* File creation time(birth) */
+ st_birthtimensec: i32, /* nsec of File creation time */
+ // end __DARWIN_STRUCT_STAT64_TIMES
+
+ st_size: off_t, /* [XSI] file size, in bytes */
+ st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
+ st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
+ st_flags: u32, /* user defined flags for file */
+ st_gen: u32, /* file generation number */
+ st_lspare: i32, /* RESERVED: DO NOT USE! */
+ st_qspare: [2]i64, /* RESERVED: DO NOT USE! */
+}
+
+/* sys/_types/_timeval.h */
+_STRUCT_TIMEVAL :: struct {
+ tv_sec: __darwin_time_t, /* seconds */
+ tv_usec: __darwin_suseconds_t, /* microseconds */
+}
+
+/* pwd.h */
+_Password_Entry :: struct {
+ pw_name: cstring, /* username */
+ pw_passwd: cstring, /* user password */
+ pw_uid: i32, /* user ID */
+ pw_gid: i32, /* group ID */
+ pw_change: u64, /* password change time */
+ pw_class: cstring, /* user access class */
+ pw_gecos: cstring, /* full user name */
+ pw_dir: cstring, /* home directory */
+ pw_shell: cstring, /* shell program */
+ pw_expire: u64, /* account expiration */
+ pw_fields: i32, /* filled fields */
+}
+
+/* processinfo.h */
+_Proc_Bsdinfo :: struct {
+ pbi_flags: u32, /* if is 64bit; emulated etc */
+ pbi_status: u32,
+ pbi_xstatus: u32,
+ pbi_pid: u32,
+ pbi_ppid: u32,
+ pbi_uid: u32,
+ pbi_gid: u32,
+ pbi_ruid: u32,
+ pbi_rgid: u32,
+ pbi_svuid: u32,
+ pbi_svgid: u32,
+ res: u32,
+ pbi_comm: [DARWIN_MAXCOMLEN]u8,
+ pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */
+ pbi_nfiles: u32,
+ pbi_pgid: u32,
+ pbi_pjobc: u32,
+ e_tdev: u32, /* controlling tty dev */
+ e_tpgid: u32, /* tty process group id */
+ pbi_nice: i32,
+ pbi_start_tvsec: u64,
+ pbi_start_tvusec: u64,
+}
+
+/*--==========================================================================--*/
+
+syscall_fsync :: #force_inline proc(fildes: c.int) -> bool {
+ return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes)))
+}
+
+syscall_write :: #force_inline proc (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
+ return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.write), uintptr(fildes), uintptr(buf), uintptr(nbyte)))
+}
+
+syscall_read :: #force_inline proc(fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
+ return cast(i64)intrinsics.syscall(unix_offset_syscall(.read), uintptr(fildes), uintptr(buf), uintptr(nbyte))
+}
+
+syscall_open :: #force_inline proc(path: cstring, oflag: u32, mode: u32) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.open), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
+}
+
+syscall_close :: #force_inline proc(fd: c.int) -> bool {
+ return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd)))
+}
+
+syscall_fchmod :: #force_inline proc(fildes: c.int, mode: u32) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.fchmod), uintptr(fildes), uintptr(mode)))
+}
+
+syscall_chmod :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.chmod), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_mkdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_mkdir_at :: #force_inline proc(fd: c.int, path: cstring, mode: u32) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), uintptr(fd), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_rmdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rmdir), transmute(uintptr)path, uintptr(mode)))
+}
+
+syscall_rename :: #force_inline proc(path_old: cstring, path_new: cstring) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rename), transmute(uintptr)path_old, transmute(uintptr)path_new))
+}
+
+syscall_rename_at :: #force_inline proc(from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
+ return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.renameat), uintptr(from_fd), transmute(uintptr)from, uintptr(to_fd), transmute(uintptr)to))
+}
+
+syscall_lseek :: #force_inline proc(fd: c.int, offset: i64, whence: c.int) -> i64 {
+ return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
+}
+
+syscall_gettid :: #force_inline proc() -> u64 {
+ return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
+}
+
+syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstat), uintptr(fd), uintptr(status))
+}
+
+syscall_lstat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.lstat), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_stat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.stat), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_fstatat :: #force_inline proc(fd: c.int, path: cstring, status: ^stat) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstatat), uintptr(fd), transmute(uintptr)path, uintptr(status))
+}
+
+syscall_link :: #force_inline proc(path: cstring, to_link: cstring) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.link), transmute(uintptr)path, transmute(uintptr)to_link)
+}
+
+syscall_linkat :: #force_inline proc(fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.linkat), uintptr(fd), transmute(uintptr)path, uintptr(fd2), transmute(uintptr)to_link)
+}
+
+syscall_readlink :: #force_inline proc(path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+ return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlink), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
+}
+
+syscall_readlinkat :: #force_inline proc(fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+ return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlinkat), uintptr(fd), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
+}
+
+syscall_access :: #force_inline proc(path: cstring, mode: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.access), transmute(uintptr)path, uintptr(mode))
+}
+
+syscall_faccessat :: #force_inline proc(fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.faccessat), uintptr(fd), transmute(uintptr)path, uintptr(mode), uintptr(flag))
+}
+
+syscall_getdirentries :: #force_inline proc(fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getdirentries), uintptr(fd), uintptr(buf), uintptr(nbytes), uintptr(base_pointer))
+}
+
+syscall_truncate :: #force_inline proc (path: cstring, length: off_t) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.truncate), transmute(uintptr)path, uintptr(length))
+}
+
+syscall_ftruncate :: #force_inline proc (fd: c.int, length: off_t) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.ftruncate), uintptr(fd), uintptr(length))
+}
+
+syscall_sysctl :: #force_inline proc (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctl), uintptr(name), uintptr(namelen), uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
+}
+
+syscall_copyfile :: #force_inline proc(from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.copyfile), transmute(uintptr)from, transmute(uintptr)to, uintptr(state), uintptr(flags))
+}
+
+// think about this? last arg should be more than one
+syscall_fcntl :: #force_inline proc(fd: c.int, cmd: c.int, other: rawptr) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other))
+}
+
+syscall_exit :: #force_inline proc(code: c.int) {
+ intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
+}
+
+syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.kill), uintptr(pid), uintptr(sig))
+}
+
+syscall_dup :: #force_inline proc(fd: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.dup), uintptr(fd))
+}
+
+syscall_execve :: #force_inline proc(path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.execve), transmute(uintptr)path, transmute(uintptr)argv, transmute(uintptr)env)
+}
+
+syscall_munmap :: #force_inline proc(addr: rawptr, len: u64) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len))
+}
+
+syscall_mmap :: #force_inline proc(addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
+ return cast(^u8)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len), uintptr(port), uintptr(flags), uintptr(fd), uintptr(offset))
+}
+
+syscall_flock :: #force_inline proc(fd: c.int, operation: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.flock), uintptr(fd), uintptr(operation))
+}
+
+syscall_utimes :: #force_inline proc(path: cstring, times: ^timeval) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.utimes), transmute(uintptr)path, uintptr(times))
+}
+
+syscall_futimes :: #force_inline proc(fd: c.int, times: ^timeval) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.futimes), uintptr(fd), uintptr(times))
+}
+
+syscall_adjtime :: #force_inline proc(delta: ^timeval, old_delta: ^timeval) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.adjtime), uintptr(delta), uintptr(old_delta))
+}
+
+syscall_sysctlbyname :: #force_inline proc(name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctlbyname), transmute(uintptr)name, uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
+}
+
+syscall_proc_info :: #force_inline proc(num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.proc_info), uintptr(num), uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(buffer), uintptr(buffer_size))
+}
+
+syscall_openat :: #force_inline proc(fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.openat), uintptr(fd), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
+}
+
+syscall_getentropy :: #force_inline proc(buf: [^]u8, buflen: u64) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen))
+}
+
+syscall_pipe :: #force_inline proc(fds: [^]c.int) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(&fds[0]), uintptr(&fds[1]))
+}
+
+syscall_chdir :: #force_inline proc(path: cstring) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), transmute(uintptr)path)
+}
+
+syscall_fchdir :: #force_inline proc(fd: c.int, path: cstring) -> c.int {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path)
+}
\ No newline at end of file
diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin
index 542a550cb..e138b8610 100644
--- a/core/sys/unix/pthread_darwin.odin
+++ b/core/sys/unix/pthread_darwin.odin
@@ -81,3 +81,16 @@ PTHREAD_MUTEX_NORMAL :: 0
PTHREAD_MUTEX_RECURSIVE :: 1
PTHREAD_MUTEX_ERRORCHECK :: 2
+PTHREAD_CANCEL_ENABLE :: 0
+PTHREAD_CANCEL_DISABLE :: 1
+PTHREAD_CANCEL_DEFERRED :: 0
+PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
+foreign import pthread "System.framework"
+
+@(default_calling_convention="c")
+foreign pthread {
+ pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
+ pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
+ pthread_cancel :: proc (thread: pthread_t) -> c.int ---
+}
\ No newline at end of file
diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin
index e5b0087b1..e02345cad 100644
--- a/core/sys/unix/pthread_freebsd.odin
+++ b/core/sys/unix/pthread_freebsd.odin
@@ -1,76 +1,76 @@
//+build freebsd
package unix
-import "core:c";
+import "core:c"
-pthread_t :: distinct u64;
-// pthread_t :: struct #align 16 { x: u64 };
+pthread_t :: distinct u64
+// pthread_t :: struct #align 16 { x: u64 }
-PTHREAD_COND_T_SIZE :: 8;
+PTHREAD_COND_T_SIZE :: 8
-PTHREAD_MUTEXATTR_T_SIZE :: 8;
-PTHREAD_CONDATTR_T_SIZE :: 8;
-PTHREAD_RWLOCKATTR_T_SIZE :: 8;
-PTHREAD_BARRIERATTR_T_SIZE :: 8;
+PTHREAD_MUTEXATTR_T_SIZE :: 8
+PTHREAD_CONDATTR_T_SIZE :: 8
+PTHREAD_RWLOCKATTR_T_SIZE :: 8
+PTHREAD_BARRIERATTR_T_SIZE :: 8
// WARNING: The sizes of these things are different yet again
// on non-X86!
when size_of(int) == 8 {
- PTHREAD_ATTR_T_SIZE :: 8;
- PTHREAD_MUTEX_T_SIZE :: 8;
- PTHREAD_RWLOCK_T_SIZE :: 8;
- PTHREAD_BARRIER_T_SIZE :: 8;
+ PTHREAD_ATTR_T_SIZE :: 8
+ PTHREAD_MUTEX_T_SIZE :: 8
+ PTHREAD_RWLOCK_T_SIZE :: 8
+ PTHREAD_BARRIER_T_SIZE :: 8
} else when size_of(int) == 4 { // TODO
- PTHREAD_ATTR_T_SIZE :: 32;
- PTHREAD_MUTEX_T_SIZE :: 32;
- PTHREAD_RWLOCK_T_SIZE :: 44;
- PTHREAD_BARRIER_T_SIZE :: 20;
+ PTHREAD_ATTR_T_SIZE :: 32
+ PTHREAD_MUTEX_T_SIZE :: 32
+ PTHREAD_RWLOCK_T_SIZE :: 44
+ PTHREAD_BARRIER_T_SIZE :: 20
}
pthread_cond_t :: struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
-};
+}
pthread_mutex_t :: struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
-};
+}
pthread_rwlock_t :: struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
-};
+}
pthread_barrier_t :: struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
-};
+}
pthread_attr_t :: struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
-};
+}
pthread_condattr_t :: struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
-};
+}
pthread_mutexattr_t :: struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
-};
+}
pthread_rwlockattr_t :: struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
-};
+}
pthread_barrierattr_t :: struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
-};
+}
-PTHREAD_MUTEX_ERRORCHECK :: 1;
-PTHREAD_MUTEX_RECURSIVE :: 2;
-PTHREAD_MUTEX_NORMAL :: 3;
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE :: 2
+PTHREAD_MUTEX_NORMAL :: 3
-PTHREAD_CREATE_JOINABLE :: 0;
-PTHREAD_CREATE_DETACHED :: 1;
-PTHREAD_INHERIT_SCHED :: 4;
-PTHREAD_EXPLICIT_SCHED :: 0;
-PTHREAD_PROCESS_PRIVATE :: 0;
-PTHREAD_PROCESS_SHARED :: 1;
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_CREATE_DETACHED :: 1
+PTHREAD_INHERIT_SCHED :: 4
+PTHREAD_EXPLICIT_SCHED :: 0
+PTHREAD_PROCESS_PRIVATE :: 0
+PTHREAD_PROCESS_SHARED :: 1
-SCHED_FIFO :: 1;
-SCHED_OTHER :: 2;
-SCHED_RR :: 3; // Round robin.
+SCHED_FIFO :: 1
+SCHED_OTHER :: 2
+SCHED_RR :: 3 // Round robin.
sched_param :: struct {
@@ -92,23 +92,31 @@ sem_t :: struct {
_padding: u32,
}
+PTHREAD_CANCEL_ENABLE :: 0
+PTHREAD_CANCEL_DISABLE :: 1
+PTHREAD_CANCEL_DEFERRED :: 0
+PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
foreign import "system:pthread"
@(default_calling_convention="c")
foreign pthread {
// create named semaphore.
// used in process-shared semaphores.
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---;
+ sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---;
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---;
- sem_post :: proc(sem: ^sem_t) -> c.int ---;
- sem_wait :: proc(sem: ^sem_t) -> c.int ---;
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---;
- // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---;
+ sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+ sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+ sem_post :: proc(sem: ^sem_t) -> c.int ---
+ sem_wait :: proc(sem: ^sem_t) -> c.int ---
+ sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+ // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
// see https://linux.die.net/man/3/pthread_yield
- pthread_yield :: proc() ---;
-}
+ pthread_yield :: proc() ---
+ pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
+ pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
+ pthread_cancel :: proc (thread: pthread_t) -> c.int ---
+}
\ No newline at end of file
diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin
index 099e7c7e9..9c297ef22 100644
--- a/core/sys/unix/pthread_linux.odin
+++ b/core/sys/unix/pthread_linux.odin
@@ -94,6 +94,11 @@ when size_of(int) == 8 {
SEM_T_SIZE :: 16
}
+PTHREAD_CANCEL_ENABLE :: 0
+PTHREAD_CANCEL_DISABLE :: 1
+PTHREAD_CANCEL_DEFERRED :: 0
+PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
foreign import "system:pthread"
@(default_calling_convention="c")
@@ -112,4 +117,8 @@ foreign pthread {
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
// see https://linux.die.net/man/3/pthread_yield
pthread_yield :: proc() -> c.int ---
+
+ pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
+ pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
+ pthread_cancel :: proc (thread: pthread_t) -> c.int ---
}
diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin
new file mode 100644
index 000000000..7ae82e662
--- /dev/null
+++ b/core/sys/unix/pthread_openbsd.odin
@@ -0,0 +1,74 @@
+//+build openbsd
+package unix
+
+import "core:c"
+
+pthread_t :: distinct rawptr
+pthread_attr_t :: distinct rawptr
+pthread_mutex_t :: distinct rawptr
+pthread_mutexattr_t :: distinct rawptr
+pthread_cond_t :: distinct rawptr
+pthread_condattr_t :: distinct rawptr
+pthread_rwlock_t :: distinct rawptr
+pthread_rwlockattr_t :: distinct rawptr
+pthread_barrier_t :: distinct rawptr
+pthread_barrierattr_t :: distinct rawptr
+pthread_spinlock_t :: distinct rawptr
+
+pthread_key_t :: distinct c.int
+pthread_once_t :: struct {
+ state: c.int,
+ mutex: pthread_mutex_t,
+}
+
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE :: 2
+PTHREAD_MUTEX_NORMAL :: 3
+PTHREAD_MUTEX_STRICT_NP :: 4
+
+PTHREAD_DETACHED :: 0x1
+PTHREAD_SCOPE_SYSTEM :: 0x2
+PTHREAD_INHERIT_SCHED :: 0x4
+PTHREAD_NOFLOAT :: 0x8
+
+PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_SCOPE_PROCESS :: 0
+PTHREAD_EXPLICIT_SCHED :: 0
+
+SCHED_FIFO :: 1
+SCHED_OTHER :: 2
+SCHED_RR :: 3
+
+sched_param :: struct {
+ sched_priority: c.int,
+}
+
+sem_t :: distinct rawptr
+
+PTHREAD_CANCEL_ENABLE :: 0
+PTHREAD_CANCEL_DISABLE :: 1
+PTHREAD_CANCEL_DEFERRED :: 0
+PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+ sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
+
+ sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+ sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+ sem_post :: proc(sem: ^sem_t) -> c.int ---
+ sem_wait :: proc(sem: ^sem_t) -> c.int ---
+ sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+ //sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
+
+ // NOTE: unclear whether pthread_yield is well-supported on Linux systems,
+ // see https://linux.die.net/man/3/pthread_yield
+ pthread_yield :: proc() ---
+
+ pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
+ pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
+ pthread_cancel :: proc (thread: pthread_t) -> c.int ---
+}
\ No newline at end of file
diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin
index ccd8f7844..8bf397647 100644
--- a/core/sys/unix/pthread_unix.odin
+++ b/core/sys/unix/pthread_unix.odin
@@ -1,10 +1,9 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package unix
foreign import "system:pthread"
import "core:c"
-import "core:time"
//
// On success, these functions return 0.
@@ -72,7 +71,7 @@ foreign pthread {
// assumes the mutex is pre-locked
pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int ---
- pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---
+ pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int ---
pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int ---
@@ -95,7 +94,7 @@ foreign pthread {
pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
- pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---
+ pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin
new file mode 100644
index 000000000..d611e33f0
--- /dev/null
+++ b/core/sys/unix/syscalls_linux.odin
@@ -0,0 +1,1854 @@
+package unix
+
+import "core:intrinsics"
+
+// Linux has inconsistent system call numbering across architectures,
+// for largely historical reasons. This attempts to provide a unified
+// Odin-side interface for system calls that are required for the core
+// library to work.
+
+// For authorative system call numbers, the following files in the kernel
+// source can be used:
+//
+// amd64: arch/x86/entry/syscalls/syscall_64.tbl
+// arm64: include/uapi/asm-generic/unistd.h
+// 386: arch/x86/entry/syscalls/sycall_32.tbl
+// arm: arch/arm/tools/syscall.tbl
+
+when ODIN_ARCH == .amd64 {
+ SYS_read : uintptr : 0
+ SYS_write : uintptr : 1
+ SYS_open : uintptr : 2
+ SYS_close : uintptr : 3
+ SYS_stat : uintptr : 4
+ SYS_fstat : uintptr : 5
+ SYS_lstat : uintptr : 6
+ SYS_poll : uintptr : 7
+ SYS_lseek : uintptr : 8
+ SYS_mmap : uintptr : 9
+ SYS_mprotect : uintptr : 10
+ SYS_munmap : uintptr : 11
+ SYS_brk : uintptr : 12
+ SYS_rt_sigaction : uintptr : 13
+ SYS_rt_sigprocmask : uintptr : 14
+ SYS_rt_sigreturn : uintptr : 15
+ SYS_ioctl : uintptr : 16
+ SYS_pread64 : uintptr : 17
+ SYS_pwrite64 : uintptr : 18
+ SYS_readv : uintptr : 19
+ SYS_writev : uintptr : 20
+ SYS_access : uintptr : 21
+ SYS_pipe : uintptr : 22
+ SYS_select : uintptr : 23
+ SYS_sched_yield : uintptr : 24
+ SYS_mremap : uintptr : 25
+ SYS_msync : uintptr : 26
+ SYS_mincore : uintptr : 27
+ SYS_madvise : uintptr : 28
+ SYS_shmget : uintptr : 29
+ SYS_shmat : uintptr : 30
+ SYS_shmctl : uintptr : 31
+ SYS_dup : uintptr : 32
+ SYS_dup2 : uintptr : 33
+ SYS_pause : uintptr : 34
+ SYS_nanosleep : uintptr : 35
+ SYS_getitimer : uintptr : 36
+ SYS_alarm : uintptr : 37
+ SYS_setitimer : uintptr : 38
+ SYS_getpid : uintptr : 39
+ SYS_sendfile : uintptr : 40
+ SYS_socket : uintptr : 41
+ SYS_connect : uintptr : 42
+ SYS_accept : uintptr : 43
+ SYS_sendto : uintptr : 44
+ SYS_recvfrom : uintptr : 45
+ SYS_sendmsg : uintptr : 46
+ SYS_recvmsg : uintptr : 47
+ SYS_shutdown : uintptr : 48
+ SYS_bind : uintptr : 49
+ SYS_listen : uintptr : 50
+ SYS_getsockname : uintptr : 51
+ SYS_getpeername : uintptr : 52
+ SYS_socketpair : uintptr : 53
+ SYS_setsockopt : uintptr : 54
+ SYS_getsockopt : uintptr : 55
+ SYS_clone : uintptr : 56
+ SYS_fork : uintptr : 57
+ SYS_vfork : uintptr : 58
+ SYS_execve : uintptr : 59
+ SYS_exit : uintptr : 60
+ SYS_wait4 : uintptr : 61
+ SYS_kill : uintptr : 62
+ SYS_uname : uintptr : 63
+ SYS_semget : uintptr : 64
+ SYS_semop : uintptr : 65
+ SYS_semctl : uintptr : 66
+ SYS_shmdt : uintptr : 67
+ SYS_msgget : uintptr : 68
+ SYS_msgsnd : uintptr : 69
+ SYS_msgrcv : uintptr : 70
+ SYS_msgctl : uintptr : 71
+ SYS_fcntl : uintptr : 72
+ SYS_flock : uintptr : 73
+ SYS_fsync : uintptr : 74
+ SYS_fdatasync : uintptr : 75
+ SYS_truncate : uintptr : 76
+ SYS_ftruncate : uintptr : 77
+ SYS_getdents : uintptr : 78
+ SYS_getcwd : uintptr : 79
+ SYS_chdir : uintptr : 80
+ SYS_fchdir : uintptr : 81
+ SYS_rename : uintptr : 82
+ SYS_mkdir : uintptr : 83
+ SYS_rmdir : uintptr : 84
+ SYS_creat : uintptr : 85
+ SYS_link : uintptr : 86
+ SYS_unlink : uintptr : 87
+ SYS_symlink : uintptr : 88
+ SYS_readlink : uintptr : 89
+ SYS_chmod : uintptr : 90
+ SYS_fchmod : uintptr : 91
+ SYS_chown : uintptr : 92
+ SYS_fchown : uintptr : 93
+ SYS_lchown : uintptr : 94
+ SYS_umask : uintptr : 95
+ SYS_gettimeofday : uintptr : 96
+ SYS_getrlimit : uintptr : 97
+ SYS_getrusage : uintptr : 98
+ SYS_sysinfo : uintptr : 99
+ SYS_times : uintptr : 100
+ SYS_ptrace : uintptr : 101
+ SYS_getuid : uintptr : 102
+ SYS_syslog : uintptr : 103
+ SYS_getgid : uintptr : 104
+ SYS_setuid : uintptr : 105
+ SYS_setgid : uintptr : 106
+ SYS_geteuid : uintptr : 107
+ SYS_getegid : uintptr : 108
+ SYS_setpgid : uintptr : 109
+ SYS_getppid : uintptr : 110
+ SYS_getpgrp : uintptr : 111
+ SYS_setsid : uintptr : 112
+ SYS_setreuid : uintptr : 113
+ SYS_setregid : uintptr : 114
+ SYS_getgroups : uintptr : 115
+ SYS_setgroups : uintptr : 116
+ SYS_setresuid : uintptr : 117
+ SYS_getresuid : uintptr : 118
+ SYS_setresgid : uintptr : 119
+ SYS_getresgid : uintptr : 120
+ SYS_getpgid : uintptr : 121
+ SYS_setfsuid : uintptr : 122
+ SYS_setfsgid : uintptr : 123
+ SYS_getsid : uintptr : 124
+ SYS_capget : uintptr : 125
+ SYS_capset : uintptr : 126
+ SYS_rt_sigpending : uintptr : 127
+ SYS_rt_sigtimedwait : uintptr : 128
+ SYS_rt_sigqueueinfo : uintptr : 129
+ SYS_rt_sigsuspend : uintptr : 130
+ SYS_sigaltstack : uintptr : 131
+ SYS_utime : uintptr : 132
+ SYS_mknod : uintptr : 133
+ SYS_uselib : uintptr : 134
+ SYS_personality : uintptr : 135
+ SYS_ustat : uintptr : 136
+ SYS_statfs : uintptr : 137
+ SYS_fstatfs : uintptr : 138
+ SYS_sysfs : uintptr : 139
+ SYS_getpriority : uintptr : 140
+ SYS_setpriority : uintptr : 141
+ SYS_sched_setparam : uintptr : 142
+ SYS_sched_getparam : uintptr : 143
+ SYS_sched_setscheduler : uintptr : 144
+ SYS_sched_getscheduler : uintptr : 145
+ SYS_sched_get_priority_max : uintptr : 146
+ SYS_sched_get_priority_min : uintptr : 147
+ SYS_sched_rr_get_interval : uintptr : 148
+ SYS_mlock : uintptr : 149
+ SYS_munlock : uintptr : 150
+ SYS_mlockall : uintptr : 151
+ SYS_munlockall : uintptr : 152
+ SYS_vhangup : uintptr : 153
+ SYS_modify_ldt : uintptr : 154
+ SYS_pivot_root : uintptr : 155
+ SYS__sysctl : uintptr : 156
+ SYS_prctl : uintptr : 157
+ SYS_arch_prctl : uintptr : 158
+ SYS_adjtimex : uintptr : 159
+ SYS_setrlimit : uintptr : 160
+ SYS_chroot : uintptr : 161
+ SYS_sync : uintptr : 162
+ SYS_acct : uintptr : 163
+ SYS_settimeofday : uintptr : 164
+ SYS_mount : uintptr : 165
+ SYS_umount2 : uintptr : 166
+ SYS_swapon : uintptr : 167
+ SYS_swapoff : uintptr : 168
+ SYS_reboot : uintptr : 169
+ SYS_sethostname : uintptr : 170
+ SYS_setdomainname : uintptr : 171
+ SYS_iopl : uintptr : 172
+ SYS_ioperm : uintptr : 173
+ SYS_create_module : uintptr : 174
+ SYS_init_module : uintptr : 175
+ SYS_delete_module : uintptr : 176
+ SYS_get_kernel_syms : uintptr : 177
+ SYS_query_module : uintptr : 178
+ SYS_quotactl : uintptr : 179
+ SYS_nfsservctl : uintptr : 180
+ SYS_getpmsg : uintptr : 181
+ SYS_putpmsg : uintptr : 182
+ SYS_afs_syscall : uintptr : 183
+ SYS_tuxcall : uintptr : 184
+ SYS_security : uintptr : 185
+ SYS_gettid : uintptr : 186
+ SYS_readahead : uintptr : 187
+ SYS_setxattr : uintptr : 188
+ SYS_lsetxattr : uintptr : 189
+ SYS_fsetxattr : uintptr : 190
+ SYS_getxattr : uintptr : 191
+ SYS_lgetxattr : uintptr : 192
+ SYS_fgetxattr : uintptr : 193
+ SYS_listxattr : uintptr : 194
+ SYS_llistxattr : uintptr : 195
+ SYS_flistxattr : uintptr : 196
+ SYS_removexattr : uintptr : 197
+ SYS_lremovexattr : uintptr : 198
+ SYS_fremovexattr : uintptr : 199
+ SYS_tkill : uintptr : 200
+ SYS_time : uintptr : 201
+ SYS_futex : uintptr : 202
+ SYS_sched_setaffinity : uintptr : 203
+ SYS_sched_getaffinity : uintptr : 204
+ SYS_set_thread_area : uintptr : 205
+ SYS_io_setup : uintptr : 206
+ SYS_io_destroy : uintptr : 207
+ SYS_io_getevents : uintptr : 208
+ SYS_io_submit : uintptr : 209
+ SYS_io_cancel : uintptr : 210
+ SYS_get_thread_area : uintptr : 211
+ SYS_lookup_dcookie : uintptr : 212
+ SYS_epoll_create : uintptr : 213
+ SYS_epoll_ctl_old : uintptr : 214
+ SYS_epoll_wait_old : uintptr : 215
+ SYS_remap_file_pages : uintptr : 216
+ SYS_getdents64 : uintptr : 217
+ SYS_set_tid_address : uintptr : 218
+ SYS_restart_syscall : uintptr : 219
+ SYS_semtimedop : uintptr : 220
+ SYS_fadvise64 : uintptr : 221
+ SYS_timer_create : uintptr : 222
+ SYS_timer_settime : uintptr : 223
+ SYS_timer_gettime : uintptr : 224
+ SYS_timer_getoverrun : uintptr : 225
+ SYS_timer_delete : uintptr : 226
+ SYS_clock_settime : uintptr : 227
+ SYS_clock_gettime : uintptr : 228
+ SYS_clock_getres : uintptr : 229
+ SYS_clock_nanosleep : uintptr : 230
+ SYS_exit_group : uintptr : 231
+ SYS_epoll_wait : uintptr : 232
+ SYS_epoll_ctl : uintptr : 233
+ SYS_tgkill : uintptr : 234
+ SYS_utimes : uintptr : 235
+ SYS_vserver : uintptr : 236
+ SYS_mbind : uintptr : 237
+ SYS_set_mempolicy : uintptr : 238
+ SYS_get_mempolicy : uintptr : 239
+ SYS_mq_open : uintptr : 240
+ SYS_mq_unlink : uintptr : 241
+ SYS_mq_timedsend : uintptr : 242
+ SYS_mq_timedreceive : uintptr : 243
+ SYS_mq_notify : uintptr : 244
+ SYS_mq_getsetattr : uintptr : 245
+ SYS_kexec_load : uintptr : 246
+ SYS_waitid : uintptr : 247
+ SYS_add_key : uintptr : 248
+ SYS_request_key : uintptr : 249
+ SYS_keyctl : uintptr : 250
+ SYS_ioprio_set : uintptr : 251
+ SYS_ioprio_get : uintptr : 252
+ SYS_inotify_init : uintptr : 253
+ SYS_inotify_add_watch : uintptr : 254
+ SYS_inotify_rm_watch : uintptr : 255
+ SYS_migrate_pages : uintptr : 256
+ SYS_openat : uintptr : 257
+ SYS_mkdirat : uintptr : 258
+ SYS_mknodat : uintptr : 259
+ SYS_fchownat : uintptr : 260
+ SYS_futimesat : uintptr : 261
+ SYS_fstatat : uintptr : 262
+ SYS_unlinkat : uintptr : 263
+ SYS_renameat : uintptr : 264
+ SYS_linkat : uintptr : 265
+ SYS_symlinkat : uintptr : 266
+ SYS_readlinkat : uintptr : 267
+ SYS_fchmodat : uintptr : 268
+ SYS_faccessat : uintptr : 269
+ SYS_pselect6 : uintptr : 270
+ SYS_ppoll : uintptr : 271
+ SYS_unshare : uintptr : 272
+ SYS_set_robust_list : uintptr : 273
+ SYS_get_robust_list : uintptr : 274
+ SYS_splice : uintptr : 275
+ SYS_tee : uintptr : 276
+ SYS_sync_file_range : uintptr : 277
+ SYS_vmsplice : uintptr : 278
+ SYS_move_pages : uintptr : 279
+ SYS_utimensat : uintptr : 280
+ SYS_epoll_pwait : uintptr : 281
+ SYS_signalfd : uintptr : 282
+ SYS_timerfd_create : uintptr : 283
+ SYS_eventfd : uintptr : 284
+ SYS_fallocate : uintptr : 285
+ SYS_timerfd_settime : uintptr : 286
+ SYS_timerfd_gettime : uintptr : 287
+ SYS_accept4 : uintptr : 288
+ SYS_signalfd4 : uintptr : 289
+ SYS_eventfd2 : uintptr : 290
+ SYS_epoll_create1 : uintptr : 291
+ SYS_dup3 : uintptr : 292
+ SYS_pipe2 : uintptr : 293
+ SYS_inotify_init1 : uintptr : 294
+ SYS_preadv : uintptr : 295
+ SYS_pwritev : uintptr : 296
+ SYS_rt_tgsigqueueinfo : uintptr : 297
+ SYS_perf_event_open : uintptr : 298
+ SYS_recvmmsg : uintptr : 299
+ SYS_fanotify_init : uintptr : 300
+ SYS_fanotify_mark : uintptr : 301
+ SYS_prlimit64 : uintptr : 302
+ SYS_name_to_handle_at : uintptr : 303
+ SYS_open_by_handle_at : uintptr : 304
+ SYS_clock_adjtime : uintptr : 305
+ SYS_syncfs : uintptr : 306
+ SYS_sendmmsg : uintptr : 307
+ SYS_setns : uintptr : 308
+ SYS_getcpu : uintptr : 309
+ SYS_process_vm_readv : uintptr : 310
+ SYS_process_vm_writev : uintptr : 311
+ SYS_kcmp : uintptr : 312
+ SYS_finit_module : uintptr : 313
+ SYS_sched_setattr : uintptr : 314
+ SYS_sched_getattr : uintptr : 315
+ SYS_renameat2 : uintptr : 316
+ SYS_seccomp : uintptr : 317
+ SYS_getrandom : uintptr : 318
+ SYS_memfd_create : uintptr : 319
+ SYS_kexec_file_load : uintptr : 320
+ SYS_bpf : uintptr : 321
+ SYS_execveat : uintptr : 322
+ SYS_userfaultfd : uintptr : 323
+ SYS_membarrier : uintptr : 324
+ SYS_mlock2 : uintptr : 325
+ SYS_copy_file_range : uintptr : 326
+ SYS_preadv2 : uintptr : 327
+ SYS_pwritev2 : uintptr : 328
+ SYS_pkey_mprotect : uintptr : 329
+ SYS_pkey_alloc : uintptr : 330
+ SYS_pkey_free : uintptr : 331
+ SYS_statx : uintptr : 332
+ SYS_io_pgetevents : uintptr : 333
+ SYS_rseq : uintptr : 334
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+ SYS_memfd_secret : uintptr : 447
+} else when ODIN_ARCH == .arm64 {
+ SYS_io_setup : uintptr : 0
+ SYS_io_destroy : uintptr : 1
+ SYS_io_submit : uintptr : 2
+ SYS_io_cancel : uintptr : 3
+ SYS_io_getevents : uintptr : 4
+ SYS_setxattr : uintptr : 5
+ SYS_lsetxattr : uintptr : 6
+ SYS_fsetxattr : uintptr : 7
+ SYS_getxattr : uintptr : 8
+ SYS_lgetxattr : uintptr : 9
+ SYS_fgetxattr : uintptr : 10
+ SYS_listxattr : uintptr : 11
+ SYS_llistxattr : uintptr : 12
+ SYS_flistxattr : uintptr : 13
+ SYS_removexattr : uintptr : 14
+ SYS_lremovexattr : uintptr : 15
+ SYS_fremovexattr : uintptr : 16
+ SYS_getcwd : uintptr : 17
+ SYS_lookup_dcookie : uintptr : 18
+ SYS_eventfd2 : uintptr : 19
+ SYS_epoll_create1 : uintptr : 20
+ SYS_epoll_ctl : uintptr : 21
+ SYS_epoll_pwait : uintptr : 22
+ SYS_dup : uintptr : 23
+ SYS_dup3 : uintptr : 24
+ SYS_fcntl : uintptr : 25
+ SYS_inotify_init1 : uintptr : 26
+ SYS_inotify_add_watch : uintptr : 27
+ SYS_inotify_rm_watch : uintptr : 28
+ SYS_ioctl : uintptr : 29
+ SYS_ioprio_set : uintptr : 30
+ SYS_ioprio_get : uintptr : 31
+ SYS_flock : uintptr : 32
+ SYS_mknodat : uintptr : 33
+ SYS_mkdirat : uintptr : 34
+ SYS_unlinkat : uintptr : 35
+ SYS_symlinkat : uintptr : 36
+ SYS_linkat : uintptr : 37
+ SYS_renameat : uintptr : 38
+ SYS_umount2 : uintptr : 39
+ SYS_mount : uintptr : 40
+ SYS_pivot_root : uintptr : 41
+ SYS_nfsservctl : uintptr : 42
+ SYS_statfs : uintptr : 43
+ SYS_fstatfs : uintptr : 44
+ SYS_truncate : uintptr : 45
+ SYS_ftruncate : uintptr : 46
+ SYS_fallocate : uintptr : 47
+ SYS_faccessat : uintptr : 48
+ SYS_chdir : uintptr : 49
+ SYS_fchdir : uintptr : 50
+ SYS_chroot : uintptr : 51
+ SYS_fchmod : uintptr : 52
+ SYS_fchmodat : uintptr : 53
+ SYS_fchownat : uintptr : 54
+ SYS_fchown : uintptr : 55
+ SYS_openat : uintptr : 56
+ SYS_close : uintptr : 57
+ SYS_vhangup : uintptr : 58
+ SYS_pipe2 : uintptr : 59
+ SYS_quotactl : uintptr : 60
+ SYS_getdents64 : uintptr : 61
+ SYS_lseek : uintptr : 62
+ SYS_read : uintptr : 63
+ SYS_write : uintptr : 64
+ SYS_readv : uintptr : 65
+ SYS_writev : uintptr : 66
+ SYS_pread64 : uintptr : 67
+ SYS_pwrite64 : uintptr : 68
+ SYS_preadv : uintptr : 69
+ SYS_pwritev : uintptr : 70
+ SYS_sendfile : uintptr : 71
+ SYS_pselect6 : uintptr : 72
+ SYS_ppoll : uintptr : 73
+ SYS_signalfd4 : uintptr : 74
+ SYS_vmsplice : uintptr : 75
+ SYS_splice : uintptr : 76
+ SYS_tee : uintptr : 77
+ SYS_readlinkat : uintptr : 78
+ SYS_fstatat : uintptr : 79
+ SYS_fstat : uintptr : 80
+ SYS_sync : uintptr : 81
+ SYS_fsync : uintptr : 82
+ SYS_fdatasync : uintptr : 83
+ SYS_sync_file_range : uintptr : 84
+ SYS_timerfd_create : uintptr : 85
+ SYS_timerfd_settime : uintptr : 86
+ SYS_timerfd_gettime : uintptr : 87
+ SYS_utimensat : uintptr : 88
+ SYS_acct : uintptr : 89
+ SYS_capget : uintptr : 90
+ SYS_capset : uintptr : 91
+ SYS_personality : uintptr : 92
+ SYS_exit : uintptr : 93
+ SYS_exit_group : uintptr : 94
+ SYS_waitid : uintptr : 95
+ SYS_set_tid_address : uintptr : 96
+ SYS_unshare : uintptr : 97
+ SYS_futex : uintptr : 98
+ SYS_set_robust_list : uintptr : 99
+ SYS_get_robust_list : uintptr : 100
+ SYS_nanosleep : uintptr : 101
+ SYS_getitimer : uintptr : 102
+ SYS_setitimer : uintptr : 103
+ SYS_kexec_load : uintptr : 104
+ SYS_init_module : uintptr : 105
+ SYS_delete_module : uintptr : 106
+ SYS_timer_create : uintptr : 107
+ SYS_timer_gettime : uintptr : 108
+ SYS_timer_getoverrun : uintptr : 109
+ SYS_timer_settime : uintptr : 110
+ SYS_timer_delete : uintptr : 111
+ SYS_clock_settime : uintptr : 112
+ SYS_clock_gettime : uintptr : 113
+ SYS_clock_getres : uintptr : 114
+ SYS_clock_nanosleep : uintptr : 115
+ SYS_syslog : uintptr : 116
+ SYS_ptrace : uintptr : 117
+ SYS_sched_setparam : uintptr : 118
+ SYS_sched_setscheduler : uintptr : 119
+ SYS_sched_getscheduler : uintptr : 120
+ SYS_sched_getparam : uintptr : 121
+ SYS_sched_setaffinity : uintptr : 122
+ SYS_sched_getaffinity : uintptr : 123
+ SYS_sched_yield : uintptr : 124
+ SYS_sched_get_priority_max : uintptr : 125
+ SYS_sched_get_priority_min : uintptr : 126
+ SYS_sched_rr_get_interval : uintptr : 127
+ SYS_restart_syscall : uintptr : 128
+ SYS_kill : uintptr : 129
+ SYS_tkill : uintptr : 130
+ SYS_tgkill : uintptr : 131
+ SYS_sigaltstack : uintptr : 132
+ SYS_rt_sigsuspend : uintptr : 133
+ SYS_rt_sigaction : uintptr : 134
+ SYS_rt_sigprocmask : uintptr : 135
+ SYS_rt_sigpending : uintptr : 136
+ SYS_rt_sigtimedwait : uintptr : 137
+ SYS_rt_sigqueueinfo : uintptr : 138
+ SYS_rt_sigreturn : uintptr : 139
+ SYS_setpriority : uintptr : 140
+ SYS_getpriority : uintptr : 141
+ SYS_reboot : uintptr : 142
+ SYS_setregid : uintptr : 143
+ SYS_setgid : uintptr : 144
+ SYS_setreuid : uintptr : 145
+ SYS_setuid : uintptr : 146
+ SYS_setresuid : uintptr : 147
+ SYS_getresuid : uintptr : 148
+ SYS_setresgid : uintptr : 149
+ SYS_getresgid : uintptr : 150
+ SYS_setfsuid : uintptr : 151
+ SYS_setfsgid : uintptr : 152
+ SYS_times : uintptr : 153
+ SYS_setpgid : uintptr : 154
+ SYS_getpgid : uintptr : 155
+ SYS_getsid : uintptr : 156
+ SYS_setsid : uintptr : 157
+ SYS_getgroups : uintptr : 158
+ SYS_setgroups : uintptr : 159
+ SYS_uname : uintptr : 160
+ SYS_sethostname : uintptr : 161
+ SYS_setdomainname : uintptr : 162
+ SYS_getrlimit : uintptr : 163
+ SYS_setrlimit : uintptr : 164
+ SYS_getrusage : uintptr : 165
+ SYS_umask : uintptr : 166
+ SYS_prctl : uintptr : 167
+ SYS_getcpu : uintptr : 168
+ SYS_gettimeofday : uintptr : 169
+ SYS_settimeofday : uintptr : 170
+ SYS_adjtimex : uintptr : 171
+ SYS_getpid : uintptr : 172
+ SYS_getppid : uintptr : 173
+ SYS_getuid : uintptr : 174
+ SYS_geteuid : uintptr : 175
+ SYS_getgid : uintptr : 176
+ SYS_getegid : uintptr : 177
+ SYS_gettid : uintptr : 178
+ SYS_sysinfo : uintptr : 179
+ SYS_mq_open : uintptr : 180
+ SYS_mq_unlink : uintptr : 181
+ SYS_mq_timedsend : uintptr : 182
+ SYS_mq_timedreceive : uintptr : 183
+ SYS_mq_notify : uintptr : 184
+ SYS_mq_getsetattr : uintptr : 185
+ SYS_msgget : uintptr : 186
+ SYS_msgctl : uintptr : 187
+ SYS_msgrcv : uintptr : 188
+ SYS_msgsnd : uintptr : 189
+ SYS_semget : uintptr : 190
+ SYS_semctl : uintptr : 191
+ SYS_semtimedop : uintptr : 192
+ SYS_semop : uintptr : 193
+ SYS_shmget : uintptr : 194
+ SYS_shmctl : uintptr : 195
+ SYS_shmat : uintptr : 196
+ SYS_shmdt : uintptr : 197
+ SYS_socket : uintptr : 198
+ SYS_socketpair : uintptr : 199
+ SYS_bind : uintptr : 200
+ SYS_listen : uintptr : 201
+ SYS_accept : uintptr : 202
+ SYS_connect : uintptr : 203
+ SYS_getsockname : uintptr : 204
+ SYS_getpeername : uintptr : 205
+ SYS_sendto : uintptr : 206
+ SYS_recvfrom : uintptr : 207
+ SYS_setsockopt : uintptr : 208
+ SYS_getsockopt : uintptr : 209
+ SYS_shutdown : uintptr : 210
+ SYS_sendmsg : uintptr : 211
+ SYS_recvmsg : uintptr : 212
+ SYS_readahead : uintptr : 213
+ SYS_brk : uintptr : 214
+ SYS_munmap : uintptr : 215
+ SYS_mremap : uintptr : 216
+ SYS_add_key : uintptr : 217
+ SYS_request_key : uintptr : 218
+ SYS_keyctl : uintptr : 219
+ SYS_clone : uintptr : 220
+ SYS_execve : uintptr : 221
+ SYS_mmap : uintptr : 222
+ SYS_fadvise64 : uintptr : 223
+ SYS_swapon : uintptr : 224
+ SYS_swapoff : uintptr : 225
+ SYS_mprotect : uintptr : 226
+ SYS_msync : uintptr : 227
+ SYS_mlock : uintptr : 228
+ SYS_munlock : uintptr : 229
+ SYS_mlockall : uintptr : 230
+ SYS_munlockall : uintptr : 231
+ SYS_mincore : uintptr : 232
+ SYS_madvise : uintptr : 233
+ SYS_remap_file_pages : uintptr : 234
+ SYS_mbind : uintptr : 235
+ SYS_get_mempolicy : uintptr : 236
+ SYS_set_mempolicy : uintptr : 237
+ SYS_migrate_pages : uintptr : 238
+ SYS_move_pages : uintptr : 239
+ SYS_rt_tgsigqueueinfo : uintptr : 240
+ SYS_perf_event_open : uintptr : 241
+ SYS_accept4 : uintptr : 242
+ SYS_recvmmsg : uintptr : 243
+ SYS_arch_specific_syscall : uintptr : 244
+ SYS_wait4 : uintptr : 260
+ SYS_prlimit64 : uintptr : 261
+ SYS_fanotify_init : uintptr : 262
+ SYS_fanotify_mark : uintptr : 263
+ SYS_clock_adjtime : uintptr : 266
+ SYS_syncfs : uintptr : 267
+ SYS_setns : uintptr : 268
+ SYS_sendmmsg : uintptr : 269
+ SYS_process_vm_readv : uintptr : 270
+ SYS_process_vm_writev : uintptr : 271
+ SYS_kcmp : uintptr : 272
+ SYS_finit_module : uintptr : 273
+ SYS_sched_setattr : uintptr : 274
+ SYS_sched_getattr : uintptr : 275
+ SYS_renameat2 : uintptr : 276
+ SYS_seccomp : uintptr : 277
+ SYS_getrandom : uintptr : 278
+ SYS_memfd_create : uintptr : 279
+ SYS_bpf : uintptr : 280
+ SYS_execveat : uintptr : 281
+ SYS_userfaultfd : uintptr : 282
+ SYS_membarrier : uintptr : 283
+ SYS_mlock2 : uintptr : 284
+ SYS_copy_file_range : uintptr : 285
+ SYS_preadv2 : uintptr : 286
+ SYS_pwritev2 : uintptr : 287
+ SYS_pkey_mprotect : uintptr : 288
+ SYS_pkey_alloc : uintptr : 289
+ SYS_pkey_free : uintptr : 290
+ SYS_statx : uintptr : 291
+ SYS_io_pgetevents : uintptr : 292
+ SYS_rseq : uintptr : 293
+ SYS_kexec_file_load : uintptr : 294
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+
+ SIGCHLD :: 17
+} else when ODIN_ARCH == .i386 {
+ SYS_restart_syscall : uintptr : 0
+ SYS_exit : uintptr : 1
+ SYS_fork : uintptr : 2
+ SYS_read : uintptr : 3
+ SYS_write : uintptr : 4
+ SYS_open : uintptr : 5
+ SYS_close : uintptr : 6
+ SYS_waitpid : uintptr : 7
+ SYS_creat : uintptr : 8
+ SYS_link : uintptr : 9
+ SYS_unlink : uintptr : 10
+ SYS_execve : uintptr : 11
+ SYS_chdir : uintptr : 12
+ SYS_time : uintptr : 13
+ SYS_mknod : uintptr : 14
+ SYS_chmod : uintptr : 15
+ SYS_lchown : uintptr : 16
+ SYS_break : uintptr : 17
+ SYS_oldstat : uintptr : 18
+ SYS_lseek : uintptr : 19
+ SYS_getpid : uintptr : 20
+ SYS_mount : uintptr : 21
+ SYS_umount : uintptr : 22
+ SYS_setuid : uintptr : 23
+ SYS_getuid : uintptr : 24
+ SYS_stime : uintptr : 25
+ SYS_ptrace : uintptr : 26
+ SYS_alarm : uintptr : 27
+ SYS_oldfstat : uintptr : 28
+ SYS_pause : uintptr : 29
+ SYS_utime : uintptr : 30
+ SYS_stty : uintptr : 31
+ SYS_gtty : uintptr : 32
+ SYS_access : uintptr : 33
+ SYS_nice : uintptr : 34
+ SYS_ftime : uintptr : 35
+ SYS_sync : uintptr : 36
+ SYS_kill : uintptr : 37
+ SYS_rename : uintptr : 38
+ SYS_mkdir : uintptr : 39
+ SYS_rmdir : uintptr : 40
+ SYS_dup : uintptr : 41
+ SYS_pipe : uintptr : 42
+ SYS_times : uintptr : 43
+ SYS_prof : uintptr : 44
+ SYS_brk : uintptr : 45
+ SYS_setgid : uintptr : 46
+ SYS_getgid : uintptr : 47
+ SYS_signal : uintptr : 48
+ SYS_geteuid : uintptr : 49
+ SYS_getegid : uintptr : 50
+ SYS_acct : uintptr : 51
+ SYS_umount2 : uintptr : 52
+ SYS_lock : uintptr : 53
+ SYS_ioctl : uintptr : 54
+ SYS_fcntl : uintptr : 55
+ SYS_mpx : uintptr : 56
+ SYS_setpgid : uintptr : 57
+ SYS_ulimit : uintptr : 58
+ SYS_oldolduname : uintptr : 59
+ SYS_umask : uintptr : 60
+ SYS_chroot : uintptr : 61
+ SYS_ustat : uintptr : 62
+ SYS_dup2 : uintptr : 63
+ SYS_getppid : uintptr : 64
+ SYS_getpgrp : uintptr : 65
+ SYS_setsid : uintptr : 66
+ SYS_sigaction : uintptr : 67
+ SYS_sgetmask : uintptr : 68
+ SYS_ssetmask : uintptr : 69
+ SYS_setreuid : uintptr : 70
+ SYS_setregid : uintptr : 71
+ SYS_sigsuspend : uintptr : 72
+ SYS_sigpending : uintptr : 73
+ SYS_sethostname : uintptr : 74
+ SYS_setrlimit : uintptr : 75
+ SYS_getrlimit : uintptr : 76
+ SYS_getrusage : uintptr : 77
+ SYS_gettimeofday : uintptr : 78
+ SYS_settimeofday : uintptr : 79
+ SYS_getgroups : uintptr : 80
+ SYS_setgroups : uintptr : 81
+ SYS_select : uintptr : 82
+ SYS_symlink : uintptr : 83
+ SYS_oldlstat : uintptr : 84
+ SYS_readlink : uintptr : 85
+ SYS_uselib : uintptr : 86
+ SYS_swapon : uintptr : 87
+ SYS_reboot : uintptr : 88
+ SYS_readdir : uintptr : 89
+ SYS_old_mmap : uintptr : 90 // 90 is "sys_old_mmap", we want mmap2
+ SYS_munmap : uintptr : 91
+ SYS_truncate : uintptr : 92
+ SYS_ftruncate : uintptr : 93
+ SYS_fchmod : uintptr : 94
+ SYS_fchown : uintptr : 95
+ SYS_getpriority : uintptr : 96
+ SYS_setpriority : uintptr : 97
+ SYS_profil : uintptr : 98
+ SYS_statfs : uintptr : 99
+ SYS_fstatfs : uintptr : 100
+ SYS_ioperm : uintptr : 101
+ SYS_socketcall : uintptr : 102
+ SYS_syslog : uintptr : 103
+ SYS_setitimer : uintptr : 104
+ SYS_getitimer : uintptr : 105
+ SYS_stat : uintptr : 106
+ SYS_lstat : uintptr : 107
+ SYS_fstat : uintptr : 108
+ SYS_olduname : uintptr : 109
+ SYS_iopl : uintptr : 110
+ SYS_vhangup : uintptr : 111
+ SYS_idle : uintptr : 112
+ SYS_vm86old : uintptr : 113
+ SYS_wait4 : uintptr : 114
+ SYS_swapoff : uintptr : 115
+ SYS_sysinfo : uintptr : 116
+ SYS_ipc : uintptr : 117
+ SYS_fsync : uintptr : 118
+ SYS_sigreturn : uintptr : 119
+ SYS_clone : uintptr : 120
+ SYS_setdomainname : uintptr : 121
+ SYS_uname : uintptr : 122
+ SYS_modify_ldt : uintptr : 123
+ SYS_adjtimex : uintptr : 124
+ SYS_mprotect : uintptr : 125
+ SYS_sigprocmask : uintptr : 126
+ SYS_create_module : uintptr : 127
+ SYS_init_module : uintptr : 128
+ SYS_delete_module : uintptr : 129
+ SYS_get_kernel_syms : uintptr : 130
+ SYS_quotactl : uintptr : 131
+ SYS_getpgid : uintptr : 132
+ SYS_fchdir : uintptr : 133
+ SYS_bdflush : uintptr : 134
+ SYS_sysfs : uintptr : 135
+ SYS_personality : uintptr : 136
+ SYS_afs_syscall : uintptr : 137
+ SYS_setfsuid : uintptr : 138
+ SYS_setfsgid : uintptr : 139
+ SYS__llseek : uintptr : 140
+ SYS_getdents : uintptr : 141
+ SYS__newselect : uintptr : 142
+ SYS_flock : uintptr : 143
+ SYS_msync : uintptr : 144
+ SYS_readv : uintptr : 145
+ SYS_writev : uintptr : 146
+ SYS_getsid : uintptr : 147
+ SYS_fdatasync : uintptr : 148
+ SYS__sysctl : uintptr : 149
+ SYS_mlock : uintptr : 150
+ SYS_munlock : uintptr : 151
+ SYS_mlockall : uintptr : 152
+ SYS_munlockall : uintptr : 153
+ SYS_sched_setparam : uintptr : 154
+ SYS_sched_getparam : uintptr : 155
+ SYS_sched_setscheduler : uintptr : 156
+ SYS_sched_getscheduler : uintptr : 157
+ SYS_sched_yield : uintptr : 158
+ SYS_sched_get_priority_max : uintptr : 159
+ SYS_sched_get_priority_min : uintptr : 160
+ SYS_sched_rr_get_interval : uintptr : 161
+ SYS_nanosleep : uintptr : 162
+ SYS_mremap : uintptr : 163
+ SYS_setresuid : uintptr : 164
+ SYS_getresuid : uintptr : 165
+ SYS_vm86 : uintptr : 166
+ SYS_query_module : uintptr : 167
+ SYS_poll : uintptr : 168
+ SYS_nfsservctl : uintptr : 169
+ SYS_setresgid : uintptr : 170
+ SYS_getresgid : uintptr : 171
+ SYS_prctl : uintptr : 172
+ SYS_rt_sigreturn : uintptr : 173
+ SYS_rt_sigaction : uintptr : 174
+ SYS_rt_sigprocmask : uintptr : 175
+ SYS_rt_sigpending : uintptr : 176
+ SYS_rt_sigtimedwait : uintptr : 177
+ SYS_rt_sigqueueinfo : uintptr : 178
+ SYS_rt_sigsuspend : uintptr : 179
+ SYS_pread64 : uintptr : 180
+ SYS_pwrite64 : uintptr : 181
+ SYS_chown : uintptr : 182
+ SYS_getcwd : uintptr : 183
+ SYS_capget : uintptr : 184
+ SYS_capset : uintptr : 185
+ SYS_sigaltstack : uintptr : 186
+ SYS_sendfile : uintptr : 187
+ SYS_getpmsg : uintptr : 188
+ SYS_putpmsg : uintptr : 189
+ SYS_vfork : uintptr : 190
+ SYS_ugetrlimit : uintptr : 191
+ SYS_mmap : uintptr : 192 // actually mmap2
+ SYS_truncate64 : uintptr : 193
+ SYS_ftruncate64 : uintptr : 194
+ SYS_stat64 : uintptr : 195
+ SYS_lstat64 : uintptr : 196
+ SYS_fstat64 : uintptr : 197
+ SYS_lchown32 : uintptr : 198
+ SYS_getuid32 : uintptr : 199
+ SYS_getgid32 : uintptr : 200
+ SYS_geteuid32 : uintptr : 201
+ SYS_getegid32 : uintptr : 202
+ SYS_setreuid32 : uintptr : 203
+ SYS_setregid32 : uintptr : 204
+ SYS_getgroups32 : uintptr : 205
+ SYS_setgroups32 : uintptr : 206
+ SYS_fchown32 : uintptr : 207
+ SYS_setresuid32 : uintptr : 208
+ SYS_getresuid32 : uintptr : 209
+ SYS_setresgid32 : uintptr : 210
+ SYS_getresgid32 : uintptr : 211
+ SYS_chown32 : uintptr : 212
+ SYS_setuid32 : uintptr : 213
+ SYS_setgid32 : uintptr : 214
+ SYS_setfsuid32 : uintptr : 215
+ SYS_setfsgid32 : uintptr : 216
+ SYS_pivot_root : uintptr : 217
+ SYS_mincore : uintptr : 218
+ SYS_madvise : uintptr : 219
+ SYS_getdents64 : uintptr : 220
+ SYS_fcntl64 : uintptr : 221
+ SYS_gettid : uintptr : 224
+ SYS_readahead : uintptr : 225
+ SYS_setxattr : uintptr : 226
+ SYS_lsetxattr : uintptr : 227
+ SYS_fsetxattr : uintptr : 228
+ SYS_getxattr : uintptr : 229
+ SYS_lgetxattr : uintptr : 230
+ SYS_fgetxattr : uintptr : 231
+ SYS_listxattr : uintptr : 232
+ SYS_llistxattr : uintptr : 233
+ SYS_flistxattr : uintptr : 234
+ SYS_removexattr : uintptr : 235
+ SYS_lremovexattr : uintptr : 236
+ SYS_fremovexattr : uintptr : 237
+ SYS_tkill : uintptr : 238
+ SYS_sendfile64 : uintptr : 239
+ SYS_futex : uintptr : 240
+ SYS_sched_setaffinity : uintptr : 241
+ SYS_sched_getaffinity : uintptr : 242
+ SYS_set_thread_area : uintptr : 243
+ SYS_get_thread_area : uintptr : 244
+ SYS_io_setup : uintptr : 245
+ SYS_io_destroy : uintptr : 246
+ SYS_io_getevents : uintptr : 247
+ SYS_io_submit : uintptr : 248
+ SYS_io_cancel : uintptr : 249
+ SYS_fadvise64 : uintptr : 250
+ SYS_exit_group : uintptr : 252
+ SYS_lookup_dcookie : uintptr : 253
+ SYS_epoll_create : uintptr : 254
+ SYS_epoll_ctl : uintptr : 255
+ SYS_epoll_wait : uintptr : 256
+ SYS_remap_file_pages : uintptr : 257
+ SYS_set_tid_address : uintptr : 258
+ SYS_timer_create : uintptr : 259
+ SYS_timer_settime : uintptr : 260
+ SYS_timer_gettime : uintptr : 261
+ SYS_timer_getoverrun : uintptr : 262
+ SYS_timer_delete : uintptr : 263
+ SYS_clock_settime : uintptr : 264
+ SYS_clock_gettime : uintptr : 265
+ SYS_clock_getres : uintptr : 266
+ SYS_clock_nanosleep : uintptr : 267
+ SYS_statfs64 : uintptr : 268
+ SYS_fstatfs64 : uintptr : 269
+ SYS_tgkill : uintptr : 270
+ SYS_utimes : uintptr : 271
+ SYS_fadvise64_64 : uintptr : 272
+ SYS_vserver : uintptr : 273
+ SYS_mbind : uintptr : 274
+ SYS_get_mempolicy : uintptr : 275
+ SYS_set_mempolicy : uintptr : 276
+ SYS_mq_open : uintptr : 277
+ SYS_mq_unlink : uintptr : 278
+ SYS_mq_timedsend : uintptr : 279
+ SYS_mq_timedreceive : uintptr : 280
+ SYS_mq_notify : uintptr : 281
+ SYS_mq_getsetattr : uintptr : 282
+ SYS_kexec_load : uintptr : 283
+ SYS_waitid : uintptr : 284
+ SYS_add_key : uintptr : 286
+ SYS_request_key : uintptr : 287
+ SYS_keyctl : uintptr : 288
+ SYS_ioprio_set : uintptr : 289
+ SYS_ioprio_get : uintptr : 290
+ SYS_inotify_init : uintptr : 291
+ SYS_inotify_add_watch : uintptr : 292
+ SYS_inotify_rm_watch : uintptr : 293
+ SYS_migrate_pages : uintptr : 294
+ SYS_openat : uintptr : 295
+ SYS_mkdirat : uintptr : 296
+ SYS_mknodat : uintptr : 297
+ SYS_fchownat : uintptr : 298
+ SYS_futimesat : uintptr : 299
+ SYS_fstatat64 : uintptr : 300
+ SYS_unlinkat : uintptr : 301
+ SYS_renameat : uintptr : 302
+ SYS_linkat : uintptr : 303
+ SYS_symlinkat : uintptr : 304
+ SYS_readlinkat : uintptr : 305
+ SYS_fchmodat : uintptr : 306
+ SYS_faccessat : uintptr : 307
+ SYS_pselect6 : uintptr : 308
+ SYS_ppoll : uintptr : 309
+ SYS_unshare : uintptr : 310
+ SYS_set_robust_list : uintptr : 311
+ SYS_get_robust_list : uintptr : 312
+ SYS_splice : uintptr : 313
+ SYS_sync_file_range : uintptr : 314
+ SYS_tee : uintptr : 315
+ SYS_vmsplice : uintptr : 316
+ SYS_move_pages : uintptr : 317
+ SYS_getcpu : uintptr : 318
+ SYS_epoll_pwait : uintptr : 319
+ SYS_utimensat : uintptr : 320
+ SYS_signalfd : uintptr : 321
+ SYS_timerfd_create : uintptr : 322
+ SYS_eventfd : uintptr : 323
+ SYS_fallocate : uintptr : 324
+ SYS_timerfd_settime : uintptr : 325
+ SYS_timerfd_gettime : uintptr : 326
+ SYS_signalfd4 : uintptr : 327
+ SYS_eventfd2 : uintptr : 328
+ SYS_epoll_create1 : uintptr : 329
+ SYS_dup3 : uintptr : 330
+ SYS_pipe2 : uintptr : 331
+ SYS_inotify_init1 : uintptr : 332
+ SYS_preadv : uintptr : 333
+ SYS_pwritev : uintptr : 334
+ SYS_rt_tgsigqueueinfo : uintptr : 335
+ SYS_perf_event_open : uintptr : 336
+ SYS_recvmmsg : uintptr : 337
+ SYS_fanotify_init : uintptr : 338
+ SYS_fanotify_mark : uintptr : 339
+ SYS_prlimit64 : uintptr : 340
+ SYS_name_to_handle_at : uintptr : 341
+ SYS_open_by_handle_at : uintptr : 342
+ SYS_clock_adjtime : uintptr : 343
+ SYS_syncfs : uintptr : 344
+ SYS_sendmmsg : uintptr : 345
+ SYS_setns : uintptr : 346
+ SYS_process_vm_readv : uintptr : 347
+ SYS_process_vm_writev : uintptr : 348
+ SYS_kcmp : uintptr : 349
+ SYS_finit_module : uintptr : 350
+ SYS_sched_setattr : uintptr : 351
+ SYS_sched_getattr : uintptr : 352
+ SYS_renameat2 : uintptr : 353
+ SYS_seccomp : uintptr : 354
+ SYS_getrandom : uintptr : 355
+ SYS_memfd_create : uintptr : 356
+ SYS_bpf : uintptr : 357
+ SYS_execveat : uintptr : 358
+ SYS_socket : uintptr : 359
+ SYS_socketpair : uintptr : 360
+ SYS_bind : uintptr : 361
+ SYS_connect : uintptr : 362
+ SYS_listen : uintptr : 363
+ SYS_accept4 : uintptr : 364
+ SYS_getsockopt : uintptr : 365
+ SYS_setsockopt : uintptr : 366
+ SYS_getsockname : uintptr : 367
+ SYS_getpeername : uintptr : 368
+ SYS_sendto : uintptr : 369
+ SYS_sendmsg : uintptr : 370
+ SYS_recvfrom : uintptr : 371
+ SYS_recvmsg : uintptr : 372
+ SYS_shutdown : uintptr : 373
+ SYS_userfaultfd : uintptr : 374
+ SYS_membarrier : uintptr : 375
+ SYS_mlock2 : uintptr : 376
+ SYS_copy_file_range : uintptr : 377
+ SYS_preadv2 : uintptr : 378
+ SYS_pwritev2 : uintptr : 379
+ SYS_pkey_mprotect : uintptr : 380
+ SYS_pkey_alloc : uintptr : 381
+ SYS_pkey_free : uintptr : 382
+ SYS_statx : uintptr : 383
+ SYS_arch_prctl : uintptr : 384
+ SYS_io_pgetevents : uintptr : 385
+ SYS_rseq : uintptr : 386
+ SYS_semget : uintptr : 393
+ SYS_semctl : uintptr : 394
+ SYS_shmget : uintptr : 395
+ SYS_shmctl : uintptr : 396
+ SYS_shmat : uintptr : 397
+ SYS_shmdt : uintptr : 398
+ SYS_msgget : uintptr : 399
+ SYS_msgsnd : uintptr : 400
+ SYS_msgrcv : uintptr : 401
+ SYS_msgctl : uintptr : 402
+ SYS_clock_gettime64 : uintptr : 403
+ SYS_clock_settime64 : uintptr : 404
+ SYS_clock_adjtime64 : uintptr : 405
+ SYS_clock_getres_time64 : uintptr : 406
+ SYS_clock_nanosleep_time64 : uintptr : 407
+ SYS_timer_gettime64 : uintptr : 408
+ SYS_timer_settime64 : uintptr : 409
+ SYS_timerfd_gettime64 : uintptr : 410
+ SYS_timerfd_settime64 : uintptr : 411
+ SYS_utimensat_time64 : uintptr : 412
+ SYS_pselect6_time64 : uintptr : 413
+ SYS_ppoll_time64 : uintptr : 414
+ SYS_io_pgetevents_time64 : uintptr : 416
+ SYS_recvmmsg_time64 : uintptr : 417
+ SYS_mq_timedsend_time64 : uintptr : 418
+ SYS_mq_timedreceive_time64 : uintptr : 419
+ SYS_semtimedop_time64 : uintptr : 420
+ SYS_rt_sigtimedwait_time64 : uintptr : 421
+ SYS_futex_time64 : uintptr : 422
+ SYS_sched_rr_get_interval_time64 : uintptr : 423
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+ SYS_memfd_secret : uintptr : 447
+} else when ODIN_ARCH == .arm32 { // TODO
+ SYS_restart_syscall : uintptr : 0
+ SYS_exit : uintptr : 1
+ SYS_fork : uintptr : 2
+ SYS_read : uintptr : 3
+ SYS_write : uintptr : 4
+ SYS_open : uintptr : 5
+ SYS_close : uintptr : 6
+ SYS_creat : uintptr : 8
+ SYS_link : uintptr : 9
+ SYS_unlink : uintptr : 10
+ SYS_execve : uintptr : 11
+ SYS_chdir : uintptr : 12
+ SYS_mknod : uintptr : 14
+ SYS_chmod : uintptr : 15
+ SYS_lchown : uintptr : 16
+ SYS_lseek : uintptr : 19
+ SYS_getpid : uintptr : 20
+ SYS_mount : uintptr : 21
+ SYS_setuid : uintptr : 23
+ SYS_getuid : uintptr : 24
+ SYS_ptrace : uintptr : 26
+ SYS_pause : uintptr : 29
+ SYS_access : uintptr : 33
+ SYS_nice : uintptr : 34
+ SYS_sync : uintptr : 36
+ SYS_kill : uintptr : 37
+ SYS_rename : uintptr : 38
+ SYS_mkdir : uintptr : 39
+ SYS_rmdir : uintptr : 40
+ SYS_dup : uintptr : 41
+ SYS_pipe : uintptr : 42
+ SYS_times : uintptr : 43
+ SYS_brk : uintptr : 45
+ SYS_setgid : uintptr : 46
+ SYS_getgid : uintptr : 47
+ SYS_geteuid : uintptr : 49
+ SYS_getegid : uintptr : 50
+ SYS_acct : uintptr : 51
+ SYS_umount2 : uintptr : 52
+ SYS_ioctl : uintptr : 54
+ SYS_fcntl : uintptr : 55
+ SYS_setpgid : uintptr : 57
+ SYS_umask : uintptr : 60
+ SYS_chroot : uintptr : 61
+ SYS_ustat : uintptr : 62
+ SYS_dup2 : uintptr : 63
+ SYS_getppid : uintptr : 64
+ SYS_getpgrp : uintptr : 65
+ SYS_setsid : uintptr : 66
+ SYS_sigaction : uintptr : 67
+ SYS_setreuid : uintptr : 70
+ SYS_setregid : uintptr : 71
+ SYS_sigsuspend : uintptr : 72
+ SYS_sigpending : uintptr : 73
+ SYS_sethostname : uintptr : 74
+ SYS_setrlimit : uintptr : 75
+ SYS_getrusage : uintptr : 77
+ SYS_gettimeofday : uintptr : 78
+ SYS_settimeofday : uintptr : 79
+ SYS_getgroups : uintptr : 80
+ SYS_setgroups : uintptr : 81
+ SYS_symlink : uintptr : 83
+ SYS_readlink : uintptr : 85
+ SYS_uselib : uintptr : 86
+ SYS_swapon : uintptr : 87
+ SYS_reboot : uintptr : 88
+ SYS_munmap : uintptr : 91
+ SYS_truncate : uintptr : 92
+ SYS_ftruncate : uintptr : 93
+ SYS_fchmod : uintptr : 94
+ SYS_fchown : uintptr : 95
+ SYS_getpriority : uintptr : 96
+ SYS_setpriority : uintptr : 97
+ SYS_statfs : uintptr : 99
+ SYS_fstatfs : uintptr : 100
+ SYS_syslog : uintptr : 103
+ SYS_setitimer : uintptr : 104
+ SYS_getitimer : uintptr : 105
+ SYS_stat : uintptr : 106
+ SYS_lstat : uintptr : 107
+ SYS_fstat : uintptr : 108
+ SYS_vhangup : uintptr : 111
+ SYS_wait4 : uintptr : 114
+ SYS_swapoff : uintptr : 115
+ SYS_sysinfo : uintptr : 116
+ SYS_fsync : uintptr : 118
+ SYS_sigreturn : uintptr : 119
+ SYS_clone : uintptr : 120
+ SYS_setdomainname : uintptr : 121
+ SYS_uname : uintptr : 122
+ SYS_adjtimex : uintptr : 124
+ SYS_mprotect : uintptr : 125
+ SYS_sigprocmask : uintptr : 126
+ SYS_init_module : uintptr : 128
+ SYS_delete_module : uintptr : 129
+ SYS_quotactl : uintptr : 131
+ SYS_getpgid : uintptr : 132
+ SYS_fchdir : uintptr : 133
+ SYS_bdflush : uintptr : 134
+ SYS_sysfs : uintptr : 135
+ SYS_personality : uintptr : 136
+ SYS_setfsuid : uintptr : 138
+ SYS_setfsgid : uintptr : 139
+ SYS__llseek : uintptr : 140
+ SYS_getdents : uintptr : 141
+ SYS__newselect : uintptr : 142
+ SYS_flock : uintptr : 143
+ SYS_msync : uintptr : 144
+ SYS_readv : uintptr : 145
+ SYS_writev : uintptr : 146
+ SYS_getsid : uintptr : 147
+ SYS_fdatasync : uintptr : 148
+ SYS__sysctl : uintptr : 149
+ SYS_mlock : uintptr : 150
+ SYS_munlock : uintptr : 151
+ SYS_mlockall : uintptr : 152
+ SYS_munlockall : uintptr : 153
+ SYS_sched_setparam : uintptr : 154
+ SYS_sched_getparam : uintptr : 155
+ SYS_sched_setscheduler : uintptr : 156
+ SYS_sched_getscheduler : uintptr : 157
+ SYS_sched_yield : uintptr : 158
+ SYS_sched_get_priority_max : uintptr : 159
+ SYS_sched_get_priority_min : uintptr : 160
+ SYS_sched_rr_get_interval : uintptr : 161
+ SYS_nanosleep : uintptr : 162
+ SYS_mremap : uintptr : 163
+ SYS_setresuid : uintptr : 164
+ SYS_getresuid : uintptr : 165
+ SYS_poll : uintptr : 168
+ SYS_nfsservctl : uintptr : 169
+ SYS_setresgid : uintptr : 170
+ SYS_getresgid : uintptr : 171
+ SYS_prctl : uintptr : 172
+ SYS_rt_sigreturn : uintptr : 173
+ SYS_rt_sigaction : uintptr : 174
+ SYS_rt_sigprocmask : uintptr : 175
+ SYS_rt_sigpending : uintptr : 176
+ SYS_rt_sigtimedwait : uintptr : 177
+ SYS_rt_sigqueueinfo : uintptr : 178
+ SYS_rt_sigsuspend : uintptr : 179
+ SYS_pread64 : uintptr : 180
+ SYS_pwrite64 : uintptr : 181
+ SYS_chown : uintptr : 182
+ SYS_getcwd : uintptr : 183
+ SYS_capget : uintptr : 184
+ SYS_capset : uintptr : 185
+ SYS_sigaltstack : uintptr : 186
+ SYS_sendfile : uintptr : 187
+ SYS_vfork : uintptr : 190
+ SYS_ugetrlimit : uintptr : 191
+ SYS_mmap : uintptr : 192 // actually mmap2
+ SYS_truncate64 : uintptr : 193
+ SYS_ftruncate64 : uintptr : 194
+ SYS_stat64 : uintptr : 195
+ SYS_lstat64 : uintptr : 196
+ SYS_fstat64 : uintptr : 197
+ SYS_lchown32 : uintptr : 198
+ SYS_getuid32 : uintptr : 199
+ SYS_getgid32 : uintptr : 200
+ SYS_geteuid32 : uintptr : 201
+ SYS_getegid32 : uintptr : 202
+ SYS_setreuid32 : uintptr : 203
+ SYS_setregid32 : uintptr : 204
+ SYS_getgroups32 : uintptr : 205
+ SYS_setgroups32 : uintptr : 206
+ SYS_fchown32 : uintptr : 207
+ SYS_setresuid32 : uintptr : 208
+ SYS_getresuid32 : uintptr : 209
+ SYS_setresgid32 : uintptr : 210
+ SYS_getresgid32 : uintptr : 211
+ SYS_chown32 : uintptr : 212
+ SYS_setuid32 : uintptr : 213
+ SYS_setgid32 : uintptr : 214
+ SYS_setfsuid32 : uintptr : 215
+ SYS_setfsgid32 : uintptr : 216
+ SYS_getdents64 : uintptr : 217
+ SYS_pivot_root : uintptr : 218
+ SYS_mincore : uintptr : 219
+ SYS_madvise : uintptr : 220
+ SYS_fcntl64 : uintptr : 221
+ SYS_gettid : uintptr : 224
+ SYS_readahead : uintptr : 225
+ SYS_setxattr : uintptr : 226
+ SYS_lsetxattr : uintptr : 227
+ SYS_fsetxattr : uintptr : 228
+ SYS_getxattr : uintptr : 229
+ SYS_lgetxattr : uintptr : 230
+ SYS_fgetxattr : uintptr : 231
+ SYS_listxattr : uintptr : 232
+ SYS_llistxattr : uintptr : 233
+ SYS_flistxattr : uintptr : 234
+ SYS_removexattr : uintptr : 235
+ SYS_lremovexattr : uintptr : 236
+ SYS_fremovexattr : uintptr : 237
+ SYS_tkill : uintptr : 238
+ SYS_sendfile64 : uintptr : 239
+ SYS_futex : uintptr : 240
+ SYS_sched_setaffinity : uintptr : 241
+ SYS_sched_getaffinity : uintptr : 242
+ SYS_io_setup : uintptr : 243
+ SYS_io_destroy : uintptr : 244
+ SYS_io_getevents : uintptr : 245
+ SYS_io_submit : uintptr : 246
+ SYS_io_cancel : uintptr : 247
+ SYS_exit_group : uintptr : 248
+ SYS_lookup_dcookie : uintptr : 249
+ SYS_epoll_create : uintptr : 250
+ SYS_epoll_ctl : uintptr : 251
+ SYS_epoll_wait : uintptr : 252
+ SYS_remap_file_pages : uintptr : 253
+ SYS_set_tid_address : uintptr : 256
+ SYS_timer_create : uintptr : 257
+ SYS_timer_settime : uintptr : 258
+ SYS_timer_gettime : uintptr : 259
+ SYS_timer_getoverrun : uintptr : 260
+ SYS_timer_delete : uintptr : 261
+ SYS_clock_settime : uintptr : 262
+ SYS_clock_gettime : uintptr : 263
+ SYS_clock_getres : uintptr : 264
+ SYS_clock_nanosleep : uintptr : 265
+ SYS_statfs64 : uintptr : 266
+ SYS_fstatfs64 : uintptr : 267
+ SYS_tgkill : uintptr : 268
+ SYS_utimes : uintptr : 269
+ SYS_fadvise64_64 : uintptr : 270
+ SYS_pciconfig_iobase : uintptr : 271
+ SYS_pciconfig_read : uintptr : 272
+ SYS_pciconfig_write : uintptr : 273
+ SYS_mq_open : uintptr : 274
+ SYS_mq_unlink : uintptr : 275
+ SYS_mq_timedsend : uintptr : 276
+ SYS_mq_timedreceive : uintptr : 277
+ SYS_mq_notify : uintptr : 278
+ SYS_mq_getsetattr : uintptr : 279
+ SYS_waitid : uintptr : 280
+ SYS_socket : uintptr : 281
+ SYS_bind : uintptr : 282
+ SYS_connect : uintptr : 283
+ SYS_listen : uintptr : 284
+ SYS_accept : uintptr : 285
+ SYS_getsockname : uintptr : 286
+ SYS_getpeername : uintptr : 287
+ SYS_socketpair : uintptr : 288
+ SYS_send : uintptr : 289
+ SYS_sendto : uintptr : 290
+ SYS_recv : uintptr : 291
+ SYS_recvfrom : uintptr : 292
+ SYS_shutdown : uintptr : 293
+ SYS_setsockopt : uintptr : 294
+ SYS_getsockopt : uintptr : 295
+ SYS_sendmsg : uintptr : 296
+ SYS_recvmsg : uintptr : 297
+ SYS_semop : uintptr : 298
+ SYS_semget : uintptr : 299
+ SYS_semctl : uintptr : 300
+ SYS_msgsnd : uintptr : 301
+ SYS_msgrcv : uintptr : 302
+ SYS_msgget : uintptr : 303
+ SYS_msgctl : uintptr : 304
+ SYS_shmat : uintptr : 305
+ SYS_shmdt : uintptr : 306
+ SYS_shmget : uintptr : 307
+ SYS_shmctl : uintptr : 308
+ SYS_add_key : uintptr : 309
+ SYS_request_key : uintptr : 310
+ SYS_keyctl : uintptr : 311
+ SYS_semtimedop : uintptr : 312
+ SYS_vserver : uintptr : 313
+ SYS_ioprio_set : uintptr : 314
+ SYS_ioprio_get : uintptr : 315
+ SYS_inotify_init : uintptr : 316
+ SYS_inotify_add_watch : uintptr : 317
+ SYS_inotify_rm_watch : uintptr : 318
+ SYS_mbind : uintptr : 319
+ SYS_get_mempolicy : uintptr : 320
+ SYS_set_mempolicy : uintptr : 321
+ SYS_openat : uintptr : 322
+ SYS_mkdirat : uintptr : 323
+ SYS_mknodat : uintptr : 324
+ SYS_fchownat : uintptr : 325
+ SYS_futimesat : uintptr : 326
+ SYS_fstatat64 : uintptr : 327
+ SYS_unlinkat : uintptr : 328
+ SYS_renameat : uintptr : 329
+ SYS_linkat : uintptr : 330
+ SYS_symlinkat : uintptr : 331
+ SYS_readlinkat : uintptr : 332
+ SYS_fchmodat : uintptr : 333
+ SYS_faccessat : uintptr : 334
+ SYS_pselect6 : uintptr : 335
+ SYS_ppoll : uintptr : 336
+ SYS_unshare : uintptr : 337
+ SYS_set_robust_list : uintptr : 338
+ SYS_get_robust_list : uintptr : 339
+ SYS_splice : uintptr : 340
+ SYS_sync_file_range : uintptr : 341
+ SYS_tee : uintptr : 342
+ SYS_vmsplice : uintptr : 343
+ SYS_move_pages : uintptr : 344
+ SYS_getcpu : uintptr : 345
+ SYS_epoll_pwait : uintptr : 346
+ SYS_kexec_load : uintptr : 347
+ SYS_utimensat : uintptr : 348
+ SYS_signalfd : uintptr : 349
+ SYS_timerfd_create : uintptr : 350
+ SYS_eventfd : uintptr : 351
+ SYS_fallocate : uintptr : 352
+ SYS_timerfd_settime : uintptr : 353
+ SYS_timerfd_gettime : uintptr : 354
+ SYS_signalfd4 : uintptr : 355
+ SYS_eventfd2 : uintptr : 356
+ SYS_epoll_create1 : uintptr : 357
+ SYS_dup3 : uintptr : 358
+ SYS_pipe2 : uintptr : 359
+ SYS_inotify_init1 : uintptr : 360
+ SYS_preadv : uintptr : 361
+ SYS_pwritev : uintptr : 362
+ SYS_rt_tgsigqueueinfo : uintptr : 363
+ SYS_perf_event_open : uintptr : 364
+ SYS_recvmmsg : uintptr : 365
+ SYS_accept4 : uintptr : 366
+ SYS_fanotify_init : uintptr : 367
+ SYS_fanotify_mark : uintptr : 368
+ SYS_prlimit64 : uintptr : 369
+ SYS_name_to_handle_at : uintptr : 370
+ SYS_open_by_handle_at : uintptr : 371
+ SYS_clock_adjtime : uintptr : 372
+ SYS_syncfs : uintptr : 373
+ SYS_sendmmsg : uintptr : 374
+ SYS_setns : uintptr : 375
+ SYS_process_vm_readv : uintptr : 376
+ SYS_process_vm_writev : uintptr : 377
+ SYS_kcmp : uintptr : 378
+ SYS_finit_module : uintptr : 379
+ SYS_sched_setattr : uintptr : 380
+ SYS_sched_getattr : uintptr : 381
+ SYS_renameat2 : uintptr : 382
+ SYS_seccomp : uintptr : 383
+ SYS_getrandom : uintptr : 384
+ SYS_memfd_create : uintptr : 385
+ SYS_bpf : uintptr : 386
+ SYS_execveat : uintptr : 387
+ SYS_userfaultfd : uintptr : 388
+ SYS_membarrier : uintptr : 389
+ SYS_mlock2 : uintptr : 390
+ SYS_copy_file_range : uintptr : 391
+ SYS_preadv2 : uintptr : 392
+ SYS_pwritev2 : uintptr : 393
+ SYS_pkey_mprotect : uintptr : 394
+ SYS_pkey_alloc : uintptr : 395
+ SYS_pkey_free : uintptr : 396
+ SYS_statx : uintptr : 397
+ SYS_rseq : uintptr : 398
+ SYS_io_pgetevents : uintptr : 399
+ SYS_migrate_pages : uintptr : 400
+ SYS_kexec_file_load : uintptr : 401
+ SYS_clock_gettime64 : uintptr : 403
+ SYS_clock_settime64 : uintptr : 404
+ SYS_clock_adjtime64 : uintptr : 405
+ SYS_clock_getres_time64 : uintptr : 406
+ SYS_clock_nanosleep_time64 : uintptr : 407
+ SYS_timer_gettime64 : uintptr : 408
+ SYS_timer_settime64 : uintptr : 409
+ SYS_timerfd_gettime64 : uintptr : 410
+ SYS_timerfd_settime64 : uintptr : 411
+ SYS_utimensat_time64 : uintptr : 412
+ SYS_pselect6_time64 : uintptr : 413
+ SYS_ppoll_time64 : uintptr : 414
+ SYS_io_pgetevents_time64 : uintptr : 416
+ SYS_recvmmsg_time64 : uintptr : 417
+ SYS_mq_timedsend_time64 : uintptr : 418
+ SYS_mq_timedreceive_time64 : uintptr : 419
+ SYS_semtimedop_time64 : uintptr : 420
+ SYS_rt_sigtimedwait_time64 : uintptr : 421
+ SYS_futex_time64 : uintptr : 422
+ SYS_sched_rr_get_interval_time64 : uintptr : 423
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+} else {
+ #panic("Unsupported architecture")
+}
+
+// syscall related constants
+AT_FDCWD :: ~uintptr(99)
+AT_REMOVEDIR :: uintptr(0x200)
+AT_SYMLINK_FOLLOW :: uintptr(0x400)
+AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
+
+// mmap flags
+PROT_NONE :: 0x0
+PROT_READ :: 0x1
+PROT_WRITE :: 0x2
+PROT_EXEC :: 0x4
+PROT_GROWSDOWN :: 0x01000000
+PROT_GROWSUP :: 0x02000000
+
+MAP_FIXED :: 0x10
+MAP_SHARED :: 0x1
+MAP_PRIVATE :: 0x2
+MAP_SHARED_VALIDATE :: 0x3
+MAP_ANONYMOUS :: 0x20
+
+// mremap flags
+MREMAP_MAYMOVE :: 1
+MREMAP_FIXED :: 2
+MREMAP_DONTUNMAP :: 4
+
+// madvise flags
+MADV_NORMAL :: 0
+MADV_RANDOM :: 1
+MADV_SEQUENTIAL :: 2
+MADV_WILLNEED :: 3
+MADV_DONTNEED :: 4
+MADV_FREE :: 8
+MADV_REMOVE :: 9
+MADV_DONTFORK :: 10
+MADV_DOFORK :: 11
+MADV_MERGEABLE :: 12
+MADV_UNMERGEABLE :: 13
+MADV_HUGEPAGE :: 14
+MADV_NOHUGEPAGE :: 15
+MADV_DONTDUMP :: 16
+MADV_DODUMP :: 17
+MADV_WIPEONFORK :: 18
+MADV_KEEPONFORK :: 19
+MADV_HWPOISON :: 100
+
+sys_gettid :: proc "contextless" () -> int {
+ return cast(int)intrinsics.syscall(SYS_gettid)
+}
+
+sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
+ return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags))
+}
+
+sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+ } else { // NOTE: arm64 does not have open
+ return int(intrinsics.syscall(SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+ }
+}
+
+sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int {
+ return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
+}
+
+sys_close :: proc "contextless" (fd: int) -> int {
+ return int(intrinsics.syscall(SYS_close, uintptr(fd)))
+}
+
+sys_read :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
+}
+
+sys_pread :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
+ } else {
+ low := uintptr(offset & 0xFFFFFFFF)
+ high := uintptr(offset >> 32)
+ return int(intrinsics.syscall(SYS_pread64, uintptr(fd), uintptr(buf), uintptr(size), high, low))
+ }
+}
+
+sys_write :: proc "contextless" (fd: int, buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
+}
+
+sys_pwrite :: proc "contextless" (fd: int, buf: rawptr, size: uint, offset: i64) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), uintptr(offset)))
+ } else {
+ low := uintptr(offset & 0xFFFFFFFF)
+ high := uintptr(offset >> 32)
+ return int(intrinsics.syscall(SYS_pwrite64, uintptr(fd), uintptr(buf), uintptr(size), high, low))
+ }
+}
+
+sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return i64(intrinsics.syscall(SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
+ } else {
+ low := uintptr(offset & 0xFFFFFFFF)
+ high := uintptr(offset >> 32)
+ result: i64
+ res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
+ return res if res < 0 else result
+ }
+}
+
+sys_stat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
+ when ODIN_ARCH == .amd64 {
+ return int(intrinsics.syscall(SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
+ } else when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
+ } else { // NOTE: arm64 does not have stat
+ return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
+ }
+}
+
+sys_fstat :: proc "contextless" (fd: int, stat: rawptr) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(SYS_fstat, uintptr(fd), uintptr(stat)))
+ } else {
+ return int(intrinsics.syscall(SYS_fstat64, uintptr(fd), uintptr(stat)))
+ }
+}
+
+sys_lstat :: proc "contextless" (path: cstring, stat: rawptr) -> int {
+ when ODIN_ARCH == .amd64 {
+ return int(intrinsics.syscall(SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
+ } else when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
+ } else { // NOTE: arm64 does not have any lstat
+ return int(intrinsics.syscall(SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
+ }
+}
+
+sys_readlink :: proc "contextless" (path: cstring, buf: rawptr, bufsiz: uint) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
+ } else { // NOTE: arm64 does not have readlink
+ return int(intrinsics.syscall(SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
+ }
+}
+
+sys_symlink :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_symlink, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
+ } else { // NOTE: arm64 does not have symlink
+ return int(intrinsics.syscall(SYS_symlinkat, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name))))
+ }
+}
+
+sys_access :: proc "contextless" (path: cstring, mask: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_access, uintptr(rawptr(path)), uintptr(mask)))
+ } else { // NOTE: arm64 does not have access
+ return int(intrinsics.syscall(SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
+ }
+}
+
+sys_getcwd :: proc "contextless" (buf: rawptr, size: uint) -> int {
+ return int(intrinsics.syscall(SYS_getcwd, uintptr(buf), uintptr(size)))
+}
+
+sys_chdir :: proc "contextless" (path: cstring) -> int {
+ return int(intrinsics.syscall(SYS_chdir, uintptr(rawptr(path))))
+}
+
+sys_fchdir :: proc "contextless" (fd: int) -> int {
+ return int(intrinsics.syscall(SYS_fchdir, uintptr(fd)))
+}
+
+sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
+ } else { // NOTE: arm64 does not have chmod
+ return int(intrinsics.syscall(SYS_fchmodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
+ }
+}
+
+sys_fchmod :: proc "contextless" (fd: int, mode: int) -> int {
+ return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode)))
+}
+
+sys_chown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_chown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
+ } else { // NOTE: arm64 does not have chown
+ return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), 0))
+ }
+}
+
+sys_fchown :: proc "contextless" (fd: int, user: int, group: int) -> int {
+ return int(intrinsics.syscall(SYS_fchown, uintptr(fd), uintptr(user), uintptr(group)))
+}
+
+sys_lchown :: proc "contextless" (path: cstring, user: int, group: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_lchown, uintptr(rawptr(path)), uintptr(user), uintptr(group)))
+ } else { // NOTE: arm64 does not have lchown
+ return int(intrinsics.syscall(SYS_fchownat, AT_FDCWD, uintptr(rawptr(path)), uintptr(user), uintptr(group), AT_SYMLINK_NOFOLLOW))
+ }
+}
+
+sys_rename :: proc "contextless" (old, new: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
+ } else { // NOTE: arm64 does not have rename
+ return int(intrinsics.syscall(SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
+ }
+}
+
+sys_link :: proc "contextless" (old_name: cstring, new_name: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_link, uintptr(rawptr(old_name)), uintptr(rawptr(new_name))))
+ } else { // NOTE: arm64 does not have link
+ return int(intrinsics.syscall(SYS_linkat, AT_FDCWD, uintptr(rawptr(old_name)), AT_FDCWD, uintptr(rawptr(new_name)), AT_SYMLINK_FOLLOW))
+ }
+}
+
+sys_unlink :: proc "contextless" (path: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_unlink, uintptr(rawptr(path))))
+ } else { // NOTE: arm64 does not have unlink
+ return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
+ }
+}
+
+sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
+ return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag))
+}
+
+sys_rmdir :: proc "contextless" (path: cstring) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_rmdir, uintptr(rawptr(path))))
+ } else { // NOTE: arm64 does not have rmdir
+ return int(intrinsics.syscall(SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
+ }
+}
+
+sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
+ } else { // NOTE: arm64 does not have mkdir
+ return int(intrinsics.syscall(SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
+ }
+}
+
+sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int {
+ return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode)))
+}
+
+sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
+ } else { // NOTE: arm64 does not have mknod
+ return int(intrinsics.syscall(SYS_mknodat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
+ }
+}
+
+sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int {
+ return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
+}
+
+sys_truncate :: proc "contextless" (path: cstring, length: i64) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
+ } else {
+ low := uintptr(length & 0xFFFFFFFF)
+ high := uintptr(length >> 32)
+ return int(intrinsics.syscall(SYS_truncate64, uintptr(rawptr(path)), high, low))
+ }
+}
+
+sys_ftruncate :: proc "contextless" (fd: int, length: i64) -> int {
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ return int(intrinsics.syscall(SYS_ftruncate, uintptr(fd), uintptr(length)))
+ } else {
+ low := uintptr(length & 0xFFFFFFFF)
+ high := uintptr(length >> 32)
+ return int(intrinsics.syscall(SYS_ftruncate64, uintptr(fd), high, low))
+ }
+}
+
+sys_fsync :: proc "contextless" (fd: int) -> int {
+ return int(intrinsics.syscall(SYS_fsync, uintptr(fd)))
+}
+
+sys_getdents64 :: proc "contextless" (fd: int, dirent: rawptr, count: int) -> int {
+ return int(intrinsics.syscall(SYS_getdents64, uintptr(fd), uintptr(dirent), uintptr(count)))
+}
+
+sys_fork :: proc "contextless" () -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_fork))
+ } else {
+ return int(intrinsics.syscall(SYS_clone, SIGCHLD))
+ }
+}
+
+sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int {
+ return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset))
+}
+
+sys_mremap :: proc "contextless" (addr: rawptr, old_length, new_length: uint, flags: int, new_addr: rawptr = nil) -> int {
+ return int(intrinsics.syscall(SYS_mremap, uintptr(addr), uintptr(old_length), uintptr(new_length), uintptr(flags), uintptr(new_addr)))
+}
+
+sys_munmap :: proc "contextless" (addr: rawptr, length: uint) -> int {
+ return int(intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length)))
+}
+
+sys_mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: int) -> int {
+ return int(intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot)))
+}
+
+sys_madvise :: proc "contextless" (addr: rawptr, length: uint, advice: int) -> int {
+ return int(intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice)))
+}
+
+
+// NOTE: Unsure about if this works directly on 32 bit archs. It may need 32 bit version of the time struct.
+// As of Linux 5.1, there is a utimensat_time64 function. Maybe use this in the future?
+sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, flags: int) -> int {
+ return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags)))
+}
+
+get_errno :: proc "contextless" (res: int) -> i32 {
+ if res < 0 && res > -4096 {
+ return i32(-res)
+ }
+ return 0
+}
diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin
new file mode 100644
index 000000000..fa3a7a29d
--- /dev/null
+++ b/core/sys/unix/time_unix.odin
@@ -0,0 +1,75 @@
+//+build linux, darwin, freebsd, openbsd
+package unix
+
+when ODIN_OS == .Darwin {
+ foreign import libc "System.framework"
+} else {
+ foreign import libc "system:c"
+}
+
+import "core:c"
+
+@(default_calling_convention="c")
+foreign libc {
+ clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int ---
+ sleep :: proc(seconds: c.uint) -> c.int ---
+ nanosleep :: proc(requested, remaining: ^timespec) -> c.int ---
+}
+
+timespec :: struct {
+ tv_sec: i64, // seconds
+ tv_nsec: i64, // nanoseconds
+}
+
+when ODIN_OS == .OpenBSD {
+ CLOCK_REALTIME :: 0
+ CLOCK_PROCESS_CPUTIME_ID :: 2
+ CLOCK_MONOTONIC :: 3
+ CLOCK_THREAD_CPUTIME_ID :: 4
+ CLOCK_UPTIME :: 5
+ CLOCK_BOOTTIME :: 6
+
+ // CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
+ CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
+} else {
+ CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
+ CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
+ CLOCK_PROCESS_CPUTIME_ID :: 2
+ CLOCK_THREAD_CPUTIME_ID :: 3
+ CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+ CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
+ CLOCK_MONOTONIC_COARSE :: 6
+ CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
+ CLOCK_REALTIME_ALARM :: 8
+ CLOCK_BOOTTIME_ALARM :: 9
+}
+
+// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
+// I do not know if Darwin programmers are used to the existance of these constants or not, so
+// I'm leaving aliases to them for now.
+CLOCK_SYSTEM :: CLOCK_REALTIME
+CLOCK_CALENDAR :: CLOCK_MONOTONIC
+
+boot_time_in_nanoseconds :: proc "c" () -> i64 {
+ ts_now, ts_boottime: timespec
+ clock_gettime(CLOCK_REALTIME, &ts_now)
+ clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
+
+ ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
+ return i64(ns)
+}
+
+seconds_since_boot :: proc "c" () -> f64 {
+ ts_boottime: timespec
+ clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
+ return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
+}
+
+
+inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) {
+ s, ns := nanoseconds / 1e9, nanoseconds % 1e9
+ requested := timespec{tv_sec=s, tv_nsec=ns}
+ res = nanosleep(&requested, &remaining)
+ return
+}
+
diff --git a/core/sys/win32/crt.odin b/core/sys/win32/crt.odin
deleted file mode 100644
index b39d35375..000000000
--- a/core/sys/win32/crt.odin
+++ /dev/null
@@ -1,15 +0,0 @@
-// +build windows
-package win32
-
-import "core:strings"
-
-foreign {
- @(link_name="_wgetcwd") _get_cwd_wide :: proc(buffer: Wstring, buf_len: int) -> ^Wstring ---
-}
-
-get_cwd :: proc(allocator := context.temp_allocator) -> string {
- buffer := make([]u16, MAX_PATH_WIDE, allocator)
- _get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE)
- file := utf16_to_utf8(buffer[:], allocator)
- return strings.trim_right_null(file)
-}
diff --git a/core/sys/win32/gdi32.odin b/core/sys/win32/gdi32.odin
deleted file mode 100644
index 13bc4796f..000000000
--- a/core/sys/win32/gdi32.odin
+++ /dev/null
@@ -1,26 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:gdi32.lib"
-
-WHITENESS :: 0x00FF0062
-BLACKNESS :: 0x00000042
-
-@(default_calling_convention = "std")
-foreign gdi32 {
- @(link_name="GetStockObject") get_stock_object :: proc(fn_object: i32) -> Hgdiobj ---
-
- @(link_name="StretchDIBits")
- stretch_dibits :: proc(hdc: Hdc,
- x_dst, y_dst, width_dst, height_dst: i32,
- x_src, y_src, width_src, header_src: i32,
- bits: rawptr, bits_info: ^Bitmap_Info,
- usage: u32,
- rop: u32) -> i32 ---
-
- @(link_name="SetPixelFormat") set_pixel_format :: proc(hdc: Hdc, pixel_format: i32, pfd: ^Pixel_Format_Descriptor) -> Bool ---
- @(link_name="ChoosePixelFormat") choose_pixel_format :: proc(hdc: Hdc, pfd: ^Pixel_Format_Descriptor) -> i32 ---
- @(link_name="SwapBuffers") swap_buffers :: proc(hdc: Hdc) -> Bool ---
-
- @(link_name="PatBlt") pat_blt :: proc(hdc: Hdc, x, y, w, h: i32, rop: u32) -> Bool ---
-}
diff --git a/core/sys/win32/general.odin b/core/sys/win32/general.odin
deleted file mode 100644
index d53bf8a4f..000000000
--- a/core/sys/win32/general.odin
+++ /dev/null
@@ -1,1176 +0,0 @@
-// +build windows
-package win32
-
-Uint_Ptr :: distinct uintptr
-Int_Ptr :: distinct int
-Long_Ptr :: distinct int
-
-Handle :: distinct rawptr
-Hwnd :: distinct Handle
-Hdc :: distinct Handle
-Hinstance :: distinct Handle
-Hicon :: distinct Handle
-Hcursor :: distinct Handle
-Hmenu :: distinct Handle
-Hbitmap :: distinct Handle
-Hbrush :: distinct Handle
-Hgdiobj :: distinct Handle
-Hmodule :: distinct Handle
-Hmonitor :: distinct Handle
-Hrawinput :: distinct Handle
-Hresult :: distinct i32
-HKL :: distinct Handle
-Wparam :: distinct Uint_Ptr
-Lparam :: distinct Long_Ptr
-Lresult :: distinct Long_Ptr
-Wnd_Proc :: distinct #type proc "std" (Hwnd, u32, Wparam, Lparam) -> Lresult
-Monitor_Enum_Proc :: distinct #type proc "std" (Hmonitor, Hdc, ^Rect, Lparam) -> bool
-
-
-
-Bool :: distinct b32
-
-Wstring :: distinct ^u16
-
-Point :: struct {
- x, y: i32,
-}
-
-Wnd_Class_A :: struct {
- style: u32,
- wnd_proc: Wnd_Proc,
- cls_extra, wnd_extra: i32,
- instance: Hinstance,
- icon: Hicon,
- cursor: Hcursor,
- background: Hbrush,
- menu_name, class_name: cstring,
-}
-
-Wnd_Class_W :: struct {
- style: u32,
- wnd_proc: Wnd_Proc,
- cls_extra, wnd_extra: i32,
- instance: Hinstance,
- icon: Hicon,
- cursor: Hcursor,
- background: Hbrush,
- menu_name, class_name: Wstring,
-}
-
-Wnd_Class_Ex_A :: struct {
- size, style: u32,
- wnd_proc: Wnd_Proc,
- cls_extra, wnd_extra: i32,
- instance: Hinstance,
- icon: Hicon,
- cursor: Hcursor,
- background: Hbrush,
- menu_name, class_name: cstring,
- sm: Hicon,
-}
-
-Wnd_Class_Ex_W :: struct {
- size, style: u32,
- wnd_proc: Wnd_Proc,
- cls_extra, wnd_extra: i32,
- instance: Hinstance,
- icon: Hicon,
- cursor: Hcursor,
- background: Hbrush,
- menu_name, class_name: Wstring,
- sm: Hicon,
-}
-
-
-Msg :: struct {
- hwnd: Hwnd,
- message: u32,
- wparam: Wparam,
- lparam: Lparam,
- time: u32,
- pt: Point,
-}
-
-Rect :: struct {
- left: i32,
- top: i32,
- right: i32,
- bottom: i32,
-}
-
-Dev_Mode_A :: struct {
- device_name: [32]u8,
- spec_version: u16,
- driver_version: u16,
- size: u16,
- driver_extra: u16,
- fields: u32,
- using _: struct #raw_union {
- // Printer only fields.
- using _: struct {
- orientation: i16,
- paper_size: i16,
- paper_length: i16,
- paper_width: i16,
- scale: i16,
- copies: i16,
- default_source: i16,
- print_quality: i16,
- },
- // Display only fields.
- using _: struct {
- position: Point,
- display_orientation: u32,
- display_fixed_output: u32,
- },
- },
- color: i16,
- duplex: i16,
- y_resolution: i16,
- tt_option: i16,
- collate: i16,
- form_name: [32]u8,
- log_pixels: u16,
- bits_per_pel: u32,
- pels_width: u32,
- pels_height: u32,
- using _: struct #raw_union {
- display_flags: u32,
- nup: u32,
- },
- display_frequency: u32,
- icm_method: u32,
- icm_intent: u32,
- media_type: u32,
- dither_type: u32,
- reserved_1: u32,
- reserved_2: u32,
- panning_width: u32,
- panning_height: u32,
-}
-
-Filetime :: struct {
- lo, hi: u32,
-}
-
-Systemtime :: struct {
- year, month: u16,
- day_of_week, day: u16,
- hour, minute, second, millisecond: u16,
-}
-
-By_Handle_File_Information :: struct {
- file_attributes: u32,
- creation_time,
- last_access_time,
- last_write_time: Filetime,
- volume_serial_number,
- file_size_high,
- file_size_low,
- number_of_links,
- file_index_high,
- file_index_low: u32,
-}
-
-File_Attribute_Data :: struct {
- file_attributes: u32,
- creation_time,
- last_access_time,
- last_write_time: Filetime,
- file_size_high,
- file_size_low: u32,
-}
-
-// NOTE(Jeroen): The widechar version might want at least the 32k MAX_PATH_WIDE
-// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilew#parameters
-Find_Data_W :: struct{
- file_attributes: u32,
- creation_time: Filetime,
- last_access_time: Filetime,
- last_write_time: Filetime,
- file_size_high: u32,
- file_size_low: u32,
- reserved0: u32,
- reserved1: u32,
- file_name: [MAX_PATH]u16,
- alternate_file_name: [14]u16,
-}
-
-Find_Data_A :: struct{
- file_attributes: u32,
- creation_time: Filetime,
- last_access_time: Filetime,
- last_write_time: Filetime,
- file_size_high: u32,
- file_size_low: u32,
- reserved0: u32,
- reserved1: u32,
- file_name: [MAX_PATH]byte,
- alternate_file_name: [14]byte,
-}
-
-Security_Attributes :: struct {
- length: u32,
- security_descriptor: rawptr,
- inherit_handle: Bool,
-}
-
-Process_Information :: struct {
- process: Handle,
- thread: Handle,
- process_id: u32,
- thread_id: u32,
-}
-
-Startup_Info :: struct {
- cb: u32,
- reserved: Wstring,
- desktop: Wstring,
- title: Wstring,
- x: u32,
- y: u32,
- x_size: u32,
- y_size: u32,
- x_count_chars: u32,
- y_count_chars: u32,
- fill_attribute: u32,
- flags: u32,
- show_window: u16,
- _: u16,
- _: cstring,
- stdin: Handle,
- stdout: Handle,
- stderr: Handle,
-}
-
-Pixel_Format_Descriptor :: struct {
- size,
- version,
- flags: u32,
-
- pixel_type,
- color_bits,
- red_bits,
- red_shift,
- green_bits,
- green_shift,
- blue_bits,
- blue_shift,
- alpha_bits,
- alpha_shift,
- accum_bits,
- accum_red_bits,
- accum_green_bits,
- accum_blue_bits,
- accum_alpha_bits,
- depth_bits,
- stencil_bits,
- aux_buffers,
- layer_type,
- reserved: byte,
-
- layer_mask,
- visible_mask,
- damage_mask: u32,
-}
-
-Critical_Section :: struct {
- debug_info: ^Critical_Section_Debug,
-
- lock_count: i32,
- recursion_count: i32,
- owning_thread: Handle,
- lock_semaphore: Handle,
- spin_count: ^u32,
-}
-
-Critical_Section_Debug :: struct {
- typ: u16,
- creator_back_trace_index: u16,
- critical_section: ^Critical_Section,
- process_locks_list: ^List_Entry,
- entry_count: u32,
- contention_count: u32,
- flags: u32,
- creator_back_trace_index_high: u16,
- spare_word: u16,
-}
-
-List_Entry :: struct {flink, blink: ^List_Entry}
-
-
-Raw_Input_Device :: struct {
- usage_page: u16,
- usage: u16,
- flags: u32,
- wnd_target: Hwnd,
-}
-
-Raw_Input_Header :: struct {
- kind: u32,
- size: u32,
- device: Handle,
- wparam: Wparam,
-}
-
-Raw_HID :: struct {
- size_hid: u32,
- count: u32,
- raw_data: [1]byte,
-}
-
-Raw_Keyboard :: struct {
- make_code: u16,
- flags: u16,
- reserved: u16,
- vkey: u16,
- message: u32,
- extra_information: u32,
-}
-
-Raw_Mouse :: struct {
- flags: u16,
- using data: struct #raw_union {
- buttons: u32,
- using _: struct {
- button_flags: u16,
- button_data: u16,
- },
- },
- raw_buttons: u32,
- last_x: i32,
- last_y: i32,
- extra_information: u32,
-}
-
-Raw_Input :: struct {
- using header: Raw_Input_Header,
- data: struct #raw_union {
- mouse: Raw_Mouse,
- keyboard: Raw_Keyboard,
- hid: Raw_HID,
- },
-}
-
-
-Overlapped :: struct {
- internal: ^u64,
- internal_high: ^u64,
- using _: struct #raw_union {
- using _: struct {
- offset: u32,
- offset_high: u32,
- },
- pointer: rawptr,
- },
- event: Handle,
-}
-
-File_Notify_Information :: struct {
- next_entry_offset: u32,
- action: u32,
- file_name_length: u32,
- file_name: [1]u16,
-}
-
-// https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
-System_Info :: struct {
- using _: struct #raw_union {
- oem_id: u32,
- using _: struct #raw_union {
- processor_architecture: u16,
- _: u16, // reserved
- },
- },
- page_size: u32,
- minimum_application_address: rawptr,
- maximum_application_address: rawptr,
- active_processor_mask: u32,
- number_of_processors: u32,
- processor_type: u32,
- allocation_granularity: u32,
- processor_level: u16,
- processor_revision: u16,
-}
-
-// https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa
-OS_Version_Info_Ex_A :: struct {
- os_version_info_size: u32,
- major_version: u32,
- minor_version: u32,
- build_number: u32,
- platform_id : u32,
- service_pack_string: [128]u8,
- service_pack_major: u16,
- service_pack_minor: u16,
- suite_mask: u16,
- product_type: u8,
- reserved: u8,
-}
-
-MAPVK_VK_TO_VSC :: 0
-MAPVK_VSC_TO_VK :: 1
-MAPVK_VK_TO_CHAR :: 2
-MAPVK_VSC_TO_VK_EX :: 3
-
-//WinUser.h
-ENUM_CURRENT_SETTINGS :: u32(4294967295) // (DWORD)-1
-ENUM_REGISTRY_SETTINGS :: u32(4294967294) // (DWORD)-2
-
-VK_LBUTTON :: 0x01
-VK_RBUTTON :: 0x02
-VK_CANCEL :: 0x03
-VK_MBUTTON :: 0x04 /* NOT contiguous with L & RBUTTON */
-VK_XBUTTON1 :: 0x05 /* NOT contiguous with L & RBUTTON */
-VK_XBUTTON2 :: 0x06 /* NOT contiguous with L & RBUTTON */
-
-/*
- * :: 0x07 : reserved
- */
-
-VK_BACK :: 0x08
-VK_TAB :: 0x09
-
-/*
- * :: 0x0A - :: 0x0B : reserved
- */
-
-VK_CLEAR :: 0x0C
-VK_RETURN :: 0x0D
-
-/*
- * :: 0x0E - :: 0x0F : unassigned
- */
-
-VK_SHIFT :: 0x10
-VK_CONTROL :: 0x11
-VK_MENU :: 0x12
-VK_PAUSE :: 0x13
-VK_CAPITAL :: 0x14
-
-VK_KANA :: 0x15
-VK_HANGEUL :: 0x15 /* old name - should be here for compatibility */
-VK_HANGUL :: 0x15
-
-/*
- * :: 0x16 : unassigned
- */
-
-VK_JUNJA :: 0x17
-VK_FINAL :: 0x18
-VK_HANJA :: 0x19
-VK_KANJI :: 0x19
-
-/*
- * :: 0x1A : unassigned
- */
-
-VK_ESCAPE :: 0x1B
-
-VK_CONVERT :: 0x1C
-VK_NONCONVERT :: 0x1D
-VK_ACCEPT :: 0x1E
-VK_MODECHANGE :: 0x1F
-
-VK_SPACE :: 0x20
-VK_PRIOR :: 0x21
-VK_NEXT :: 0x22
-VK_END :: 0x23
-VK_HOME :: 0x24
-VK_LEFT :: 0x25
-VK_UP :: 0x26
-VK_RIGHT :: 0x27
-VK_DOWN :: 0x28
-VK_SELECT :: 0x29
-VK_PRINT :: 0x2A
-VK_EXECUTE :: 0x2B
-VK_SNAPSHOT :: 0x2C
-VK_INSERT :: 0x2D
-VK_DELETE :: 0x2E
-VK_HELP :: 0x2F
-
-/*
- * VK_0 - VK_9 are the same as ASCII '0' - '9' (:: 0x30 - :: 0x39)
- * :: 0x3A - :: 0x40 : unassigned
- * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (:: 0x41 - :: 0x5A)
- */
-
-VK_LWIN :: 0x5B
-VK_RWIN :: 0x5C
-VK_APPS :: 0x5D
-
-/*
- * :: 0x5E : reserved
- */
-
-VK_SLEEP :: 0x5F
-
-VK_NUMPAD0 :: 0x60
-VK_NUMPAD1 :: 0x61
-VK_NUMPAD2 :: 0x62
-VK_NUMPAD3 :: 0x63
-VK_NUMPAD4 :: 0x64
-VK_NUMPAD5 :: 0x65
-VK_NUMPAD6 :: 0x66
-VK_NUMPAD7 :: 0x67
-VK_NUMPAD8 :: 0x68
-VK_NUMPAD9 :: 0x69
-VK_MULTIPLY :: 0x6A
-VK_ADD :: 0x6B
-VK_SEPARATOR :: 0x6C
-VK_SUBTRACT :: 0x6D
-VK_DECIMAL :: 0x6E
-VK_DIVIDE :: 0x6F
-VK_F1 :: 0x70
-VK_F2 :: 0x71
-VK_F3 :: 0x72
-VK_F4 :: 0x73
-VK_F5 :: 0x74
-VK_F6 :: 0x75
-VK_F7 :: 0x76
-VK_F8 :: 0x77
-VK_F9 :: 0x78
-VK_F10 :: 0x79
-VK_F11 :: 0x7A
-VK_F12 :: 0x7B
-VK_F13 :: 0x7C
-VK_F14 :: 0x7D
-VK_F15 :: 0x7E
-VK_F16 :: 0x7F
-VK_F17 :: 0x80
-VK_F18 :: 0x81
-VK_F19 :: 0x82
-VK_F20 :: 0x83
-VK_F21 :: 0x84
-VK_F22 :: 0x85
-VK_F23 :: 0x86
-VK_F24 :: 0x87
-
-INVALID_HANDLE :: Handle(~uintptr(0))
-
-CREATE_SUSPENDED :: 0x00000004
-STACK_SIZE_PARAM_IS_A_RESERVATION :: 0x00010000
-WAIT_ABANDONED :: 0x00000080
-WAIT_OBJECT_0 :: 0
-WAIT_TIMEOUT :: 0x00000102
-WAIT_FAILED :: 0xffffffff
-
-CS_VREDRAW :: 0x0001
-CS_HREDRAW :: 0x0002
-CS_OWNDC :: 0x0020
-CW_USEDEFAULT :: -0x80000000
-
-WS_OVERLAPPED :: 0
-WS_MAXIMIZEBOX :: 0x00010000
-WS_MINIMIZEBOX :: 0x00020000
-WS_THICKFRAME :: 0x00040000
-WS_SYSMENU :: 0x00080000
-WS_BORDER :: 0x00800000
-WS_CAPTION :: 0x00C00000
-WS_VISIBLE :: 0x10000000
-WS_POPUP :: 0x80000000
-WS_MAXIMIZE :: 0x01000000
-WS_MINIMIZE :: 0x20000000
-WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX
-WS_POPUPWINDOW :: WS_POPUP | WS_BORDER | WS_SYSMENU
-
-WS_EX_DLGMODALFRAME :: 0x00000001
-WS_EX_NOPARENTNOTIFY :: 0x00000004
-WS_EX_TOPMOST :: 0x00000008
-WS_EX_ACCEPTFILES :: 0x00000010
-WS_EX_TRANSPARENT :: 0x00000020
-WS_EX_MDICHILD :: 0x00000040
-WS_EX_TOOLWINDOW :: 0x00000080
-WS_EX_WINDOWEDGE :: 0x00000100
-WS_EX_CLIENTEDGE :: 0x00000200
-WS_EX_CONTEXTHELP :: 0x00000400
-WS_EX_RIGHT :: 0x00001000
-WS_EX_LEFT :: 0x00000000
-WS_EX_RTLREADING :: 0x00002000
-WS_EX_LTRREADING :: 0x00000000
-WS_EX_LEFTSCROLLBAR :: 0x00004000
-WS_EX_RIGHTSCROLLBAR :: 0x00000000
-WS_EX_CONTROLPARENT :: 0x00010000
-WS_EX_STATICEDGE :: 0x00020000
-WS_EX_APPWINDOW :: 0x00040000
-WS_EX_OVERLAPPEDWINDOW :: WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE
-WS_EX_PALETTEWINDOW :: WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST
-WS_EX_LAYERED :: 0x00080000
-WS_EX_NOINHERITLAYOUT :: 0x00100000 // Disable inheritence of mirroring by children
-WS_EX_NOREDIRECTIONBITMAP :: 0x00200000
-WS_EX_LAYOUTRTL :: 0x00400000 // Right to left mirroring
-WS_EX_COMPOSITED :: 0x02000000
-WS_EX_NOACTIVATE :: 0x08000000
-
-WM_ACTIVATE :: 0x0006
-WM_ACTIVATEAPP :: 0x001C
-WM_CHAR :: 0x0102
-WM_CLOSE :: 0x0010
-WM_CREATE :: 0x0001
-WM_DESTROY :: 0x0002
-WM_INPUT :: 0x00FF
-WM_KEYDOWN :: 0x0100
-WM_KEYUP :: 0x0101
-WM_KILLFOCUS :: 0x0008
-WM_QUIT :: 0x0012
-WM_SETCURSOR :: 0x0020
-WM_SETFOCUS :: 0x0007
-WM_SIZE :: 0x0005
-WM_SIZING :: 0x0214
-WM_SYSKEYDOWN :: 0x0104
-WM_SYSKEYUP :: 0x0105
-WM_USER :: 0x0400
-WM_WINDOWPOSCHANGED :: 0x0047
-WM_COMMAND :: 0x0111
-WM_PAINT :: 0x000F
-
-WM_MOUSEWHEEL :: 0x020A
-WM_MOUSEMOVE :: 0x0200
-WM_LBUTTONDOWN :: 0x0201
-WM_LBUTTONUP :: 0x0202
-WM_LBUTTONDBLCLK :: 0x0203
-WM_RBUTTONDOWN :: 0x0204
-WM_RBUTTONUP :: 0x0205
-WM_RBUTTONDBLCLK :: 0x0206
-WM_MBUTTONDOWN :: 0x0207
-WM_MBUTTONUP :: 0x0208
-WM_MBUTTONDBLCLK :: 0x0209
-
-PM_NOREMOVE :: 0x0000
-PM_REMOVE :: 0x0001
-PM_NOYIELD :: 0x0002
-
-BLACK_BRUSH :: 4
-
-SM_CXSCREEN :: 0
-SM_CYSCREEN :: 1
-
-SW_SHOW :: 5
-
-COLOR_BACKGROUND :: Hbrush(uintptr(1))
-
-INVALID_SET_FILE_POINTER :: ~u32(0)
-HEAP_ZERO_MEMORY :: 0x00000008
-INFINITE :: 0xffffffff
-GWL_EXSTYLE :: -20
-GWLP_HINSTANCE :: -6
-GWLP_ID :: -12
-GWL_STYLE :: -16
-GWLP_USERDATA :: -21
-GWLP_WNDPROC :: -4
-Hwnd_TOP :: Hwnd(uintptr(0))
-
-BI_RGB :: 0
-DIB_RGB_COLORS :: 0x00
-SRCCOPY: u32 : 0x00cc0020
-
-
-MONITOR_DEFAULTTONULL :: 0x00000000
-MONITOR_DEFAULTTOPRIMARY :: 0x00000001
-MONITOR_DEFAULTTONEAREST :: 0x00000002
-
-SWP_FRAMECHANGED :: 0x0020
-SWP_NOOWNERZORDER :: 0x0200
-SWP_NOZORDER :: 0x0004
-SWP_NOSIZE :: 0x0001
-SWP_NOMOVE :: 0x0002
-
-
-// Raw Input
-
-
-RID_HEADER :: 0x10000005
-RID_INPUT :: 0x10000003
-
-
-RIDEV_APPKEYS :: 0x00000400
-RIDEV_CAPTUREMOUSE :: 0x00000200
-RIDEV_DEVNOTIFY :: 0x00002000
-RIDEV_EXCLUDE :: 0x00000010
-RIDEV_EXINPUTSINK :: 0x00001000
-RIDEV_INPUTSINK :: 0x00000100
-RIDEV_NOHOTKEYS :: 0x00000200
-RIDEV_NOLEGACY :: 0x00000030
-RIDEV_PAGEONLY :: 0x00000020
-RIDEV_REMOVE :: 0x00000001
-
-
-RIM_TYPEMOUSE :: 0
-RIM_TYPEKEYBOARD :: 1
-RIM_TYPEHID :: 2
-
-
-MOUSE_ATTRIBUTES_CHANGED :: 0x04
-MOUSE_MOVE_RELATIVE :: 0
-MOUSE_MOVE_ABSOLUTE :: 1
-MOUSE_VIRTUAL_DESKTOP :: 0x02
-
-
-
-RI_MOUSE_BUTTON_1_DOWN :: 0x0001
-RI_MOUSE_BUTTON_1_UP :: 0x0002
-RI_MOUSE_BUTTON_2_DOWN :: 0x0004
-RI_MOUSE_BUTTON_2_UP :: 0x0008
-RI_MOUSE_BUTTON_3_DOWN :: 0x0010
-RI_MOUSE_BUTTON_3_UP :: 0x0020
-RI_MOUSE_BUTTON_4_DOWN :: 0x0040
-RI_MOUSE_BUTTON_4_UP :: 0x0080
-RI_MOUSE_BUTTON_5_DOWN :: 0x0100
-RI_MOUSE_BUTTON_5_UP :: 0x0200
-RI_MOUSE_LEFT_BUTTON_DOWN :: 0x0001
-RI_MOUSE_LEFT_BUTTON_UP :: 0x0002
-RI_MOUSE_MIDDLE_BUTTON_DOWN :: 0x0010
-RI_MOUSE_MIDDLE_BUTTON_UP :: 0x0020
-RI_MOUSE_RIGHT_BUTTON_DOWN :: 0x0004
-RI_MOUSE_RIGHT_BUTTON_UP :: 0x0008
-RI_MOUSE_WHEEL :: 0x0400
-
-
-RI_KEY_MAKE :: 0x00
-RI_KEY_BREAK :: 0x01
-RI_KEY_E0 :: 0x02
-RI_KEY_E1 :: 0x04
-RI_KEY_TERMSRV_SET_LED :: 0x08
-RI_KEY_TERMSRV_SHADOW :: 0x10
-
-// Windows OpenGL
-
-PFD_TYPE_RGBA :: 0
-PFD_TYPE_COLORINDEX :: 1
-PFD_MAIN_PLANE :: 0
-PFD_OVERLAY_PLANE :: 1
-PFD_UNDERLAY_PLANE :: -1
-PFD_DOUBLEBUFFER :: 1
-PFD_STEREO :: 2
-PFD_DRAW_TO_WINDOW :: 4
-PFD_DRAW_TO_BITMAP :: 8
-PFD_SUPPORT_GDI :: 16
-PFD_SUPPORT_OPENGL :: 32
-PFD_GENERIC_FORMAT :: 64
-PFD_NEED_PALETTE :: 128
-PFD_NEED_SYSTEM_PALETTE :: 0x00000100
-PFD_SWAP_EXCHANGE :: 0x00000200
-PFD_SWAP_COPY :: 0x00000400
-PFD_SWAP_LAYER_BUFFERS :: 0x00000800
-PFD_GENERIC_ACCELERATED :: 0x00001000
-PFD_DEPTH_DONTCARE :: 0x20000000
-PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
-PFD_STEREO_DONTCARE :: 0x80000000
-
-GET_FILEEX_INFO_LEVELS :: distinct i32
-GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
-GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
-
-STARTF_USESHOWWINDOW :: 0x00000001
-STARTF_USESIZE :: 0x00000002
-STARTF_USEPOSITION :: 0x00000004
-STARTF_USECOUNTCHARS :: 0x00000008
-STARTF_USEFILLATTRIBUTE :: 0x00000010
-STARTF_RUNFULLSCREEN :: 0x00000020 // ignored for non-x86 platforms
-STARTF_FORCEONFEEDBACK :: 0x00000040
-STARTF_FORCEOFFFEEDBACK :: 0x00000080
-STARTF_USESTDHANDLES :: 0x00000100
-STARTF_USEHOTKEY :: 0x00000200
-STARTF_TITLEISLINKNAME :: 0x00000800
-STARTF_TITLEISAPPID :: 0x00001000
-STARTF_PREVENTPINNING :: 0x00002000
-STARTF_UNTRUSTEDSOURCE :: 0x00008000
-
-
-MOVEFILE_REPLACE_EXISTING :: 0x00000001
-MOVEFILE_COPY_ALLOWED :: 0x00000002
-MOVEFILE_DELAY_UNTIL_REBOOT :: 0x00000004
-MOVEFILE_WRITE_THROUGH :: 0x00000008
-MOVEFILE_CREATE_HARDLINK :: 0x00000010
-MOVEFILE_FAIL_IF_NOT_TRACKABLE :: 0x00000020
-
-FILE_NOTIFY_CHANGE_FILE_NAME :: 0x00000001
-FILE_NOTIFY_CHANGE_DIR_NAME :: 0x00000002
-FILE_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000004
-FILE_NOTIFY_CHANGE_SIZE :: 0x00000008
-FILE_NOTIFY_CHANGE_LAST_WRITE :: 0x00000010
-FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020
-FILE_NOTIFY_CHANGE_CREATION :: 0x00000040
-FILE_NOTIFY_CHANGE_SECURITY :: 0x00000100
-
-FILE_FLAG_WRITE_THROUGH :: 0x80000000
-FILE_FLAG_OVERLAPPED :: 0x40000000
-FILE_FLAG_NO_BUFFERING :: 0x20000000
-FILE_FLAG_RANDOM_ACCESS :: 0x10000000
-FILE_FLAG_SEQUENTIAL_SCAN :: 0x08000000
-FILE_FLAG_DELETE_ON_CLOSE :: 0x04000000
-FILE_FLAG_BACKUP_SEMANTICS :: 0x02000000
-FILE_FLAG_POSIX_SEMANTICS :: 0x01000000
-FILE_FLAG_SESSION_AWARE :: 0x00800000
-FILE_FLAG_OPEN_REPARSE_POINT :: 0x00200000
-FILE_FLAG_OPEN_NO_RECALL :: 0x00100000
-FILE_FLAG_FIRST_PIPE_INSTANCE :: 0x00080000
-
-FILE_ACTION_ADDED :: 0x00000001
-FILE_ACTION_REMOVED :: 0x00000002
-FILE_ACTION_MODIFIED :: 0x00000003
-FILE_ACTION_RENAMED_OLD_NAME :: 0x00000004
-FILE_ACTION_RENAMED_NEW_NAME :: 0x00000005
-
-CP_ACP :: 0 // default to ANSI code page
-CP_OEMCP :: 1 // default to OEM code page
-CP_MACCP :: 2 // default to MAC code page
-CP_THREAD_ACP :: 3 // current thread's ANSI code page
-CP_SYMBOL :: 42 // SYMBOL translations
-CP_UTF7 :: 65000 // UTF-7 translation
-CP_UTF8 :: 65001 // UTF-8 translation
-
-
-MB_ERR_INVALID_CHARS :: 8
-WC_ERR_INVALID_CHARS :: 128
-
-utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
- if len(s) < 1 {
- return nil
- }
-
- b := transmute([]byte)s
- cstr := cstring(&b[0])
- n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0)
- if n == 0 {
- return nil
- }
-
- text := make([]u16, n+1, allocator)
-
- n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), Wstring(&text[0]), i32(n))
- if n1 == 0 {
- delete(text, allocator)
- return nil
- }
-
- text[n] = 0
- for n >= 1 && text[n-1] == 0 {
- n -= 1
- }
- return text[:n]
-}
-utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstring {
- if res := utf8_to_utf16(s, allocator); res != nil {
- return Wstring(&res[0])
- }
- return nil
-}
-
-wstring_to_utf8 :: proc(s: Wstring, N: int, allocator := context.temp_allocator) -> string {
- if N == 0 {
- return ""
- }
-
- n := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
- if n == 0 {
- return ""
- }
-
- // If N == -1 the call to wide_char_to_multi_byte assume the wide string is null terminated
- // and will scan it to find the first null terminated character. The resulting string will
- // also null terminated.
- // If N != -1 it assumes the wide string is not null terminated and the resulting string
- // will not be null terminated, we therefore have to force it to be null terminated manually.
- text := make([]byte, n+1 if N != -1 else n, allocator)
-
- if n1 := wide_char_to_multi_byte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), cstring(&text[0]), n, nil, nil); n1 == 0 {
- delete(text, allocator)
- return ""
- }
-
- for i in 0.. string {
- if len(s) == 0 {
- return ""
- }
- return wstring_to_utf8(cast(Wstring)&s[0], len(s), allocator)
-}
-
-get_query_performance_frequency :: proc() -> i64 {
- r: i64
- query_performance_frequency(&r)
- return r
-}
-
-HIWORD_W :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff) }
-HIWORD_L :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff) }
-LOWORD_W :: proc(wParam: Wparam) -> u16 { return u16(wParam) }
-LOWORD_L :: proc(lParam: Lparam) -> u16 { return u16(lParam) }
-
-is_key_down :: #force_inline proc(key: Key_Code) -> bool { return get_async_key_state(i32(key)) < 0 }
-
-
-
-
-MAX_PATH :: 0x00000104
-MAX_PATH_WIDE :: 0x8000
-
-HANDLE_FLAG_INHERIT :: 1
-HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2
-
-FILE_BEGIN :: 0
-FILE_CURRENT :: 1
-FILE_END :: 2
-
-FILE_SHARE_READ :: 0x00000001
-FILE_SHARE_WRITE :: 0x00000002
-FILE_SHARE_DELETE :: 0x00000004
-FILE_GENERIC_ALL :: 0x10000000
-FILE_GENERIC_EXECUTE :: 0x20000000
-FILE_GENERIC_WRITE :: 0x40000000
-FILE_GENERIC_READ :: 0x80000000
-
-FILE_READ_DATA :: 0x0001
-FILE_LIST_DIRECTORY :: 0x0001
-FILE_WRITE_DATA :: 0x0002
-FILE_ADD_FILE :: 0x0002
-FILE_APPEND_DATA :: 0x0004
-FILE_ADD_SUBDIRECTORY :: 0x0004
-FILE_CREATE_PIPE_INSTANCE :: 0x0004
-FILE_READ_EA :: 0x0008
-FILE_WRITE_EA :: 0x0010
-FILE_EXECUTE :: 0x0020
-FILE_TRAVERSE :: 0x0020
-FILE_DELETE_CHILD :: 0x0040
-FILE_READ_ATTRIBUTES :: 0x0080
-FILE_WRITE_ATTRIBUTES :: 0x0100
-
-STD_INPUT_HANDLE :: -10
-STD_OUTPUT_HANDLE :: -11
-STD_ERROR_HANDLE :: -12
-
-CREATE_NEW :: 1
-CREATE_ALWAYS :: 2
-OPEN_EXISTING :: 3
-OPEN_ALWAYS :: 4
-TRUNCATE_EXISTING :: 5
-
-INVALID_FILE_ATTRIBUTES :: -1
-
-FILE_ATTRIBUTE_READONLY :: 0x00000001
-FILE_ATTRIBUTE_HIDDEN :: 0x00000002
-FILE_ATTRIBUTE_SYSTEM :: 0x00000004
-FILE_ATTRIBUTE_DIRECTORY :: 0x00000010
-FILE_ATTRIBUTE_ARCHIVE :: 0x00000020
-FILE_ATTRIBUTE_DEVICE :: 0x00000040
-FILE_ATTRIBUTE_NORMAL :: 0x00000080
-FILE_ATTRIBUTE_TEMPORARY :: 0x00000100
-FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200
-FILE_ATTRIBUTE_REPARSE_Point :: 0x00000400
-FILE_ATTRIBUTE_COMPRESSED :: 0x00000800
-FILE_ATTRIBUTE_OFFLINE :: 0x00001000
-FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000
-FILE_ATTRIBUTE_ENCRYPTED :: 0x00004000
-
-FILE_TYPE_DISK :: 0x0001
-FILE_TYPE_CHAR :: 0x0002
-FILE_TYPE_PIPE :: 0x0003
-
-
-Monitor_Info :: struct {
- size: u32,
- monitor: Rect,
- work: Rect,
- flags: u32,
-}
-
-Window_Placement :: struct {
- length: u32,
- flags: u32,
- show_cmd: u32,
- min_pos: Point,
- max_pos: Point,
- normal_pos: Rect,
-}
-
-Bitmap_Info_Header :: struct {
- size: u32,
- width, height: i32,
- planes, bit_count: i16,
- compression: u32,
- size_image: u32,
- x_pels_per_meter: i32,
- y_pels_per_meter: i32,
- clr_used: u32,
- clr_important: u32,
-}
-Bitmap_Info :: struct {
- using header: Bitmap_Info_Header,
- colors: [1]Rgb_Quad,
-}
-
-Paint_Struct :: struct {
- hdc: Hdc,
- erase: Bool,
- rc_paint: Rect,
- restore: Bool,
- inc_update: Bool,
- rgb_reserved: [32]byte,
-}
-
-
-Rgb_Quad :: struct {blue, green, red, reserved: byte}
-
-
-Key_Code :: enum i32 {
- Unknown = 0x00,
-
- Lbutton = 0x01,
- Rbutton = 0x02,
- Cancel = 0x03,
- Mbutton = 0x04,
- Xbutton1 = 0x05,
- Xbutton2 = 0x06,
- Back = 0x08,
- Tab = 0x09,
- Clear = 0x0C,
- Return = 0x0D,
-
- Shift = 0x10,
- Control = 0x11,
- Menu = 0x12,
- Pause = 0x13,
- Capital = 0x14,
- Kana = 0x15,
- Hangeul = 0x15,
- Hangul = 0x15,
- Junja = 0x17,
- Final = 0x18,
- Hanja = 0x19,
- Kanji = 0x19,
- Escape = 0x1B,
- Convert = 0x1C,
- NonConvert = 0x1D,
- Accept = 0x1E,
- ModeChange = 0x1F,
- Space = 0x20,
- Prior = 0x21,
- Next = 0x22,
- End = 0x23,
- Home = 0x24,
- Left = 0x25,
- Up = 0x26,
- Right = 0x27,
- Down = 0x28,
- Select = 0x29,
- Print = 0x2A,
- Execute = 0x2B,
- Snapshot = 0x2C,
- Insert = 0x2D,
- Delete = 0x2E,
- Help = 0x2F,
-
- Num0 = '0',
- Num1 = '1',
- Num2 = '2',
- Num3 = '3',
- Num4 = '4',
- Num5 = '5',
- Num6 = '6',
- Num7 = '7',
- Num8 = '8',
- Num9 = '9',
- A = 'A',
- B = 'B',
- C = 'C',
- D = 'D',
- E = 'E',
- F = 'F',
- G = 'G',
- H = 'H',
- I = 'I',
- J = 'J',
- K = 'K',
- L = 'L',
- M = 'M',
- N = 'N',
- O = 'O',
- P = 'P',
- Q = 'Q',
- R = 'R',
- S = 'S',
- T = 'T',
- U = 'U',
- V = 'V',
- W = 'W',
- X = 'X',
- Y = 'Y',
- Z = 'Z',
-
- Lwin = 0x5B,
- Rwin = 0x5C,
- Apps = 0x5D,
-
- Numpad0 = 0x60,
- Numpad1 = 0x61,
- Numpad2 = 0x62,
- Numpad3 = 0x63,
- Numpad4 = 0x64,
- Numpad5 = 0x65,
- Numpad6 = 0x66,
- Numpad7 = 0x67,
- Numpad8 = 0x68,
- Numpad9 = 0x69,
- Multiply = 0x6A,
- Add = 0x6B,
- Separator = 0x6C,
- Subtract = 0x6D,
- Decimal = 0x6E,
- Divide = 0x6F,
-
- F1 = 0x70,
- F2 = 0x71,
- F3 = 0x72,
- F4 = 0x73,
- F5 = 0x74,
- F6 = 0x75,
- F7 = 0x76,
- F8 = 0x77,
- F9 = 0x78,
- F10 = 0x79,
- F11 = 0x7A,
- F12 = 0x7B,
- F13 = 0x7C,
- F14 = 0x7D,
- F15 = 0x7E,
- F16 = 0x7F,
- F17 = 0x80,
- F18 = 0x81,
- F19 = 0x82,
- F20 = 0x83,
- F21 = 0x84,
- F22 = 0x85,
- F23 = 0x86,
- F24 = 0x87,
-
- Numlock = 0x90,
- Scroll = 0x91,
- Lshift = 0xA0,
- Rshift = 0xA1,
- Lcontrol = 0xA2,
- Rcontrol = 0xA3,
- Lmenu = 0xA4,
- Rmenu = 0xA5,
- ProcessKey = 0xE5,
- Attn = 0xF6,
- Crsel = 0xF7,
- Exsel = 0xF8,
- Ereof = 0xF9,
- Play = 0xFA,
- Zoom = 0xFB,
- Noname = 0xFC,
- Pa1 = 0xFD,
- OemClear = 0xFE,
-}
-
diff --git a/core/sys/win32/helpers.odin b/core/sys/win32/helpers.odin
deleted file mode 100644
index b04e5db95..000000000
--- a/core/sys/win32/helpers.odin
+++ /dev/null
@@ -1,29 +0,0 @@
-// +build windows
-package win32
-
-import "core:strings"
-
-call_external_process :: proc(program, command_line: string) -> bool {
- si := Startup_Info{ cb=size_of(Startup_Info) }
- pi := Process_Information{}
-
- return cast(bool)create_process_w(
- utf8_to_wstring(program),
- utf8_to_wstring(command_line),
- nil,
- nil,
- Bool(false),
- u32(0x10),
- nil,
- nil,
- &si,
- &pi,
- )
-}
-
-open_website :: proc(url: string) -> bool {
- p :: "C:\\Windows\\System32\\cmd.exe"
- arg := []string{"/C", "start", url}
- args := strings.join(arg, " ", context.temp_allocator)
- return call_external_process(p, args)
-}
diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin
deleted file mode 100644
index 709964fb4..000000000
--- a/core/sys/win32/kernel32.odin
+++ /dev/null
@@ -1,237 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:kernel32.lib"
-
-@(default_calling_convention = "std")
-foreign kernel32 {
- @(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
- process_attributes, thread_attributes: ^Security_Attributes,
- inherit_handle: Bool, creation_flags: u32, environment: rawptr,
- current_directory: cstring, startup_info: ^Startup_Info,
- process_information: ^Process_Information) -> Bool ---
- @(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
- process_attributes, thread_attributes: ^Security_Attributes,
- inherit_handle: Bool, creation_flags: u32, environment: rawptr,
- current_directory: Wstring, startup_info: ^Startup_Info,
- process_information: ^Process_Information) -> Bool ---
- @(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---
- @(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---
- @(link_name="GetModuleHandleA") get_module_handle_a :: proc(module_name: cstring) -> Hmodule ---
- @(link_name="GetModuleHandleW") get_module_handle_w :: proc(module_name: Wstring) -> Hmodule ---
-
- @(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---
- @(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---
-
- @(link_name="Sleep") sleep :: proc(ms: u32) ---
- @(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---
- @(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---
- @(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) ---
-
- @(link_name="GetCommandLineA") get_command_line_a :: proc() -> cstring ---
- @(link_name="GetCommandLineW") get_command_line_w :: proc() -> Wstring ---
- @(link_name="GetSystemMetrics") get_system_metrics :: proc(index: i32) -> i32 ---
- @(link_name="GetSystemInfo") get_system_info :: proc(info: ^System_Info) ---
- @(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---
- @(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---
-
- // NOTE(tetra): Not thread safe with SetCurrentDirectory and GetFullPathName;
- // The current directory is stored as a global variable in the process.
- @(link_name="GetCurrentDirectoryW") get_current_directory_w :: proc(len: u32, buf: Wstring) -> u32 ---
- @(link_name="SetCurrentDirectoryW") set_current_directory_w :: proc(buf: Wstring) -> u32 ---
-
- @(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) ---
- @(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool ---
- @(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool ---
- @(link_name="SystemTimeToFileTime") system_time_to_file_time :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool ---
-
- @(link_name="GetStdHandle") get_std_handle :: proc(h: i32) -> Handle ---
-
- @(link_name="CreateFileA")
- create_file_a :: proc(filename: cstring, desired_access, share_module: u32,
- security: rawptr,
- creation, flags_and_attribs: u32, template_file: Handle) -> Handle ---
-
- @(link_name="CreateFileW")
- create_file_w :: proc(filename: Wstring, desired_access, share_module: u32,
- security: rawptr,
- creation, flags_and_attribs: u32, template_file: Handle) -> Handle ---
-
-
- @(link_name="ReadFile") read_file :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool ---
- @(link_name="WriteFile") write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool ---
-
- @(link_name="GetFileSizeEx") get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool ---
- @(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool ---
-
- @(link_name="CreateDirectoryA") create_directory_a :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool ---
- @(link_name="CreateDirectoryW") create_directory_w :: proc(path: Wstring, security_attributes: ^Security_Attributes) -> Bool ---
-
- @(link_name="GetFileType") get_file_type :: proc(file_handle: Handle) -> u32 ---
- @(link_name="SetFilePointer") set_file_pointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 ---
-
- @(link_name="SetHandleInformation") set_handle_information :: proc(obj: Handle, mask, flags: u32) -> Bool ---
-
- @(link_name="FindFirstFileA") find_first_file_a :: proc(file_name: cstring, data: ^Find_Data_A) -> Handle ---
- @(link_name="FindNextFileA") find_next_file_a :: proc(file: Handle, data: ^Find_Data_A) -> Bool ---
-
- @(link_name="FindFirstFileW") find_first_file_w :: proc(file_name: Wstring, data: ^Find_Data_W) -> Handle ---
- @(link_name="FindNextFileW") find_next_file_w :: proc(file: Handle, data: ^Find_Data_W) -> Bool ---
-
- @(link_name="FindClose") find_close :: proc(file: Handle) -> Bool ---
-
- @(link_name="MoveFileExA") move_file_ex_a :: proc(existing, new: cstring, flags: u32) -> Bool ---
- @(link_name="DeleteFileA") delete_file_a :: proc(file_name: cstring) -> Bool ---
- @(link_name="CopyFileA") copy_file_a :: proc(existing, new: cstring, fail_if_exists: Bool) -> Bool ---
-
- @(link_name="MoveFileExW") move_file_ex_w :: proc(existing, new: Wstring, flags: u32) -> Bool ---
- @(link_name="DeleteFileW") delete_file_w :: proc(file_name: Wstring) -> Bool ---
- @(link_name="CopyFileW") copy_file_w :: proc(existing, new: Wstring, fail_if_exists: Bool) -> Bool ---
-
- @(link_name="HeapAlloc") heap_alloc :: proc(h: Handle, flags: u32, bytes: int) -> rawptr ---
- @(link_name="HeapReAlloc") heap_realloc :: proc(h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr ---
- @(link_name="HeapFree") heap_free :: proc(h: Handle, flags: u32, memory: rawptr) -> Bool ---
- @(link_name="GetProcessHeap") get_process_heap :: proc() -> Handle ---
-
- @(link_name="LocalAlloc") local_alloc :: proc(flags: u32, bytes: int) -> rawptr ---
- @(link_name="LocalReAlloc") local_realloc :: proc(mem: rawptr, bytes: int, flags: uint) -> rawptr ---
- @(link_name="LocalFree") local_free :: proc(mem: rawptr) -> rawptr ---
-
- @(link_name="FindFirstChangeNotificationA") find_first_change_notification_a :: proc(path: cstring, watch_subtree: Bool, filter: u32) -> Handle ---
- @(link_name="FindNextChangeNotification") find_next_change_notification :: proc(h: Handle) -> Bool ---
- @(link_name="FindCloseChangeNotification") find_close_change_notification :: proc(h: Handle) -> Bool ---
-
- @(link_name="ReadDirectoryChangesW") read_directory_changes_w :: proc(dir: Handle, buf: rawptr, buf_length: u32,
- watch_subtree: Bool, notify_filter: u32,
- bytes_returned: ^u32, overlapped: ^Overlapped,
- completion: rawptr) -> Bool ---
-
- @(link_name="GetOverlappedResult") get_overlapped_result :: proc(file: Handle, overlapped: ^Overlapped, number_of_bytes_transferred: ^u32, wait: Bool) -> Bool ---
-
- @(link_name="WideCharToMultiByte") wide_char_to_multi_byte :: proc(code_page: u32, flags: u32,
- wchar_str: Wstring, wchar: i32,
- multi_str: cstring, multi: i32,
- default_char: cstring, used_default_char: ^Bool) -> i32 ---
-
- @(link_name="MultiByteToWideChar") multi_byte_to_wide_char :: proc(code_page: u32, flags: u32,
- mb_str: cstring, mb: i32,
- wc_str: Wstring, wc: i32) -> i32 ---
-
- @(link_name="CreateSemaphoreA") create_semaphore_a :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle ---
- @(link_name="CreateSemaphoreW") create_semaphore_w :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle ---
- @(link_name="ReleaseSemaphore") release_semaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool ---
- @(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 ---
-}
-
-// @(default_calling_convention = "c")
-foreign kernel32 {
- @(link_name="GetLastError") get_last_error :: proc() -> i32 ---
- @(link_name="CloseHandle") close_handle :: proc(h: Handle) -> i32 ---
-
- @(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 ---
- @(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 ---
- @(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---
- @(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---
- @(link_name="CompareFileTime") compare_file_time :: proc(a, b: ^Filetime) -> i32 ---
-}
-
-@(default_calling_convention = "c")
-foreign kernel32 {
- @(link_name="InterlockedCompareExchange") interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 ---
- @(link_name="InterlockedExchange") interlocked_exchange :: proc(dst: ^i32, desired: i32) -> i32 ---
- @(link_name="InterlockedExchangeAdd") interlocked_exchange_add :: proc(dst: ^i32, desired: i32) -> i32 ---
- @(link_name="InterlockedAnd") interlocked_and :: proc(dst: ^i32, desired: i32) -> i32 ---
- @(link_name="InterlockedOr") interlocked_or :: proc(dst: ^i32, desired: i32) -> i32 ---
-
- @(link_name="InterlockedCompareExchange64") interlocked_compare_exchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 ---
- @(link_name="InterlockedExchange64") interlocked_exchange64 :: proc(dst: ^i64, desired: i64) -> i64 ---
- @(link_name="InterlockedExchangeAdd64") interlocked_exchange_add64 :: proc(dst: ^i64, desired: i64) -> i64 ---
- @(link_name="InterlockedAnd64") interlocked_and64 :: proc(dst: ^i64, desired: i64) -> i64 ---
- @(link_name="InterlockedOr64") interlocked_or64 :: proc(dst: ^i64, desired: i64) -> i64 ---
-}
-
-@(default_calling_convention = "std")
-foreign kernel32 {
- @(link_name="_mm_pause") mm_pause :: proc() ---
- @(link_name="ReadWriteBarrier") read_write_barrier :: proc() ---
- @(link_name="WriteBarrier") write_barrier :: proc() ---
- @(link_name="ReadBarrier") read_barrier :: proc() ---
-
- @(link_name="CreateThread")
- create_thread :: proc(thread_attributes: ^Security_Attributes, stack_size: uint, start_routine: proc "stdcall" (rawptr) -> u32,
- parameter: rawptr, creation_flags: u32, thread_id: ^u32) -> Handle ---
- @(link_name="ResumeThread") resume_thread :: proc(thread: Handle) -> u32 ---
- @(link_name="GetThreadPriority") get_thread_priority :: proc(thread: Handle) -> i32 ---
- @(link_name="SetThreadPriority") set_thread_priority :: proc(thread: Handle, priority: i32) -> Bool ---
- @(link_name="GetExitCodeThread") get_exit_code_thread :: proc(thread: Handle, exit_code: ^u32) -> Bool ---
- @(link_name="TerminateThread") terminate_thread :: proc(thread: Handle, exit_code: u32) -> Bool ---
-
- @(link_name="InitializeCriticalSection") initialize_critical_section :: proc(critical_section: ^Critical_Section) ---
- @(link_name="InitializeCriticalSectionAndSpinCount") initialize_critical_section_and_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> b32 ---
- @(link_name="DeleteCriticalSection") delete_critical_section :: proc(critical_section: ^Critical_Section) ---
- @(link_name="SetCriticalSectionSpinCount") set_critical_section_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> u32 ---
- @(link_name="TryEnterCriticalSection") try_enter_critical_section :: proc(critical_section: ^Critical_Section) -> b8 ---
- @(link_name="EnterCriticalSection") enter_critical_section :: proc(critical_section: ^Critical_Section) ---
- @(link_name="LeaveCriticalSection") leave_critical_section :: proc(critical_section: ^Critical_Section) ---
-
- @(link_name="CreateEventA") create_event_a :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: cstring) -> Handle ---
- @(link_name="CreateEventW") create_event_w :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: Wstring) -> Handle ---
- @(link_name="PulseEvent") pulse_event :: proc(event: Handle) -> Bool ---
- @(link_name="SetEvent") set_event :: proc(event: Handle) -> Bool ---
- @(link_name="ResetEvent") reset_event :: proc(event: Handle) -> Bool ---
-
- @(link_name="LoadLibraryA") load_library_a :: proc(c_str: cstring) -> Hmodule ---
- @(link_name="LoadLibraryW") load_library_w :: proc(c_str: Wstring) -> Hmodule ---
- @(link_name="FreeLibrary") free_library :: proc(h: Hmodule) -> Bool ---
- @(link_name="GetProcAddress") get_proc_address :: proc(h: Hmodule, c_str: cstring) -> rawptr ---
-
- @(link_name="GetFullPathNameA") get_full_path_name_a :: proc(filename: cstring, buffer_length: u32, buffer: cstring, file_part: ^Wstring) -> u32 ---
- @(link_name="GetFullPathNameW") get_full_path_name_w :: proc(filename: Wstring, buffer_length: u32, buffer: Wstring, file_part: ^Wstring) -> u32 ---
- @(link_name="GetLongPathNameA") get_long_path_name_a :: proc(short, long: cstring, len: u32) -> u32 ---
- @(link_name="GetLongPathNameW") get_long_path_name_w :: proc(short, long: Wstring, len: u32) -> u32 ---
- @(link_name="GetShortPathNameA") get_short_path_name_a :: proc(long, short: cstring, len: u32) -> u32 ---
- @(link_name="GetShortPathNameW") get_short_path_name_w :: proc(long, short: Wstring, len: u32) -> u32 ---
-
- @(link_name="GetCurrentDirectoryA") get_current_directory_a :: proc(buffer_length: u32, buffer: cstring) -> u32 ---
-}
-
-Memory_Basic_Information :: struct {
- base_address: rawptr,
- allocation_base: rawptr,
- allocation_protect: u32,
- region_size: uint,
- state: u32,
- protect: u32,
- type: u32,
-}
-
-@(default_calling_convention = "std")
-foreign kernel32 {
- @(link_name="VirtualAlloc") virtual_alloc :: proc(address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr ---
- @(link_name="VirtualAllocEx") virtual_alloc_ex :: proc(process: Handle, address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr ---
- @(link_name="VirtualFree") virtual_free :: proc(address: rawptr, size: uint, free_type: u32) -> Bool ---
- @(link_name="VirtualLock") virtual_lock :: proc(address: rawptr, size: uint) -> Bool ---
- @(link_name="VirtualProtect") virtual_protect :: proc(address: rawptr, size: uint, new_protect: u32, old_protect: ^u32) -> Bool ---
- @(link_name="VirtualQuery") virtual_query :: proc(address: rawptr, buffer: ^Memory_Basic_Information, length: uint) -> uint ---
-}
-
-MEM_COMMIT :: 0x00001000
-MEM_RESERVE :: 0x00002000
-MEM_DECOMMIT :: 0x00004000
-MEM_RELEASE :: 0x00008000
-MEM_RESET :: 0x00080000
-MEM_RESET_UNDO :: 0x01000000
-
-MEM_LARGE_PAGES :: 0x20000000
-MEM_PHYSICAL :: 0x00400000
-MEM_TOP_DOWN :: 0x00100000
-MEM_WRITE_WATCH :: 0x00200000
-
-PAGE_NOACCESS :: 0x01
-PAGE_READONLY :: 0x02
-PAGE_READWRITE :: 0x04
-PAGE_WRITECOPY :: 0x08
-PAGE_EXECUTE :: 0x10
-PAGE_EXECUTE_READ :: 0x20
-PAGE_EXECUTE_READWRITE :: 0x40
-PAGE_EXECUTE_WRITECOPY :: 0x80
diff --git a/core/sys/win32/ole32.odin b/core/sys/win32/ole32.odin
deleted file mode 100644
index f4ee52399..000000000
--- a/core/sys/win32/ole32.odin
+++ /dev/null
@@ -1,18 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:ole32.lib"
-
-//objbase.h
-Com_Init :: enum {
- Multi_Threaded = 0x0,
- Apartment_Threaded = 0x2,
- Disable_OLE1_DDE = 0x4,
- Speed_Over_Memory = 0x8,
-}
-
-@(default_calling_convention = "std")
-foreign ole32 {
- @(link_name ="CoInitializeEx") com_init_ex :: proc(reserved: rawptr, co_init: Com_Init) ->Hresult ---
- @(link_name = "CoUninitialize") com_shutdown :: proc() ---
-}
diff --git a/core/sys/win32/removal.odin b/core/sys/win32/removal.odin
new file mode 100644
index 000000000..09c16aa05
--- /dev/null
+++ b/core/sys/win32/removal.odin
@@ -0,0 +1,3 @@
+package sys_win32
+
+#panic(`"core:sys/win32" has been removed. Please use "core:sys/windows"`)
\ No newline at end of file
diff --git a/core/sys/win32/shell32.odin b/core/sys/win32/shell32.odin
deleted file mode 100644
index 3cedf0527..000000000
--- a/core/sys/win32/shell32.odin
+++ /dev/null
@@ -1,9 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:shell32.lib"
-
-@(default_calling_convention = "std")
-foreign shell32 {
- @(link_name="CommandLineToArgvW") command_line_to_argv_w :: proc(cmd_list: Wstring, num_args: ^i32) -> ^Wstring ---
-}
diff --git a/core/sys/win32/tests/general.odin b/core/sys/win32/tests/general.odin
deleted file mode 100644
index 1a5fc911a..000000000
--- a/core/sys/win32/tests/general.odin
+++ /dev/null
@@ -1,41 +0,0 @@
-package win32_tests
-
-import "core:sys/win32"
-import "core:testing"
-
-utf16_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
- result := win32.utf16_to_utf8(str[:]);
- testing.expect(t, (result == comparison) == expected_result, "Incorrect utf16_to_utf8 conversion", loc);
-}
-
-wstring_to_utf8 :: proc(t: ^testing.T, str: []u16, comparison: string, expected_result: bool, loc := #caller_location) {
- result := win32.wstring_to_utf8(nil if len(str) == 0 else cast(win32.Wstring)&str[0], -1);
- testing.expect(t, (result == comparison) == expected_result, "Incorrect wstring_to_utf8 conversion", loc);
-}
-
-@test
-test_utf :: proc(t: ^testing.T) {
- utf16_to_utf8(t, []u16{}, "", true);
- utf16_to_utf8(t, []u16{0}, "", true);
- utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
- utf16_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
- utf16_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", true);
- utf16_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
- utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
- utf16_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
-
- wstring_to_utf8(t, []u16{}, "", true);
- wstring_to_utf8(t, []u16{0}, "", true);
- wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't'}, "", true);
- wstring_to_utf8(t, []u16{0, 't', 'e', 's', 't', 0}, "", true);
- wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}, "test", true);
- wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't'}, "te", true);
- wstring_to_utf8(t, []u16{'t', 'e', 0, 's', 't', 0}, "te", true);
-
- // WARNING: Passing a non-zero-terminated string to wstring_to_utf8 is dangerous,
- // as it will go out of bounds looking for a zero.
- // It will "fail" or "succeed" by having a zero just after the end of the input string or not.
- wstring_to_utf8(t, []u16{'t', 'e', 's', 't'}, "test", false);
- wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 0}[:4], "test", true);
- wstring_to_utf8(t, []u16{'t', 'e', 's', 't', 'q'}[:4], "test", false);
-}
\ No newline at end of file
diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin
deleted file mode 100644
index 593fdbb8e..000000000
--- a/core/sys/win32/user32.odin
+++ /dev/null
@@ -1,284 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:user32.lib"
-
-import "core:intrinsics"
-
-
-Menu_Bar_Info :: struct {
- size: u32,
- bar: Rect,
- menu: Hmenu,
- wnd_menu: Hwnd,
- fields: u8,
- // field.bar_focused: 1,
- // field.focuses: 1,
-}
-
-Menu_Item_Info_A :: struct {
- size: u32,
- mask: u32,
- type: u32,
- state: u32,
- id: u32,
- submenu: Hmenu,
- bmp_checked: Hbitmap,
- bmp_unchecked: Hbitmap,
- item_data: u32,
- type_data: cstring,
- cch: u32,
-}
-Menu_Item_Info_W :: struct {
- size: u32,
- mask: u32,
- type: u32,
- state: u32,
- id: u32,
- submenu: Hmenu,
- bmp_checked: Hbitmap,
- bmp_unchecked: Hbitmap,
- item_data: u32,
- type_data: Wstring,
- cch: u32,
-}
-
-MF_BYCOMMAND :: 0x00000000
-MF_BYPOSITION :: 0x00000400
-MF_BITMAP :: 0x00000004
-MF_CHECKED :: 0x00000008
-MF_DISABLED :: 0x00000002
-MF_ENABLED :: 0x00000000
-MF_GRAYED :: 0x00000001
-MF_MENUBARBREAK :: 0x00000020
-MF_MENUBREAK :: 0x00000040
-MF_OWNERDRAW :: 0x00000100
-MF_POPUP :: 0x00000010
-MF_SEPARATOR :: 0x00000800
-MF_STRING :: 0x00000000
-MF_UNCHECKED :: 0x00000000
-
-MB_ABORTRETRYIGNORE :: 0x00000002
-MB_CANCELTRYCONTINUE :: 0x00000006
-MB_HELP :: 0x00004000
-MB_OK :: 0x00000000
-MB_OKCANCEL :: 0x00000001
-MB_RETRYCANCEL :: 0x00000005
-MB_YESNO :: 0x00000004
-MB_YESNOCANCEL :: 0x00000003
-
-MB_ICONEXCLAMATION :: 0x00000030
-MB_ICONWARNING :: 0x00000030
-MB_ICONINFORMATION :: 0x00000040
-MB_ICONASTERISK :: 0x00000040
-MB_ICONQUESTION :: 0x00000020
-MB_ICONSTOP :: 0x00000010
-MB_ICONERROR :: 0x00000010
-MB_ICONHAND :: 0x00000010
-
-MB_DEFBUTTON1 :: 0x00000000
-MB_DEFBUTTON2 :: 0x00000100
-MB_DEFBUTTON3 :: 0x00000200
-MB_DEFBUTTON4 :: 0x00000300
-
-MB_APPLMODAL :: 0x00000000
-MB_SYSTEMMODAL :: 0x00001000
-MB_TASKMODAL :: 0x00002000
-
-MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
-MB_RIGHT :: 0x00080000
-MB_RTLREADING :: 0x00100000
-MB_SETFOREGROUND :: 0x00010000
-MB_TOPMOST :: 0x00040000
-MB_SERVICE_NOTIFICATION :: 0x00200000
-
-
-@(default_calling_convention = "std")
-foreign user32 {
- @(link_name="GetDesktopWindow") get_desktop_window :: proc() -> Hwnd ---
- when !intrinsics.is_package_imported("raylib") { // NOTE(bill): this is a bit of hack but it's to get around the namespace collisions
- @(link_name="ShowCursor")show_cursor :: proc(show: Bool) -> i32 ---
- }
- @(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool ---
- @(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool ---
- @(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool ---
- @(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool ---
- @(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) ---
- @(link_name="SetWindowTextA") set_window_text_a :: proc(hwnd: Hwnd, c_string: cstring) -> Bool ---
- @(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---
- @(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 ---
- @(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 ---
- @(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---
- @(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---
-
- @(link_name="CreateWindowExA")
- create_window_ex_a :: proc(ex_style: u32,
- class_name, title: cstring,
- style: u32,
- x, y, w, h: i32,
- parent: Hwnd, menu: Hmenu, instance: Hinstance,
- param: rawptr) -> Hwnd ---
-
- @(link_name="CreateWindowExW")
- create_window_ex_w :: proc(ex_style: u32,
- class_name, title: Wstring,
- style: u32,
- x, y, w, h: i32,
- parent: Hwnd, menu: Hmenu, instance: Hinstance,
- param: rawptr) -> Hwnd ---
-
- @(link_name="ShowWindow") show_window :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool ---
- @(link_name="TranslateMessage") translate_message :: proc(msg: ^Msg) -> Bool ---
- @(link_name="DispatchMessageA") dispatch_message_a :: proc(msg: ^Msg) -> Lresult ---
- @(link_name="DispatchMessageW") dispatch_message_w :: proc(msg: ^Msg) -> Lresult ---
- @(link_name="UpdateWindow") update_window :: proc(hwnd: Hwnd) -> Bool ---
- @(link_name="GetMessageA") get_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool ---
- @(link_name="GetMessageW") get_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool ---
-
- @(link_name="PeekMessageA") peek_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool ---
- @(link_name="PeekMessageW") peek_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool ---
-
-
- @(link_name="PostMessageA") post_message_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Bool ---
- @(link_name="PostMessageW") post_message_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Bool ---
- @(link_name="SendMessageA") send_message_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---
- @(link_name="SendMessageW") send_message_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---
-
- @(link_name="DefWindowProcA") def_window_proc_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---
- @(link_name="DefWindowProcW") def_window_proc_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---
-
- @(link_name="AdjustWindowRect") adjust_window_rect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool ---
- @(link_name="GetActiveWindow") get_active_window :: proc() -> Hwnd ---
-
- @(link_name="DestroyWindow") destroy_window :: proc(wnd: Hwnd) -> Bool ---
- @(link_name="DescribePixelFormat") describe_pixel_format :: proc(dc: Hdc, pixel_format: i32, bytes: u32, pfd: ^Pixel_Format_Descriptor) -> i32 ---
-
- @(link_name="GetMonitorInfoA") get_monitor_info_a :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool ---
- @(link_name="MonitorFromWindow") monitor_from_window :: proc(wnd: Hwnd, flags: u32) -> Hmonitor ---
-
- @(link_name="SetWindowPos") set_window_pos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) -> Bool ---
-
- @(link_name="GetWindowPlacement") get_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool ---
- @(link_name="SetWindowPlacement") set_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool ---
- @(link_name="GetWindowRect") get_window_rect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool ---
-
- @(link_name="GetWindowLongPtrA") get_window_long_ptr_a :: proc(wnd: Hwnd, index: i32) -> Long_Ptr ---
- @(link_name="SetWindowLongPtrA") set_window_long_ptr_a :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr ---
- @(link_name="GetWindowLongPtrW") get_window_long_ptr_w :: proc(wnd: Hwnd, index: i32) -> Long_Ptr ---
- @(link_name="SetWindowLongPtrW") set_window_long_ptr_w :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr ---
-
- @(link_name="GetWindowText") get_window_text :: proc(wnd: Hwnd, str: cstring, maxCount: i32) -> i32 ---
-
- @(link_name="GetClientRect") get_client_rect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool ---
-
- @(link_name="GetDC") get_dc :: proc(h: Hwnd) -> Hdc ---
- @(link_name="ReleaseDC") release_dc :: proc(wnd: Hwnd, hdc: Hdc) -> i32 ---
-
- @(link_name="MapVirtualKeyA") map_virtual_key_a :: proc(scancode: u32, map_type: u32) -> u32 ---
- @(link_name="MapVirtualKeyW") map_virtual_key_w :: proc(scancode: u32, map_type: u32) -> u32 ---
-
- @(link_name="GetKeyState") get_key_state :: proc(v_key: i32) -> i16 ---
- @(link_name="GetAsyncKeyState") get_async_key_state :: proc(v_key: i32) -> i16 ---
-
- @(link_name="SetForegroundWindow") set_foreground_window :: proc(h: Hwnd) -> Bool ---
- @(link_name="SetFocus") set_focus :: proc(h: Hwnd) -> Hwnd ---
-
-
- @(link_name="LoadImageA") load_image_a :: proc(instance: Hinstance, name: cstring, type_: u32, x_desired, y_desired : i32, load : u32) -> Handle ---
- @(link_name="LoadIconA") load_icon_a :: proc(instance: Hinstance, icon_name: cstring) -> Hicon ---
- @(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool ---
-
- @(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---
- @(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor ---
- @(link_name="GetCursor") get_cursor :: proc() -> Hcursor ---
- @(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor ---
-
- @(link_name="RegisterRawInputDevices") register_raw_input_devices :: proc(raw_input_device: ^Raw_Input_Device, num_devices, size: u32) -> Bool ---
-
- @(link_name="GetRawInputData") get_raw_input_data :: proc(raw_input: Hrawinput, command: u32, data: rawptr, size: ^u32, size_header: u32) -> u32 ---
-
- @(link_name="MapVirtualKeyExW") map_virtual_key_ex_w :: proc(code, map_type: u32, hkl: HKL) -> u32 ---
- @(link_name="MapVirtualKeyExA") map_virtual_key_ex_a :: proc(code, map_type: u32, hkl: HKL) -> u32 ---
-
- @(link_name="EnumDisplayMonitors") enum_display_monitors :: proc(hdc: Hdc, rect: ^Rect, enum_proc: Monitor_Enum_Proc, lparam: Lparam) -> bool ---
-
- @(link_name="EnumDisplaySettingsA") enum_display_settings_a :: proc(device_name: cstring, mode_number: u32, mode: ^Dev_Mode_A) -> Bool ---
-}
-
-@(default_calling_convention = "std")
-foreign user32 {
- @(link_name="CreateMenu") create_menu :: proc() -> Hmenu ---
- @(link_name="CreatePopupMenu") create_popup_menu :: proc() -> Hmenu ---
- @(link_name="DestroyMenu") destroy_menu :: proc(menu: Hmenu) -> Bool ---
- @(link_name="DeleteMenu") delete_menu :: proc(menu: Hmenu, position: u32, flags: u32) -> Bool ---
-
- @(link_name="EnableMenuItem") enable_menu_item :: proc(menu: Hmenu, id_enable_itme: i32, enable: u32) -> Bool ---
- @(link_name="EndMenu") end_menu :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool ---
- @(link_name="GetMenu") get_menu :: proc(wnd: Hwnd) -> Hmenu ---
- @(link_name="GetMenuBarInfo") get_menu_bar_info :: proc(wnd: Hwnd, id_object, id_item: u32, mbi: ^Menu_Bar_Info) -> Hmenu ---
- @(link_name="GetMenuStringA") get_menu_string_a :: proc(menu: Hmenu, id_item: u32, s: cstring, cch_max: i32, flags: u32) -> i32 ---
- @(link_name="GetMenuStringW") get_menu_string_w :: proc(menu: Hmenu, id_item: u32, s: Wstring, cch_max: i32, flags: u32) -> i32 ---
- @(link_name="GetMenuState") get_menu_state :: proc(menu: Hmenu, id: u32, flags: u32) -> u32 ---
- @(link_name="GetMenuItemRect") get_menu_item_rect :: proc(wnd: Hwnd, menu: Hmenu, id_item: u32, item: ^Rect) -> Bool ---
-
- @(link_name="SetMenu") set_menu :: proc(wnd: Hwnd, menu: Hmenu) -> Bool ---
-
- @(link_name="DrawMenuBar") draw_menu_bar :: proc(wnd: Hwnd) -> Bool ---
- @(link_name="InsertMenuA") insert_menu_a :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool ---
- @(link_name="InsertMenuW") insert_menu_w :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool ---
-
- @(link_name="InsertMenuItemA") insert_menu_item_a :: proc(hmenu: Hmenu, item: u32, fByPosition: Bool, lpmi: ^Menu_Item_Info_A) -> Bool ---
- @(link_name="InsertMenuItemW") insert_menu_item_w :: proc(hmenu: Hmenu, item: u32, fByPosition: Bool, lpmi: ^Menu_Item_Info_W) -> Bool ---
-
- @(link_name="AppendMenuA") append_menu_a :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool ---
- @(link_name="AppendMenuW") append_menu_w :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool ---
-
- @(link_name="CheckMenuItem") check_menu_item :: proc(menu: Hmenu, id_check_item: u32, check: u32) -> u32 ---
- @(link_name="CheckMenuRadioItem") check_menu_radio_item :: proc(menu: Hmenu, first, last: u32, check: u32, flags: u32) -> Bool ---
-
- @(link_name="GetPropA") get_prop_a :: proc(wnd: Hwnd, s: cstring) -> Handle ---
- @(link_name="GetPropW") get_prop_w :: proc(wnd: Hwnd, s: Wstring) -> Handle ---
-
- @(link_name="MessageBoxA") message_box_a :: proc(wnd: Hwnd, text, caption: cstring, type: u32) -> i32 ---
- @(link_name="MessageBoxW") message_box_w :: proc(wnd: Hwnd, text, caption: Wstring, type: u32) -> i32 ---
-
- @(link_name="MessageBoxExA") message_box_ex_a :: proc(wnd: Hwnd, text, caption: cstring, type: u32, language_id: u16) -> i32 ---
- @(link_name="MessageBoxExW") message_box_ex_w :: proc(wnd: Hwnd, text, caption: Wstring, type: u32, language_id: u16) -> i32 ---
-
- @(link_name="BeginPaint") begin_paint :: proc(wnd: Hwnd, paint: ^Paint_Struct) -> Hdc ---
- @(link_name="EndPaint") end_paint :: proc(wnd: Hwnd, paint: ^Paint_Struct) -> Bool ---
-}
-
-
-_IDC_APPSTARTING := rawptr(uintptr(32650))
-_IDC_ARROW := rawptr(uintptr(32512))
-_IDC_CROSS := rawptr(uintptr(32515))
-_IDC_HAND := rawptr(uintptr(32649))
-_IDC_HELP := rawptr(uintptr(32651))
-_IDC_IBEAM := rawptr(uintptr(32513))
-_IDC_ICON := rawptr(uintptr(32641))
-_IDC_NO := rawptr(uintptr(32648))
-_IDC_SIZE := rawptr(uintptr(32640))
-_IDC_SIZEALL := rawptr(uintptr(32646))
-_IDC_SIZENESW := rawptr(uintptr(32643))
-_IDC_SIZENS := rawptr(uintptr(32645))
-_IDC_SIZENWSE := rawptr(uintptr(32642))
-_IDC_SIZEWE := rawptr(uintptr(32644))
-_IDC_UPARROW := rawptr(uintptr(32516))
-_IDC_WAIT := rawptr(uintptr(32514))
-IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
-IDC_ARROW := cstring(_IDC_ARROW)
-IDC_CROSS := cstring(_IDC_CROSS)
-IDC_HAND := cstring(_IDC_HAND)
-IDC_HELP := cstring(_IDC_HELP)
-IDC_IBEAM := cstring(_IDC_IBEAM)
-IDC_ICON := cstring(_IDC_ICON)
-IDC_NO := cstring(_IDC_NO)
-IDC_SIZE := cstring(_IDC_SIZE)
-IDC_SIZEALL := cstring(_IDC_SIZEALL)
-IDC_SIZENESW := cstring(_IDC_SIZENESW)
-IDC_SIZENS := cstring(_IDC_SIZENS)
-IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
-IDC_SIZEWE := cstring(_IDC_SIZEWE)
-IDC_UPARROW := cstring(_IDC_UPARROW)
-IDC_WAIT := cstring(_IDC_WAIT)
diff --git a/core/sys/win32/wgl.odin b/core/sys/win32/wgl.odin
deleted file mode 100644
index e6c414b0e..000000000
--- a/core/sys/win32/wgl.odin
+++ /dev/null
@@ -1,114 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:opengl32.lib"
-
-CONTEXT_MAJOR_VERSION_ARB :: 0x2091
-CONTEXT_MINOR_VERSION_ARB :: 0x2092
-CONTEXT_FLAGS_ARB :: 0x2094
-CONTEXT_PROFILE_MASK_ARB :: 0x9126
-CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002
-CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001
-CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002
-
-Hglrc :: distinct Handle
-Color_Ref :: distinct u32
-
-Layer_Plane_Descriptor :: struct {
- size: u16,
- version: u16,
- flags: u32,
- pixel_type: u8,
- color_bits: u8,
- red_bits: u8,
- red_shift: u8,
- green_bits: u8,
- green_shift: u8,
- blue_bits: u8,
- blue_shift: u8,
- alpha_bits: u8,
- alpha_shift: u8,
- accum_bits: u8,
- accum_red_bits: u8,
- accum_green_bits: u8,
- accum_blue_bits: u8,
- accum_alpha_bits: u8,
- depth_bits: u8,
- stencil_bits: u8,
- aux_buffers: u8,
- layer_type: u8,
- reserved: u8,
- transparent: Color_Ref,
-}
-
-Point_Float :: struct {x, y: f32}
-
-Glyph_Metrics_Float :: struct {
- black_box_x: f32,
- black_box_y: f32,
- glyph_origin: Point_Float,
- cell_inc_x: f32,
- cell_inc_y: f32,
-}
-
-Create_Context_Attribs_ARB_Type :: #type proc "c" (hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc
-Choose_Pixel_Format_ARB_Type :: #type proc "c" (hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool
-Swap_Interval_EXT_Type :: #type proc "c" (interval: i32) -> bool
-Get_Extensions_String_ARB_Type :: #type proc "c" (Hdc) -> cstring
-
-// Procedures
- create_context_attribs_arb: Create_Context_Attribs_ARB_Type
- choose_pixel_format_arb: Choose_Pixel_Format_ARB_Type
- swap_interval_ext: Swap_Interval_EXT_Type
- get_extensions_string_arb: Get_Extensions_String_ARB_Type
-
-
-foreign opengl32 {
- @(link_name="wglCreateContext")
- create_context :: proc(hdc: Hdc) -> Hglrc ---
-
- @(link_name="wglMakeCurrent")
- make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool ---
-
- @(link_name="wglGetProcAddress")
- get_gl_proc_address :: proc(c_str: cstring) -> rawptr ---
-
- @(link_name="wglDeleteContext")
- delete_context :: proc(hglrc: Hglrc) -> Bool ---
-
- @(link_name="wglCopyContext")
- copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool ---
-
- @(link_name="wglCreateLayerContext")
- create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc ---
-
- @(link_name="wglDescribeLayerPlane")
- describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool ---
-
- @(link_name="wglGetCurrentContext")
- get_current_context :: proc() -> Hglrc ---
-
- @(link_name="wglGetCurrentDC")
- get_current_dc :: proc() -> Hdc ---
-
- @(link_name="wglGetLayerPaletteEntries")
- get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 ---
-
- @(link_name="wglRealizeLayerPalette")
- realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool ---
-
- @(link_name="wglSetLayerPaletteEntries")
- set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 ---
-
- @(link_name="wglShareLists")
- share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool ---
-
- @(link_name="wglSwapLayerBuffers")
- swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool ---
-
- @(link_name="wglUseFontBitmaps")
- use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool ---
-
- @(link_name="wglUseFontOutlines")
- use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool ---
-}
diff --git a/core/sys/win32/winmm.odin b/core/sys/win32/winmm.odin
deleted file mode 100644
index 0f567fbcc..000000000
--- a/core/sys/win32/winmm.odin
+++ /dev/null
@@ -1,12 +0,0 @@
-// +build windows
-package win32
-
-foreign import "system:winmm.lib"
-
-
-@(default_calling_convention = "std")
-foreign winmm {
- @(link_name="timeBeginPeriod") time_begin_period :: proc(period: u32) -> u32 ---
-
- @(link_name="timeGetTime") time_get_time :: proc() -> u32 ---
-}
diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin
index a2a24242f..82031d4f7 100644
--- a/core/sys/windows/advapi32.odin
+++ b/core/sys/windows/advapi32.odin
@@ -3,6 +3,8 @@ package sys_windows
foreign import advapi32 "system:Advapi32.lib"
+HCRYPTPROV :: distinct HANDLE
+
@(default_calling_convention="stdcall")
foreign advapi32 {
@(link_name = "SystemFunction036")
@@ -10,6 +12,10 @@ foreign advapi32 {
OpenProcessToken :: proc(ProcessHandle: HANDLE,
DesiredAccess: DWORD,
TokenHandle: ^HANDLE) -> BOOL ---
+
+ CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD ---
+ CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
+ CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---
}
// Necessary to create a token to impersonate a user with for CreateProcessAsUser
@@ -64,4 +70,62 @@ foreign advapi32 {
lpStartupInfo: LPSTARTUPINFO,
lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL ---
-}
\ No newline at end of file
+
+ RegCreateKeyExW :: proc(
+ hKey: HKEY,
+ lpSubKey: LPCWSTR,
+ Reserved: DWORD,
+ lpClass: LPWSTR,
+ dwOptions: DWORD,
+ samDesired: REGSAM,
+ lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
+ phkResult: PHKEY,
+ lpdwDisposition: LPDWORD,
+ ) -> LSTATUS ---
+
+ RegOpenKeyW :: proc(
+ hKey: HKEY,
+ lpSubKey: LPCWSTR,
+ phkResult: PHKEY,
+ ) -> LSTATUS ---
+
+ RegOpenKeyExW :: proc(
+ hKey: HKEY,
+ lpSubKey: LPCWSTR,
+ ulOptions: DWORD,
+ samDesired: REGSAM,
+ phkResult: PHKEY,
+ ) -> LSTATUS ---
+
+ RegCloseKey :: proc(
+ hKey: HKEY,
+ ) -> LSTATUS ---
+
+ RegGetValueW :: proc(
+ hkey: HKEY,
+ lpSubKey: LPCWSTR,
+ lpValue: LPCWSTR,
+ dwFlags: DWORD,
+ pdwType: LPDWORD,
+ pvData: PVOID,
+ pcbData: LPDWORD,
+ ) -> LSTATUS ---
+
+ RegSetValueExW :: proc(
+ hKey: HKEY,
+ lpValueName: LPCWSTR,
+ Reserved: DWORD,
+ dwType: DWORD,
+ lpData: ^BYTE,
+ cbData: DWORD,
+ ) -> LSTATUS ---
+
+ RegSetKeyValueW :: proc(
+ hKey: HKEY,
+ lpSubKey: LPCWSTR,
+ lpValueName: LPCWSTR,
+ dwType: DWORD,
+ lpData: LPCVOID,
+ cbData: DWORD,
+ ) -> LSTATUS ---
+}
diff --git a/core/sys/windows/bcrypt.odin b/core/sys/windows/bcrypt.odin
index ed28d5b7f..52eb4b1b6 100644
--- a/core/sys/windows/bcrypt.odin
+++ b/core/sys/windows/bcrypt.odin
@@ -7,5 +7,5 @@ BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD : 0x00000002
@(default_calling_convention="stdcall")
foreign bcrypt {
- BCryptGenRandom :: proc(hAlgorithm: LPVOID, pBuffer: ^u8, cbBuffer: ULONG, dwFlags: ULONG) -> LONG ---
+ BCryptGenRandom :: proc(hAlgorithm: LPVOID, pBuffer: [^]u8, cbBuffer: ULONG, dwFlags: ULONG) -> LONG ---
}
diff --git a/core/sys/windows/bluetooth.odin b/core/sys/windows/bluetooth.odin
index c9f6bcc93..c2534896b 100644
--- a/core/sys/windows/bluetooth.odin
+++ b/core/sys/windows/bluetooth.odin
@@ -51,54 +51,27 @@ BLUETOOTH_DEVICE_INFO :: struct {
name: [BLUETOOTH_MAX_NAME_SIZE]u16, // Name of the device
}
-@(default_calling_convention = "std")
+@(default_calling_convention="stdcall")
foreign bthprops {
/*
Version
*/
- @(link_name="BluetoothIsVersionAvailable") bluetooth_is_version_available :: proc(
- major: u8, minor: u8,
- ) -> BOOL ---
+ BluetoothIsVersionAvailable :: proc(major: u8, minor: u8) -> BOOL ---
/*
Radio enumeration
*/
- @(link_name="BluetoothFindFirstRadio") bluetooth_find_first_radio :: proc(
- find_radio_params: ^BLUETOOTH_FIND_RADIO_PARAMS, radio: ^HANDLE,
- ) -> HBLUETOOTH_RADIO_FIND ---
-
- @(link_name="BluetoothFindNextRadio") bluetooth_find_next_radio :: proc(
- handle: HBLUETOOTH_RADIO_FIND, radio: ^HANDLE,
- ) -> BOOL ---
-
- @(link_name="BluetoothFindRadioClose") bluetooth_find_radio_close :: proc(
- handle: HBLUETOOTH_RADIO_FIND,
- ) -> BOOL ---
-
- @(link_name="BluetoothGetRadioInfo") bluetooth_get_radio_info :: proc(
- radio: HANDLE, radio_info: ^BLUETOOTH_RADIO_INFO,
- ) -> DWORD ---
+ BluetoothFindFirstRadio :: proc(find_radio_params: ^BLUETOOTH_FIND_RADIO_PARAMS, radio: ^HANDLE) -> HBLUETOOTH_RADIO_FIND ---
+ BluetoothFindNextRadio :: proc(handle: HBLUETOOTH_RADIO_FIND, radio: ^HANDLE) -> BOOL ---
+ BluetoothFindRadioClose :: proc(handle: HBLUETOOTH_RADIO_FIND) -> BOOL ---
+ BluetoothGetRadioInfo :: proc(radio: HANDLE, radio_info: ^BLUETOOTH_RADIO_INFO) -> DWORD ---
/*
Device enumeration
*/
- @(link_name="BluetoothFindFirstDevice") bluetooth_find_first_device :: proc(
- search_params: ^BLUETOOTH_DEVICE_SEARCH_PARAMS, device_info: ^BLUETOOTH_DEVICE_INFO,
- ) -> HBLUETOOTH_DEVICE_FIND ---
-
- @(link_name="BluetoothFindNextDevice") bluetooth_find_next_device :: proc(
- handle: HBLUETOOTH_DEVICE_FIND, device_info: ^BLUETOOTH_DEVICE_INFO,
- ) -> BOOL ---
-
- @(link_name="BluetoothFindDeviceClose") bluetooth_find_device_close :: proc(
- handle: HBLUETOOTH_DEVICE_FIND,
- ) -> BOOL ---
-
- @(link_name="BluetoothGetDeviceInfo") bluetooth_get_device_info :: proc(
- radio: HANDLE, device_info: ^BLUETOOTH_DEVICE_INFO,
- ) -> DWORD ---
-
- @(link_name="BluetoothDisplayDeviceProperties") bluetooth_display_device_properties :: proc(
- hwnd_parent: HWND, device_info: ^BLUETOOTH_DEVICE_INFO,
- ) -> BOOL ---
+ BluetoothFindFirstDevice :: proc(search_params: ^BLUETOOTH_DEVICE_SEARCH_PARAMS, device_info: ^BLUETOOTH_DEVICE_INFO) -> HBLUETOOTH_DEVICE_FIND ---
+ BluetoothFindNextDevice :: proc(handle: HBLUETOOTH_DEVICE_FIND, device_info: ^BLUETOOTH_DEVICE_INFO) -> BOOL ---
+ BluetoothFindDeviceClose :: proc(handle: HBLUETOOTH_DEVICE_FIND) -> BOOL ---
+ BluetoothGetDeviceInfo :: proc(radio: HANDLE, device_info: ^BLUETOOTH_DEVICE_INFO) -> DWORD ---
+ BluetoothDisplayDeviceProperties :: proc(hwnd_parent: HWND, device_info: ^BLUETOOTH_DEVICE_INFO) -> BOOL ---
}
\ No newline at end of file
diff --git a/core/sys/win32/comdlg32.odin b/core/sys/windows/comdlg32.odin
similarity index 57%
rename from core/sys/win32/comdlg32.odin
rename to core/sys/windows/comdlg32.odin
index 1e0d5c3ca..8284050f1 100644
--- a/core/sys/win32/comdlg32.odin
+++ b/core/sys/windows/comdlg32.odin
@@ -1,70 +1,41 @@
// +build windows
-package win32
+package sys_windows
-foreign import "system:comdlg32.lib"
-import "core:strings"
+foreign import "system:Comdlg32.lib"
-OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr
+LPOFNHOOKPROC :: #type proc "stdcall" (hdlg: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> UINT_PTR
-Open_File_Name_A :: struct {
- struct_size: u32,
- hwnd_owner: Hwnd,
- instance: Hinstance,
- filter: cstring,
- custom_filter: cstring,
- max_cust_filter: u32,
- filter_index: u32,
- file: cstring,
- max_file: u32,
- file_title: cstring,
- max_file_title: u32,
- initial_dir: cstring,
- title: cstring,
- flags: u32,
- file_offset: u16,
- file_extension: u16,
- def_ext: cstring,
- cust_data: Lparam,
- hook: OFN_Hook_Proc,
- template_name: cstring,
- pv_reserved: rawptr,
- dw_reserved: u32,
- flags_ex: u32,
+OPENFILENAMEW :: struct {
+ lStructSize: DWORD,
+ hwndOwner: HWND,
+ hInstance: HINSTANCE,
+ lpstrFilter: wstring,
+ lpstrCustomFilter: wstring,
+ nMaxCustFilter: DWORD,
+ nFilterIndex: DWORD,
+ lpstrFile: wstring,
+ nMaxFile: DWORD,
+ lpstrFileTitle: wstring,
+ nMaxFileTitle: DWORD,
+ lpstrInitialDir: wstring,
+ lpstrTitle: wstring,
+ Flags: DWORD,
+ nFileOffset: WORD,
+ nFileExtension: WORD,
+ lpstrDefExt: wstring,
+ lCustData: LPARAM,
+ lpfnHook: LPOFNHOOKPROC,
+ lpTemplateName: wstring,
+ pvReserved: rawptr,
+ dwReserved: DWORD,
+ FlagsEx: DWORD,
}
-Open_File_Name_W :: struct {
- struct_size: u32,
- hwnd_owner: Hwnd,
- instance: Hinstance,
- filter: Wstring,
- custom_filter: Wstring,
- max_cust_filter: u32,
- filter_index: u32,
- file: Wstring,
- max_file: u32,
- file_title: Wstring,
- max_file_title: u32,
- initial_dir: Wstring,
- title: Wstring,
- flags: u32,
- file_offset: u16,
- file_extension: u16,
- def_ext: Wstring,
- cust_data: Lparam,
- hook: OFN_Hook_Proc,
- template_name: Wstring,
- pv_reserved: rawptr,
- dw_reserved: u32,
- flags_ex: u32,
-}
-
-@(default_calling_convention = "c")
-foreign comdlg32 {
- @(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
- @(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
- @(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
- @(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
- @(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 ---
+@(default_calling_convention="stdcall")
+foreign Comdlg32 {
+ GetOpenFileNameW :: proc(arg1: ^OPENFILENAMEW) -> BOOL ---
+ GetSaveFileNameW :: proc(arg1: ^OPENFILENAMEW) -> BOOL ---
+ CommDlgExtendedError :: proc() -> u32 ---
}
OPEN_TITLE :: "Select file to open"
@@ -75,6 +46,9 @@ SAVE_TITLE :: "Select file to save"
SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER)
SAVE_EXT :: "txt"
+/*
+import "core:strings"
+
Open_Save_Mode :: enum {
Open = 0,
Save = 1,
@@ -100,23 +74,23 @@ _open_file_dialog :: proc(title: string, dir: string,
filter = strings.join(filters, "\u0000", context.temp_allocator)
filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator)
- ofn := Open_File_Name_W{
- struct_size = size_of(Open_File_Name_W),
- file = Wstring(&file_buf[0]),
- max_file = MAX_PATH_WIDE,
- title = utf8_to_wstring(title, context.temp_allocator),
- filter = utf8_to_wstring(filter, context.temp_allocator),
- initial_dir = utf8_to_wstring(dir, context.temp_allocator),
- filter_index = u32(clamp(default_filter, 1, filter_len / 2)),
- def_ext = utf8_to_wstring(default_ext, context.temp_allocator),
- flags = u32(flags),
+ ofn := OPENFILENAMEW{
+ lStructSize = size_of(OPENFILENAMEW),
+ lpstrFile = wstring(&file_buf[0]),
+ nMaxFile = MAX_PATH_WIDE,
+ lpstrTitle = utf8_to_wstring(title, context.temp_allocator),
+ lpstrFilter = utf8_to_wstring(filter, context.temp_allocator),
+ lpstrInitialDir = utf8_to_wstring(dir, context.temp_allocator),
+ nFilterIndex = u32(clamp(default_filter, 1, filter_len / 2)),
+ lpstrDefExt = utf8_to_wstring(default_ext, context.temp_allocator),
+ Flags = u32(flags),
}
switch mode {
case .Open:
- ok = bool(get_open_file_name_w(&ofn))
+ ok = bool(GetOpenFileNameW(&ofn))
case .Save:
- ok = bool(get_save_file_name_w(&ofn))
+ ok = bool(GetSaveFileNameW(&ofn))
case:
ok = false
}
@@ -124,9 +98,9 @@ _open_file_dialog :: proc(title: string, dir: string,
if !ok {
return
}
-
- file_name := utf16_to_utf8(file_buf[:], allocator)
+
+ file_name, _ := utf16_to_utf8(file_buf[:], allocator)
path = strings.trim_right_null(file_name)
return
}
@@ -140,13 +114,14 @@ select_file_to_open :: proc(title := OPEN_TITLE, dir := ".",
}
select_file_to_save :: proc(title := SAVE_TITLE, dir := ".",
- filters := []string{"All Files", "*.*"}, default_filter := u32(1),
- flags := SAVE_FLAGS, default_ext := SAVE_EXT,
- allocator := context.temp_allocator) -> (path: string, ok: bool) {
+ filters := []string{"All Files", "*.*"}, default_filter := u32(1),
+ flags := SAVE_FLAGS, default_ext := SAVE_EXT,
+ allocator := context.temp_allocator) -> (path: string, ok: bool) {
path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, Open_Save_Mode.Save, allocator)
return
}
+*/
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.
diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin
new file mode 100644
index 000000000..468b5bb87
--- /dev/null
+++ b/core/sys/windows/dwmapi.odin
@@ -0,0 +1,9 @@
+// +build windows
+package sys_windows
+
+foreign import dwmapi "system:Dwmapi.lib"
+
+@(default_calling_convention="stdcall")
+foreign dwmapi {
+ DwmFlush :: proc() -> HRESULT ---
+}
diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin
new file mode 100644
index 000000000..4403a5dc3
--- /dev/null
+++ b/core/sys/windows/gdi32.odin
@@ -0,0 +1,84 @@
+// +build windows
+package sys_windows
+
+foreign import gdi32 "system:Gdi32.lib"
+
+@(default_calling_convention="stdcall")
+foreign gdi32 {
+ GetStockObject :: proc(i: c_int) -> HGDIOBJ ---
+ SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
+ DeleteObject :: proc(ho: HGDIOBJ) -> BOOL ---
+ SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
+
+ CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
+
+ CreateDIBitmap :: proc(
+ hdc: HDC,
+ pbmih: ^BITMAPINFOHEADER,
+ flInit: DWORD,
+ pjBits: VOID,
+ pbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ ) -> HBITMAP ---
+
+ CreateDIBSection :: proc(
+ hdc: HDC,
+ pbmi: ^BITMAPINFO,
+ usage: UINT,
+ ppvBits: VOID,
+ hSection: HANDLE,
+ offset: DWORD,
+ ) -> HBITMAP ---
+
+ StretchDIBits :: proc(
+ hdc: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ DestWidth: c_int,
+ DestHeight: c_int,
+ xSrc: c_int,
+ ySrc: c_int,
+ SrcWidth: c_int,
+ SrcHeight: c_int,
+ lpBits: VOID,
+ lpbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ rop: DWORD,
+ ) -> c_int ---
+
+ StretchBlt :: proc(
+ hdcDest: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ wDest: c_int,
+ hDest: c_int,
+ hdcSrc: HDC,
+ xSrc: c_int,
+ ySrc: c_int,
+ wSrc: c_int,
+ hSrc: c_int,
+ rop: DWORD,
+ ) -> BOOL ---
+
+ SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
+ ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
+ SwapBuffers :: proc(HDC) -> BOOL ---
+
+ SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
+ GetDCBrushColor :: proc(hdc: HDC) -> COLORREF ---
+ PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
+ Rectangle :: proc(hdc: HDC, left, top, right, bottom: c_int) -> BOOL ---
+
+ CreateFontW :: proc(
+ cHeight, cWidth, cEscapement, cOrientation, cWeight: c_int,
+ bItalic, bUnderline, bStrikeOut, iCharSet, iOutPrecision: DWORD,
+ iClipPrecision, iQuality, iPitchAndFamily: DWORD,
+ pszFaceName: LPCWSTR,
+ ) -> HFONT ---
+ TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
+ GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
+}
+
+RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
+ return transmute(COLORREF)[4]u8{r, g, b, 0}
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 8c58fbd52..284936852 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -3,11 +3,10 @@ package sys_windows
foreign import kernel32 "system:Kernel32.lib"
-
-
@(default_calling_convention="stdcall")
foreign kernel32 {
- OutputDebugStringA :: proc(lpOutputString: LPCSTR) ---
+ OutputDebugStringA :: proc(lpOutputString: LPCSTR) --- // The only A thing that is allowed
+ OutputDebugStringW :: proc(lpOutputString: LPCWSTR) ---
ReadConsoleW :: proc(hConsoleInput: HANDLE,
lpBuffer: LPVOID,
@@ -23,12 +22,18 @@ foreign kernel32 {
GetConsoleMode :: proc(hConsoleHandle: HANDLE,
lpMode: LPDWORD) -> BOOL ---
-
+ SetConsoleMode :: proc(hConsoleHandle: HANDLE,
+ dwMode: DWORD) -> BOOL ---
GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
SetHandleInformation :: proc(hObject: HANDLE,
dwMask: DWORD,
dwFlags: DWORD) -> BOOL ---
+ SetFileInformationByHandle :: proc(hFile: HANDLE,
+ FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
+ lpFileInformation: LPVOID,
+ dwBufferSize: DWORD) -> BOOL ---
+
AddVectoredExceptionHandler :: proc(FirstHandler: ULONG, VectoredHandler: PVECTORED_EXCEPTION_HANDLER) -> LPVOID ---
AddVectoredContinueHandler :: proc(FirstHandler: ULONG, VectoredHandler: PVECTORED_EXCEPTION_HANDLER) -> LPVOID ---
@@ -62,6 +67,13 @@ foreign kernel32 {
GetCurrentProcessId :: proc() -> DWORD ---
GetCurrentThread :: proc() -> HANDLE ---
GetCurrentThreadId :: proc() -> DWORD ---
+ GetProcessTimes :: proc(
+ hProcess: HANDLE,
+ lpCreationTime: LPFILETIME,
+ lpExitTime: LPFILETIME,
+ lpKernelTime: LPFILETIME,
+ lpUserTime: LPFILETIME,
+ ) -> BOOL ---
GetStdHandle :: proc(which: DWORD) -> HANDLE ---
ExitProcess :: proc(uExitCode: c_uint) -> ! ---
DeviceIoControl :: proc(
@@ -89,9 +101,23 @@ foreign kernel32 {
GetExitCodeThread :: proc(thread: HANDLE, exit_code: ^DWORD) -> BOOL ---
TerminateThread :: proc(thread: HANDLE, exit_code: DWORD) -> BOOL ---
- CreateSemaphoreW :: proc(attributes: LPSECURITY_ATTRIBUTES, initial_count, maximum_count: LONG, name: LPCSTR) -> HANDLE ---
+ CreateSemaphoreW :: proc(attributes: LPSECURITY_ATTRIBUTES, initial_count, maximum_count: LONG, name: LPCWSTR) -> HANDLE ---
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: LONG, previous_count: ^LONG) -> BOOL ---
+ CreateWaitableTimerW :: proc(
+ lpTimerAttributes: LPSECURITY_ATTRIBUTES,
+ bManualReset: BOOL,
+ lpTimerName: LPCWSTR,
+ ) -> HANDLE ---
+ SetWaitableTimerEx :: proc(
+ hTimer: HANDLE,
+ lpDueTime: ^LARGE_INTEGER,
+ lPeriod: LONG,
+ pfnCompletionRoutine: PTIMERAPCROUTINE,
+ lpArgToCompletionRoutine: LPVOID,
+ WakeContext: PREASON_CONTEXT,
+ TolerableDelay: ULONG,
+ ) -> BOOL ---
WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD ---
Sleep :: proc(dwMilliseconds: DWORD) ---
GetProcessId :: proc(handle: HANDLE) -> DWORD ---
@@ -217,6 +243,7 @@ foreign kernel32 {
bInitialState: BOOL,
lpName: LPCWSTR,
) -> HANDLE ---
+ ResetEvent :: proc(hEvent: HANDLE) -> BOOL ---
WaitForMultipleObjects :: proc(
nCount: DWORD,
lpHandles: ^HANDLE,
@@ -245,6 +272,22 @@ foreign kernel32 {
HeapReAlloc :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID ---
HeapFree :: proc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL ---
+ LocalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID ---
+ LocalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID ---
+ LocalFree :: proc(mem: LPVOID) -> LPVOID ---
+
+
+ ReadDirectoryChangesW :: proc(
+ hDirectory: HANDLE,
+ lpBuffer: LPVOID,
+ nBufferLength: DWORD,
+ bWatchSubtree: BOOL,
+ dwNotifyFilter: DWORD,
+ lpBytesReturned: LPDWORD,
+ lpOverlapped: LPOVERLAPPED,
+ lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
+ ) -> BOOL ---
+
InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
TryAcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) -> BOOL ---
@@ -286,7 +329,6 @@ foreign kernel32 {
}
-STANDARD_RIGHTS_REQUIRED :: DWORD(0x000F0000)
SECTION_QUERY :: DWORD(0x0001)
SECTION_MAP_WRITE :: DWORD(0x0002)
SECTION_MAP_READ :: DWORD(0x0004)
@@ -341,6 +383,7 @@ MEM_TOP_DOWN :: 0x100000
MEM_LARGE_PAGES :: 0x20000000
MEM_4MB_PAGES :: 0x80000000
+@(default_calling_convention="stdcall")
foreign kernel32 {
VirtualAlloc :: proc(
lpAddress: LPVOID,
@@ -483,6 +526,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes
HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification
+@(default_calling_convention="stdcall")
foreign kernel32 {
CreateMemoryResourceNotification :: proc(
NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE,
@@ -498,6 +542,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002)
FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004)
FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetSystemFileCacheSize :: proc(
lpMinimumFileCacheSize: PSIZE_T,
@@ -527,6 +572,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
+@(default_calling_convention="stdcall")
foreign kernel32 {
PrefetchVirtualMemory :: proc(
hProcess: HANDLE,
@@ -584,6 +630,7 @@ foreign kernel32 {
MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetMemoryErrorHandlingCapabilities :: proc(
Capabilities: PULONG,
@@ -592,6 +639,7 @@ foreign kernel32 {
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
+@(default_calling_convention="stdcall")
foreign kernel32 {
RegisterBadMemoryNotification :: proc(
Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -612,6 +660,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow
VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal
+@(default_calling_convention="stdcall")
foreign kernel32 {
OfferVirtualMemory :: proc(
VirtualAddress: PVOID,
@@ -676,6 +725,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
Reserved : 32-6,
}*/
+@(default_calling_convention="stdcall")
foreign kernel32 {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
@@ -700,7 +750,7 @@ foreign kernel32 {
NUMA_NO_PREFERRED_NODE :: 0xffffffff
-MapViewOfFile2 :: #force_inline proc(
+MapViewOfFile2 :: #force_inline proc "stdcall" (
FileMappingHandle: HANDLE,
ProcessHandle: HANDLE,
Offset: ULONG64,
@@ -721,6 +771,7 @@ MapViewOfFile2 :: #force_inline proc(
)
}
+@(default_calling_convention="stdcall")
foreign kernel32 {
UnmapViewOfFile2 :: proc(
ProcessHandle: HANDLE,
@@ -728,3 +779,18 @@ foreign kernel32 {
UnmapFlags: ULONG,
) -> BOOL ---
}
+
+@(default_calling_convention="stdcall")
+foreign kernel32 {
+ @(link_name="SetConsoleCtrlHandler") set_console_ctrl_handler :: proc(handler: Handler_Routine, add: BOOL) -> BOOL ---
+}
+
+Handler_Routine :: proc(dwCtrlType: Control_Event) -> BOOL
+
+Control_Event :: enum DWORD {
+ control_c = 0,
+ _break = 1,
+ close = 2,
+ logoff = 5,
+ shutdown = 6,
+}
diff --git a/core/sys/windows/key_codes.odin b/core/sys/windows/key_codes.odin
new file mode 100644
index 000000000..284b0e437
--- /dev/null
+++ b/core/sys/windows/key_codes.odin
@@ -0,0 +1,260 @@
+// +build windows
+package sys_windows
+
+// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input
+KF_EXTENDED :: 0x0100
+KF_DLGMODE :: 0x0800
+KF_MENUMODE :: 0x1000
+KF_ALTDOWN :: 0x2000
+KF_REPEAT :: 0x4000
+KF_UP :: 0x8000
+
+// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
+// Virtual Keys, Standard Set
+VK_LBUTTON :: 0x01
+VK_RBUTTON :: 0x02
+VK_CANCEL :: 0x03
+VK_MBUTTON :: 0x04 // NOT contiguous with L & RBUTTON
+VK_XBUTTON1 :: 0x05 // NOT contiguous with L & RBUTTON
+VK_XBUTTON2 :: 0x06 // NOT contiguous with L & RBUTTON
+
+// 0x07 : reserved
+
+VK_BACK :: 0x08
+VK_TAB :: 0x09
+
+// 0x0A - 0x0B : reserved
+
+VK_CLEAR :: 0x0C
+VK_RETURN :: 0x0D
+
+// 0x0E - 0x0F : unassigned
+
+VK_SHIFT :: 0x10
+VK_CONTROL :: 0x11
+VK_MENU :: 0x12
+VK_PAUSE :: 0x13
+VK_CAPITAL :: 0x14
+VK_KANA :: 0x15
+VK_HANGEUL :: 0x15 // old name - should be here for compatibility
+VK_HANGUL :: 0x15
+VK_IME_ON :: 0x16
+VK_JUNJA :: 0x17
+VK_FINAL :: 0x18
+VK_HANJA :: 0x19
+VK_KANJI :: 0x19
+VK_IME_OFF :: 0x1A
+VK_ESCAPE :: 0x1B
+VK_CONVERT :: 0x1C
+VK_NONCONVERT :: 0x1D
+VK_ACCEPT :: 0x1E
+VK_MODECHANGE :: 0x1F
+VK_SPACE :: 0x20
+VK_PRIOR :: 0x21
+VK_NEXT :: 0x22
+VK_END :: 0x23
+VK_HOME :: 0x24
+VK_LEFT :: 0x25
+VK_UP :: 0x26
+VK_RIGHT :: 0x27
+VK_DOWN :: 0x28
+VK_SELECT :: 0x29
+VK_PRINT :: 0x2A
+VK_EXECUTE :: 0x2B
+VK_SNAPSHOT :: 0x2C
+VK_INSERT :: 0x2D
+VK_DELETE :: 0x2E
+VK_HELP :: 0x2F
+
+VK_0 :: '0'
+VK_1 :: '1'
+VK_2 :: '2'
+VK_3 :: '3'
+VK_4 :: '4'
+VK_5 :: '5'
+VK_6 :: '6'
+VK_7 :: '7'
+VK_8 :: '8'
+VK_9 :: '9'
+
+// 0x3A - 0x40 : unassigned
+
+VK_A :: 'A'
+VK_B :: 'B'
+VK_C :: 'C'
+VK_D :: 'D'
+VK_E :: 'E'
+VK_F :: 'F'
+VK_G :: 'G'
+VK_H :: 'H'
+VK_I :: 'I'
+VK_J :: 'J'
+VK_K :: 'K'
+VK_L :: 'L'
+VK_M :: 'M'
+VK_N :: 'N'
+VK_O :: 'O'
+VK_P :: 'P'
+VK_Q :: 'Q'
+VK_R :: 'R'
+VK_S :: 'S'
+VK_T :: 'T'
+VK_U :: 'U'
+VK_V :: 'V'
+VK_W :: 'W'
+VK_X :: 'X'
+VK_Y :: 'Y'
+VK_Z :: 'Z'
+
+VK_LWIN :: 0x5B
+VK_RWIN :: 0x5C
+VK_APPS :: 0x5D
+
+// 0x5E : reserved
+
+VK_SLEEP :: 0x5F
+VK_NUMPAD0 :: 0x60
+VK_NUMPAD1 :: 0x61
+VK_NUMPAD2 :: 0x62
+VK_NUMPAD3 :: 0x63
+VK_NUMPAD4 :: 0x64
+VK_NUMPAD5 :: 0x65
+VK_NUMPAD6 :: 0x66
+VK_NUMPAD7 :: 0x67
+VK_NUMPAD8 :: 0x68
+VK_NUMPAD9 :: 0x69
+VK_MULTIPLY :: 0x6A
+VK_ADD :: 0x6B
+VK_SEPARATOR :: 0x6C
+VK_SUBTRACT :: 0x6D
+VK_DECIMAL :: 0x6E
+VK_DIVIDE :: 0x6F
+VK_F1 :: 0x70
+VK_F2 :: 0x71
+VK_F3 :: 0x72
+VK_F4 :: 0x73
+VK_F5 :: 0x74
+VK_F6 :: 0x75
+VK_F7 :: 0x76
+VK_F8 :: 0x77
+VK_F9 :: 0x78
+VK_F10 :: 0x79
+VK_F11 :: 0x7A
+VK_F12 :: 0x7B
+VK_F13 :: 0x7C
+VK_F14 :: 0x7D
+VK_F15 :: 0x7E
+VK_F16 :: 0x7F
+VK_F17 :: 0x80
+VK_F18 :: 0x81
+VK_F19 :: 0x82
+VK_F20 :: 0x83
+VK_F21 :: 0x84
+VK_F22 :: 0x85
+VK_F23 :: 0x86
+VK_F24 :: 0x87
+
+// 0x88 - 0x8F : reserved
+
+VK_NUMLOCK :: 0x90
+VK_SCROLL :: 0x91
+
+// NEC PC-9800 kbd definitions
+VK_OEM_NEC_EQUAL :: 0x92 // '=' key on numpad
+
+// Fujitsu/OASYS kbd definitions
+VK_OEM_FJ_JISHO :: 0x92 // 'Dictionary' key
+VK_OEM_FJ_MASSHOU :: 0x93 // 'Unregister word' key
+VK_OEM_FJ_TOUROKU :: 0x94 // 'Register word' key
+VK_OEM_FJ_LOYA :: 0x95 // 'Left OYAYUBI' key
+VK_OEM_FJ_ROYA :: 0x96 // 'Right OYAYUBI' key
+
+// 0x97 - 0x9F : unassigned
+
+// VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
+// Used only as parameters to GetAsyncKeyState() and GetKeyState().
+// No other API or message will distinguish left and right keys in this way.
+VK_LSHIFT :: 0xA0
+VK_RSHIFT :: 0xA1
+VK_LCONTROL :: 0xA2
+VK_RCONTROL :: 0xA3
+VK_LMENU :: 0xA4
+VK_RMENU :: 0xA5
+
+VK_BROWSER_BACK :: 0xA6
+VK_BROWSER_FORWARD :: 0xA7
+VK_BROWSER_REFRESH :: 0xA8
+VK_BROWSER_STOP :: 0xA9
+VK_BROWSER_SEARCH :: 0xAA
+VK_BROWSER_FAVORITES :: 0xAB
+VK_BROWSER_HOME :: 0xAC
+VK_VOLUME_MUTE :: 0xAD
+VK_VOLUME_DOWN :: 0xAE
+VK_VOLUME_UP :: 0xAF
+VK_MEDIA_NEXT_TRACK :: 0xB0
+VK_MEDIA_PREV_TRACK :: 0xB1
+VK_MEDIA_STOP :: 0xB2
+VK_MEDIA_PLAY_PAUSE :: 0xB3
+VK_LAUNCH_MAIL :: 0xB4
+VK_LAUNCH_MEDIA_SELECT :: 0xB5
+VK_LAUNCH_APP1 :: 0xB6
+VK_LAUNCH_APP2 :: 0xB7
+
+// 0xB8 - 0xB9 : reserved
+
+VK_OEM_1 :: 0xBA // ';:' for US
+VK_OEM_PLUS :: 0xBB // '+' any country
+VK_OEM_COMMA :: 0xBC // ',' any country
+VK_OEM_MINUS :: 0xBD // '-' any country
+VK_OEM_PERIOD :: 0xBE // '.' any country
+VK_OEM_2 :: 0xBF // '/?' for US
+VK_OEM_3 :: 0xC0 // '`~' for US
+
+// 0xC1 - 0xDA : reserved
+
+VK_OEM_4 :: 0xDB // '[{' for US
+VK_OEM_5 :: 0xDC // '\|' for US
+VK_OEM_6 :: 0xDD // ']}' for US
+VK_OEM_7 :: 0xDE // ''"' for US
+VK_OEM_8 :: 0xDF
+
+// 0xE0 : reserved
+
+// Various extended or enhanced keyboards
+VK_OEM_AX :: 0xE1 // 'AX' key on Japanese AX kbd
+VK_OEM_102 :: 0xE2 // "<>" or "\|" on RT 102-key kbd.
+VK_ICO_HELP :: 0xE3 // Help key on ICO
+VK_ICO_00 :: 0xE4 // 00 key on ICO
+
+VK_PROCESSKEY :: 0xE5
+VK_ICO_CLEAR :: 0xE6
+VK_PACKET :: 0xE7
+
+// 0xE8 : unassigned
+
+// Nokia/Ericsson definitions
+VK_OEM_RESET :: 0xE9
+VK_OEM_JUMP :: 0xEA
+VK_OEM_PA1 :: 0xEB
+VK_OEM_PA2 :: 0xEC
+VK_OEM_PA3 :: 0xED
+VK_OEM_WSCTRL :: 0xEE
+VK_OEM_CUSEL :: 0xEF
+VK_OEM_ATTN :: 0xF0
+VK_OEM_FINISH :: 0xF1
+VK_OEM_COPY :: 0xF2
+VK_OEM_AUTO :: 0xF3
+VK_OEM_ENLW :: 0xF4
+VK_OEM_BACKTAB :: 0xF5
+
+VK_ATTN :: 0xF6
+VK_CRSEL :: 0xF7
+VK_EXSEL :: 0xF8
+VK_EREOF :: 0xF9
+VK_PLAY :: 0xFA
+VK_ZOOM :: 0xFB
+VK_NONAME :: 0xFC
+VK_PA1 :: 0xFD
+VK_OEM_CLEAR :: 0xFE
+
+// 0xFF : reserved
diff --git a/core/sys/windows/ntdll.odin b/core/sys/windows/ntdll.odin
index 5deffd9f9..dda5b9711 100644
--- a/core/sys/windows/ntdll.odin
+++ b/core/sys/windows/ntdll.odin
@@ -3,7 +3,7 @@ package sys_windows
foreign import ntdll_lib "system:ntdll.lib"
-@(default_calling_convention="std")
+@(default_calling_convention="stdcall")
foreign ntdll_lib {
- RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS ---
+ RtlGetVersion :: proc(lpVersionInformation: ^OSVERSIONINFOEXW) -> NTSTATUS ---
}
\ No newline at end of file
diff --git a/core/sys/windows/ole32.odin b/core/sys/windows/ole32.odin
new file mode 100644
index 000000000..4a4b470ea
--- /dev/null
+++ b/core/sys/windows/ole32.odin
@@ -0,0 +1,39 @@
+// +build windows
+package sys_windows
+
+foreign import "system:Ole32.lib"
+
+//objbase.h
+COINIT :: enum DWORD {
+ APARTMENTTHREADED = 0x2,
+ MULTITHREADED,
+ DISABLE_OLE1DDE = 0x4,
+ SPEED_OVER_MEMORY = 0x8,
+}
+
+IUnknown :: struct {
+ using Vtbl: ^IUnknownVtbl,
+}
+IUnknownVtbl :: struct {
+ QueryInterface: proc "stdcall" (This: ^IUnknown, riid: REFIID, ppvObject: ^rawptr) -> HRESULT,
+ AddRef: proc "stdcall" (This: ^IUnknown) -> ULONG,
+ Release: proc "stdcall" (This: ^IUnknown) -> ULONG,
+}
+
+LPUNKNOWN :: ^IUnknown
+
+@(default_calling_convention="stdcall")
+foreign Ole32 {
+ CoInitializeEx :: proc(reserved: rawptr, co_init: COINIT) -> HRESULT ---
+ CoUninitialize :: proc() ---
+
+ CoCreateInstance :: proc(
+ rclsid: REFCLSID,
+ pUnkOuter: LPUNKNOWN,
+ dwClsContext: DWORD,
+ riid: REFIID,
+ ppv: ^LPVOID,
+ ) -> HRESULT ---
+
+ CoTaskMemFree :: proc(pv: rawptr) ---
+}
diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin
index 70d8943bd..a6ecefc32 100644
--- a/core/sys/windows/shell32.odin
+++ b/core/sys/windows/shell32.odin
@@ -3,7 +3,7 @@ package sys_windows
foreign import shell32 "system:Shell32.lib"
-@(default_calling_convention = "std")
+@(default_calling_convention="stdcall")
foreign shell32 {
CommandLineToArgvW :: proc(cmd_list: wstring, num_args: ^c_int) -> ^wstring ---
}
diff --git a/core/sys/windows/shlwapi.odin b/core/sys/windows/shlwapi.odin
new file mode 100644
index 000000000..1852d536f
--- /dev/null
+++ b/core/sys/windows/shlwapi.odin
@@ -0,0 +1,11 @@
+// +build windows
+package sys_windows
+
+foreign import shlwapi "system:shlwapi.lib"
+
+@(default_calling_convention="stdcall")
+foreign shlwapi {
+ PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
+ PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
+ PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
+}
diff --git a/core/sys/windows/synchronization.odin b/core/sys/windows/synchronization.odin
index c4e1d2188..c98730aa0 100644
--- a/core/sys/windows/synchronization.odin
+++ b/core/sys/windows/synchronization.odin
@@ -5,7 +5,7 @@ foreign import Synchronization "system:Synchronization.lib"
@(default_calling_convention="stdcall")
foreign Synchronization {
- WaitOnAddress :: proc(Address: PVOID, CompareAddress: PVOID, AddressSize: SIZE_T, dwMilliseconds: DWORD) -> BOOL ---
+ WaitOnAddress :: proc(Address: PVOID, CompareAddress: PVOID, AddressSize: SIZE_T, dwMilliseconds: DWORD) -> BOOL ---
WakeByAddressSingle :: proc(Address: PVOID) ---
- WakeByAddressAll :: proc(Address: PVOID) ---
+ WakeByAddressAll :: proc(Address: PVOID) ---
}
diff --git a/core/sys/windows/system_params.odin b/core/sys/windows/system_params.odin
new file mode 100644
index 000000000..e94d777bf
--- /dev/null
+++ b/core/sys/windows/system_params.odin
@@ -0,0 +1,271 @@
+// +build windows
+package sys_windows
+
+// Parameter for SystemParametersInfo.
+SPI_GETBEEP :: 0x0001
+SPI_SETBEEP :: 0x0002
+SPI_GETMOUSE :: 0x0003
+SPI_SETMOUSE :: 0x0004
+SPI_GETBORDER :: 0x0005
+SPI_SETBORDER :: 0x0006
+SPI_GETKEYBOARDSPEED :: 0x000A
+SPI_SETKEYBOARDSPEED :: 0x000B
+SPI_LANGDRIVER :: 0x000C
+SPI_ICONHORIZONTALSPACING :: 0x000D
+SPI_GETSCREENSAVETIMEOUT :: 0x000E
+SPI_SETSCREENSAVETIMEOUT :: 0x000F
+SPI_GETSCREENSAVEACTIVE :: 0x0010
+SPI_SETSCREENSAVEACTIVE :: 0x0011
+SPI_GETGRIDGRANULARITY :: 0x0012
+SPI_SETGRIDGRANULARITY :: 0x0013
+SPI_SETDESKWALLPAPER :: 0x0014
+SPI_SETDESKPATTERN :: 0x0015
+SPI_GETKEYBOARDDELAY :: 0x0016
+SPI_SETKEYBOARDDELAY :: 0x0017
+SPI_ICONVERTICALSPACING :: 0x0018
+SPI_GETICONTITLEWRAP :: 0x0019
+SPI_SETICONTITLEWRAP :: 0x001A
+SPI_GETMENUDROPALIGNMENT :: 0x001B
+SPI_SETMENUDROPALIGNMENT :: 0x001C
+SPI_SETDOUBLECLKWIDTH :: 0x001D
+SPI_SETDOUBLECLKHEIGHT :: 0x001E
+SPI_GETICONTITLELOGFONT :: 0x001F
+SPI_SETDOUBLECLICKTIME :: 0x0020
+SPI_SETMOUSEBUTTONSWAP :: 0x0021
+SPI_SETICONTITLELOGFONT :: 0x0022
+SPI_GETFASTTASKSWITCH :: 0x0023
+SPI_SETFASTTASKSWITCH :: 0x0024
+
+SPI_SETDRAGFULLWINDOWS :: 0x0025
+SPI_GETDRAGFULLWINDOWS :: 0x0026
+SPI_GETNONCLIENTMETRICS :: 0x0029
+SPI_SETNONCLIENTMETRICS :: 0x002A
+SPI_GETMINIMIZEDMETRICS :: 0x002B
+SPI_SETMINIMIZEDMETRICS :: 0x002C
+SPI_GETICONMETRICS :: 0x002D
+SPI_SETICONMETRICS :: 0x002E
+SPI_SETWORKAREA :: 0x002F
+SPI_GETWORKAREA :: 0x0030
+SPI_SETPENWINDOWS :: 0x0031
+SPI_GETHIGHCONTRAST :: 0x0042
+SPI_SETHIGHCONTRAST :: 0x0043
+SPI_GETKEYBOARDPREF :: 0x0044
+SPI_SETKEYBOARDPREF :: 0x0045
+SPI_GETSCREENREADER :: 0x0046
+SPI_SETSCREENREADER :: 0x0047
+SPI_GETANIMATION :: 0x0048
+SPI_SETANIMATION :: 0x0049
+SPI_GETFONTSMOOTHING :: 0x004A
+SPI_SETFONTSMOOTHING :: 0x004B
+SPI_SETDRAGWIDTH :: 0x004C
+SPI_SETDRAGHEIGHT :: 0x004D
+SPI_SETHANDHELD :: 0x004E
+SPI_GETLOWPOWERTIMEOUT :: 0x004F
+SPI_GETPOWEROFFTIMEOUT :: 0x0050
+SPI_SETLOWPOWERTIMEOUT :: 0x0051
+SPI_SETPOWEROFFTIMEOUT :: 0x0052
+SPI_GETLOWPOWERACTIVE :: 0x0053
+SPI_GETPOWEROFFACTIVE :: 0x0054
+SPI_SETLOWPOWERACTIVE :: 0x0055
+SPI_SETPOWEROFFACTIVE :: 0x0056
+SPI_SETCURSORS :: 0x0057
+SPI_SETICONS :: 0x0058
+SPI_GETDEFAULTINPUTLANG :: 0x0059
+SPI_SETDEFAULTINPUTLANG :: 0x005A
+SPI_SETLANGTOGGLE :: 0x005B
+SPI_GETWINDOWSEXTENSION :: 0x005C
+SPI_SETMOUSETRAILS :: 0x005D
+SPI_GETMOUSETRAILS :: 0x005E
+SPI_SETSCREENSAVERRUNNING :: 0x0061
+
+SPI_SCREENSAVERRUNNING :: SPI_SETSCREENSAVERRUNNING
+SPI_GETFILTERKEYS :: 0x0032
+SPI_SETFILTERKEYS :: 0x0033
+SPI_GETTOGGLEKEYS :: 0x0034
+SPI_SETTOGGLEKEYS :: 0x0035
+SPI_GETMOUSEKEYS :: 0x0036
+SPI_SETMOUSEKEYS :: 0x0037
+SPI_GETSHOWSOUNDS :: 0x0038
+SPI_SETSHOWSOUNDS :: 0x0039
+SPI_GETSTICKYKEYS :: 0x003A
+SPI_SETSTICKYKEYS :: 0x003B
+SPI_GETACCESSTIMEOUT :: 0x003C
+SPI_SETACCESSTIMEOUT :: 0x003D
+SPI_GETSERIALKEYS :: 0x003E
+SPI_SETSERIALKEYS :: 0x003F
+SPI_GETSOUNDSENTRY :: 0x0040
+SPI_SETSOUNDSENTRY :: 0x0041
+SPI_GETSNAPTODEFBUTTON :: 0x005F
+SPI_SETSNAPTODEFBUTTON :: 0x0060
+SPI_GETMOUSEHOVERWIDTH :: 0x0062
+SPI_SETMOUSEHOVERWIDTH :: 0x0063
+SPI_GETMOUSEHOVERHEIGHT :: 0x0064
+SPI_SETMOUSEHOVERHEIGHT :: 0x0065
+SPI_GETMOUSEHOVERTIME :: 0x0066
+SPI_SETMOUSEHOVERTIME :: 0x0067
+SPI_GETWHEELSCROLLLINES :: 0x0068
+SPI_SETWHEELSCROLLLINES :: 0x0069
+SPI_GETMENUSHOWDELAY :: 0x006A
+SPI_SETMENUSHOWDELAY :: 0x006B
+
+SPI_GETWHEELSCROLLCHARS :: 0x006C
+SPI_SETWHEELSCROLLCHARS :: 0x006D
+SPI_GETSHOWIMEUI :: 0x006E
+SPI_SETSHOWIMEUI :: 0x006F
+SPI_GETMOUSESPEED :: 0x0070
+SPI_SETMOUSESPEED :: 0x0071
+SPI_GETSCREENSAVERRUNNING :: 0x0072
+SPI_GETDESKWALLPAPER :: 0x0073
+SPI_GETAUDIODESCRIPTION :: 0x0074
+SPI_SETAUDIODESCRIPTION :: 0x0075
+SPI_GETSCREENSAVESECURE :: 0x0076
+SPI_SETSCREENSAVESECURE :: 0x0077
+
+SPI_GETHUNGAPPTIMEOUT :: 0x0078
+SPI_SETHUNGAPPTIMEOUT :: 0x0079
+SPI_GETWAITTOKILLTIMEOUT :: 0x007A
+SPI_SETWAITTOKILLTIMEOUT :: 0x007B
+SPI_GETWAITTOKILLSERVICETIMEOUT :: 0x007C
+SPI_SETWAITTOKILLSERVICETIMEOUT :: 0x007D
+SPI_GETMOUSEDOCKTHRESHOLD :: 0x007E
+SPI_SETMOUSEDOCKTHRESHOLD :: 0x007F
+SPI_GETPENDOCKTHRESHOLD :: 0x0080
+SPI_SETPENDOCKTHRESHOLD :: 0x0081
+SPI_GETWINARRANGING :: 0x0082
+SPI_SETWINARRANGING :: 0x0083
+SPI_GETMOUSEDRAGOUTTHRESHOLD :: 0x0084
+SPI_SETMOUSEDRAGOUTTHRESHOLD :: 0x0085
+SPI_GETPENDRAGOUTTHRESHOLD :: 0x0086
+SPI_SETPENDRAGOUTTHRESHOLD :: 0x0087
+SPI_GETMOUSESIDEMOVETHRESHOLD :: 0x0088
+SPI_SETMOUSESIDEMOVETHRESHOLD :: 0x0089
+SPI_GETPENSIDEMOVETHRESHOLD :: 0x008A
+SPI_SETPENSIDEMOVETHRESHOLD :: 0x008B
+SPI_GETDRAGFROMMAXIMIZE :: 0x008C
+SPI_SETDRAGFROMMAXIMIZE :: 0x008D
+SPI_GETSNAPSIZING :: 0x008E
+SPI_SETSNAPSIZING :: 0x008F
+SPI_GETDOCKMOVING :: 0x0090
+SPI_SETDOCKMOVING :: 0x0091
+
+SPI_GETACTIVEWINDOWTRACKING :: 0x1000
+SPI_SETACTIVEWINDOWTRACKING :: 0x1001
+SPI_GETMENUANIMATION :: 0x1002
+SPI_SETMENUANIMATION :: 0x1003
+SPI_GETCOMBOBOXANIMATION :: 0x1004
+SPI_SETCOMBOBOXANIMATION :: 0x1005
+SPI_GETLISTBOXSMOOTHSCROLLING :: 0x1006
+SPI_SETLISTBOXSMOOTHSCROLLING :: 0x1007
+SPI_GETGRADIENTCAPTIONS :: 0x1008
+SPI_SETGRADIENTCAPTIONS :: 0x1009
+SPI_GETKEYBOARDCUES :: 0x100A
+SPI_SETKEYBOARDCUES :: 0x100B
+SPI_GETMENUUNDERLINES :: SPI_GETKEYBOARDCUES
+SPI_SETMENUUNDERLINES :: SPI_SETKEYBOARDCUES
+SPI_GETACTIVEWNDTRKZORDER :: 0x100C
+SPI_SETACTIVEWNDTRKZORDER :: 0x100D
+SPI_GETHOTTRACKING :: 0x100E
+SPI_SETHOTTRACKING :: 0x100F
+SPI_GETMENUFADE :: 0x1012
+SPI_SETMENUFADE :: 0x1013
+SPI_GETSELECTIONFADE :: 0x1014
+SPI_SETSELECTIONFADE :: 0x1015
+SPI_GETTOOLTIPANIMATION :: 0x1016
+SPI_SETTOOLTIPANIMATION :: 0x1017
+SPI_GETTOOLTIPFADE :: 0x1018
+SPI_SETTOOLTIPFADE :: 0x1019
+SPI_GETCURSORSHADOW :: 0x101A
+SPI_SETCURSORSHADOW :: 0x101B
+SPI_GETMOUSESONAR :: 0x101C
+SPI_SETMOUSESONAR :: 0x101D
+SPI_GETMOUSECLICKLOCK :: 0x101E
+SPI_SETMOUSECLICKLOCK :: 0x101F
+SPI_GETMOUSEVANISH :: 0x1020
+SPI_SETMOUSEVANISH :: 0x1021
+SPI_GETFLATMENU :: 0x1022
+SPI_SETFLATMENU :: 0x1023
+SPI_GETDROPSHADOW :: 0x1024
+SPI_SETDROPSHADOW :: 0x1025
+SPI_GETBLOCKSENDINPUTRESETS :: 0x1026
+SPI_SETBLOCKSENDINPUTRESETS :: 0x1027
+SPI_GETUIEFFECTS :: 0x103E
+SPI_SETUIEFFECTS :: 0x103F
+SPI_GETDISABLEOVERLAPPEDCONTENT :: 0x1040
+SPI_SETDISABLEOVERLAPPEDCONTENT :: 0x1041
+SPI_GETCLIENTAREAANIMATION :: 0x1042
+SPI_SETCLIENTAREAANIMATION :: 0x1043
+SPI_GETCLEARTYPE :: 0x1048
+SPI_SETCLEARTYPE :: 0x1049
+SPI_GETSPEECHRECOGNITION :: 0x104A
+SPI_SETSPEECHRECOGNITION :: 0x104B
+SPI_GETCARETBROWSING :: 0x104C
+SPI_SETCARETBROWSING :: 0x104D
+SPI_GETTHREADLOCALINPUTSETTINGS :: 0x104E
+SPI_SETTHREADLOCALINPUTSETTINGS :: 0x104F
+SPI_GETSYSTEMLANGUAGEBAR :: 0x1050
+SPI_SETSYSTEMLANGUAGEBAR :: 0x1051
+SPI_GETFOREGROUNDLOCKTIMEOUT :: 0x2000
+SPI_SETFOREGROUNDLOCKTIMEOUT :: 0x2001
+SPI_GETACTIVEWNDTRKTIMEOUT :: 0x2002
+SPI_SETACTIVEWNDTRKTIMEOUT :: 0x2003
+SPI_GETFOREGROUNDFLASHCOUNT :: 0x2004
+SPI_SETFOREGROUNDFLASHCOUNT :: 0x2005
+SPI_GETCARETWIDTH :: 0x2006
+SPI_SETCARETWIDTH :: 0x2007
+SPI_GETMOUSECLICKLOCKTIME :: 0x2008
+SPI_SETMOUSECLICKLOCKTIME :: 0x2009
+SPI_GETFONTSMOOTHINGTYPE :: 0x200A
+SPI_SETFONTSMOOTHINGTYPE :: 0x200B
+// constants for SPI_GETFONTSMOOTHINGTYPE and SPI_SETFONTSMOOTHINGTYPE:
+FE_FONTSMOOTHINGSTANDARD :: 0x0001
+FE_FONTSMOOTHINGCLEARTYPE :: 0x0002
+
+SPI_GETFONTSMOOTHINGCONTRAST :: 0x200C
+SPI_SETFONTSMOOTHINGCONTRAST :: 0x200D
+
+SPI_GETFOCUSBORDERWIDTH :: 0x200E
+SPI_SETFOCUSBORDERWIDTH :: 0x200F
+SPI_GETFOCUSBORDERHEIGHT :: 0x2010
+SPI_SETFOCUSBORDERHEIGHT :: 0x2011
+
+SPI_GETFONTSMOOTHINGORIENTATION :: 0x2012
+SPI_SETFONTSMOOTHINGORIENTATION :: 0x2013
+
+// constants for SPI_GETFONTSMOOTHINGORIENTATION and SPI_SETFONTSMOOTHINGORIENTATION:
+FE_FONTSMOOTHINGORIENTATIONBGR :: 0x0000
+FE_FONTSMOOTHINGORIENTATIONRGB :: 0x0001
+
+SPI_GETMINIMUMHITRADIUS :: 0x2014
+SPI_SETMINIMUMHITRADIUS :: 0x2015
+SPI_GETMESSAGEDURATION :: 0x2016
+SPI_SETMESSAGEDURATION :: 0x2017
+
+SPI_GETCONTACTVISUALIZATION :: 0x2018
+SPI_SETCONTACTVISUALIZATION :: 0x2019
+// constants for SPI_GETCONTACTVISUALIZATION and SPI_SETCONTACTVISUALIZATION
+CONTACTVISUALIZATION_OFF :: 0x0000
+CONTACTVISUALIZATION_ON :: 0x0001
+CONTACTVISUALIZATION_PRESENTATIONMODE :: 0x0002
+
+SPI_GETGESTUREVISUALIZATION :: 0x201A
+SPI_SETGESTUREVISUALIZATION :: 0x201B
+// constants for SPI_GETGESTUREVISUALIZATION and SPI_SETGESTUREVISUALIZATION
+GESTUREVISUALIZATION_OFF :: 0x0000
+GESTUREVISUALIZATION_ON :: 0x001F
+GESTUREVISUALIZATION_TAP :: 0x0001
+GESTUREVISUALIZATION_DOUBLETAP :: 0x0002
+GESTUREVISUALIZATION_PRESSANDTAP :: 0x0004
+GESTUREVISUALIZATION_PRESSANDHOLD :: 0x0008
+GESTUREVISUALIZATION_RIGHTTAP :: 0x0010
+
+SPI_GETMOUSEWHEELROUTING :: 0x201C
+SPI_SETMOUSEWHEELROUTING :: 0x201D
+
+MOUSEWHEEL_ROUTING_FOCUS :: 0
+MOUSEWHEEL_ROUTING_HYBRID :: 1
+MOUSEWHEEL_ROUTING_MOUSE_POS :: 2
+
+// Flags
+SPIF_UPDATEINIFILE :: 0x0001
+SPIF_SENDWININICHANGE :: 0x0002
+SPIF_SENDCHANGE :: SPIF_SENDWININICHANGE
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 3e25a4c18..edf6e593e 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -3,30 +3,45 @@ package sys_windows
import "core:c"
-c_char :: c.char
-c_uchar :: c.uchar
-c_int :: c.int
-c_uint :: c.uint
-c_long :: c.long
-c_longlong :: c.longlong
-c_ulong :: c.ulong
-c_short :: c.short
-c_ushort :: c.ushort
-size_t :: c.size_t
-wchar_t :: c.wchar_t
+c_char :: c.char
+c_uchar :: c.uchar
+c_int :: c.int
+c_uint :: c.uint
+c_long :: c.long
+c_longlong :: c.longlong
+c_ulong :: c.ulong
+c_ulonglong :: c.ulonglong
+c_short :: c.short
+c_ushort :: c.ushort
+size_t :: c.size_t
+wchar_t :: c.wchar_t
DWORD :: c_ulong
+QWORD :: c.ulonglong
HANDLE :: distinct LPVOID
HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
HWND :: distinct HANDLE
+HDC :: distinct HANDLE
HMONITOR :: distinct HANDLE
+HICON :: distinct HANDLE
+HCURSOR :: distinct HANDLE
+HMENU :: distinct HANDLE
+HBRUSH :: distinct HANDLE
+HGDIOBJ :: distinct HANDLE
+HBITMAP :: distinct HANDLE
+HGLOBAL :: distinct HANDLE
+HHOOK :: distinct HANDLE
+HKEY :: distinct HANDLE
+HDESK :: distinct HANDLE
+HFONT :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
GROUP :: distinct c_uint
LARGE_INTEGER :: distinct c_longlong
+ULARGE_INTEGER :: distinct c_ulonglong
LONG :: c_long
UINT :: c_uint
INT :: c_int
@@ -42,9 +57,20 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
+UINT_PTR :: uintptr
ULONG :: c_ulong
+ULONGLONG :: c_ulonglong
UCHAR :: BYTE
NTSTATUS :: c.long
+COLORREF :: DWORD
+LPCOLORREF :: ^COLORREF
+LPARAM :: LONG_PTR
+WPARAM :: UINT_PTR
+LRESULT :: LONG_PTR
+LPRECT :: ^RECT
+LPPOINT :: ^POINT
+LSTATUS :: LONG
+PHKEY :: ^HKEY
UINT8 :: u8
UINT16 :: u16
@@ -71,6 +97,7 @@ PBOOL :: ^BOOL
LPBOOL :: ^BOOL
LPCSTR :: cstring
LPCWSTR :: wstring
+LPCTSTR :: wstring
LPDWORD :: ^DWORD
PCSTR :: cstring
PCWSTR :: wstring
@@ -81,7 +108,9 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSTARTUPINFO :: ^STARTUPINFO
-PVOID :: rawptr
+LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
+VOID :: rawptr
+PVOID :: rawptr
LPVOID :: rawptr
PINT :: ^INT
LPINT :: ^INT
@@ -95,6 +124,8 @@ LPWSADATA :: ^WSADATA
LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO
LPSTR :: ^CHAR
LPWSTR :: ^WCHAR
+OLECHAR :: WCHAR
+LPOLESTR :: ^OLECHAR
LPFILETIME :: ^FILETIME
LPWSABUF :: ^WSABUF
LPWSAOVERLAPPED :: distinct rawptr
@@ -105,6 +136,8 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
+MMRESULT :: UINT
+
SOCKET :: distinct uintptr // TODO
socklen_t :: c_int
ADDRESS_FAMILY :: USHORT
@@ -142,23 +175,47 @@ FILE_GENERIC_ALL: DWORD : 0x10000000
FILE_GENERIC_EXECUTE: DWORD : 0x20000000
FILE_GENERIC_READ: DWORD : 0x80000000
+FILE_ACTION_ADDED :: 0x00000001
+FILE_ACTION_REMOVED :: 0x00000002
+FILE_ACTION_MODIFIED :: 0x00000003
+FILE_ACTION_RENAMED_OLD_NAME :: 0x00000004
+FILE_ACTION_RENAMED_NEW_NAME :: 0x00000005
+
+FILE_NOTIFY_CHANGE_FILE_NAME :: 0x00000001
+FILE_NOTIFY_CHANGE_DIR_NAME :: 0x00000002
+FILE_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000004
+FILE_NOTIFY_CHANGE_SIZE :: 0x00000008
+FILE_NOTIFY_CHANGE_LAST_WRITE :: 0x00000010
+FILE_NOTIFY_CHANGE_LAST_ACCESS :: 0x00000020
+FILE_NOTIFY_CHANGE_CREATION :: 0x00000040
+FILE_NOTIFY_CHANGE_SECURITY :: 0x00000100
+
CREATE_NEW: DWORD : 1
CREATE_ALWAYS: DWORD : 2
OPEN_ALWAYS: DWORD : 4
OPEN_EXISTING: DWORD : 3
TRUNCATE_EXISTING: DWORD : 5
+FILE_READ_DATA : DWORD : 0x00000001
+FILE_LIST_DIRECTORY : DWORD : 0x00000001
+FILE_WRITE_DATA : DWORD : 0x00000002
+FILE_ADD_FILE : DWORD : 0x00000002
+FILE_APPEND_DATA : DWORD : 0x00000004
+FILE_ADD_SUBDIRECTORY : DWORD : 0x00000004
+FILE_CREATE_PIPE_INSTANCE : DWORD : 0x00000004
+FILE_READ_EA : DWORD : 0x00000008
+FILE_WRITE_EA : DWORD : 0x00000010
+FILE_EXECUTE : DWORD : 0x00000020
+FILE_TRAVERSE : DWORD : 0x00000020
+FILE_DELETE_CHILD : DWORD : 0x00000040
+FILE_READ_ATTRIBUTES : DWORD : 0x00000080
+FILE_WRITE_ATTRIBUTES : DWORD : 0x00000100
+GENERIC_READ : DWORD : 0x80000000
+GENERIC_WRITE : DWORD : 0x40000000
+GENERIC_EXECUTE : DWORD : 0x20000000
+GENERIC_ALL : DWORD : 0x10000000
-FILE_WRITE_DATA: DWORD : 0x00000002
-FILE_APPEND_DATA: DWORD : 0x00000004
-FILE_WRITE_EA: DWORD : 0x00000010
-FILE_WRITE_ATTRIBUTES: DWORD : 0x00000100
-READ_CONTROL: DWORD : 0x00020000
-SYNCHRONIZE: DWORD : 0x00100000
-GENERIC_READ: DWORD : 0x80000000
-GENERIC_WRITE: DWORD : 0x40000000
-STANDARD_RIGHTS_WRITE: DWORD : READ_CONTROL
FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
@@ -177,6 +234,555 @@ 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
+
+DIAGNOSTIC_REASON_SIMPLE_STRING :: 0x00000001
+DIAGNOSTIC_REASON_DETAILED_STRING :: 0x00000002
+DIAGNOSTIC_REASON_NOT_SPECIFIED :: 0x80000000
+
+ENUM_CURRENT_SETTINGS : DWORD : 4294967295 // (DWORD)-1
+ENUM_REGISTRY_SETTINGS : DWORD : 4294967294 // (DWORD)-2
+
+// Defines for power request APIs
+
+POWER_REQUEST_CONTEXT_VERSION :: DIAGNOSTIC_REASON_VERSION
+
+POWER_REQUEST_CONTEXT_SIMPLE_STRING :: DIAGNOSTIC_REASON_SIMPLE_STRING
+POWER_REQUEST_CONTEXT_DETAILED_STRING :: DIAGNOSTIC_REASON_DETAILED_STRING
+
+REASON_CONTEXT :: struct {
+ Version: ULONG,
+ Flags: DWORD,
+ Reason: struct #raw_union {
+ Detailed: struct {
+ LocalizedReasonModule: HMODULE,
+ LocalizedReasonId: ULONG,
+ ReasonStringCount: ULONG,
+ ReasonStrings: ^LPWSTR,
+ },
+ SimpleReasonString: LPWSTR,
+ },
+}
+PREASON_CONTEXT :: ^REASON_CONTEXT
+
+// RRF - Registry Routine Flags (for RegGetValue)
+RRF_RT_REG_NONE :: 0x00000001
+RRF_RT_REG_SZ :: 0x00000002
+RRF_RT_REG_EXPAND_SZ :: 0x00000004
+RRF_RT_REG_BINARY :: 0x00000008
+RRF_RT_REG_DWORD :: 0x00000010
+RRF_RT_REG_MULTI_SZ :: 0x00000020
+RRF_RT_REG_QWORD :: 0x00000040
+RRF_RT_DWORD :: (RRF_RT_REG_BINARY | RRF_RT_REG_DWORD)
+RRF_RT_QWORD :: (RRF_RT_REG_BINARY | RRF_RT_REG_QWORD)
+RRF_RT_ANY :: 0x0000ffff
+RRF_NOEXPAND :: 0x10000000
+RRF_ZEROONFAILURE :: 0x20000000
+
+ACCESS_MASK :: DWORD
+PACCESS_MASK :: ^ACCESS_MASK
+REGSAM :: ACCESS_MASK
+
+// Reserved Key Handles.
+HKEY_CLASSES_ROOT :: HKEY(uintptr(0x80000000))
+HKEY_CURRENT_USER :: HKEY(uintptr(0x80000001))
+HKEY_LOCAL_MACHINE :: HKEY(uintptr(0x80000002))
+HKEY_USERS :: HKEY(uintptr(0x80000003))
+HKEY_PERFORMANCE_DATA :: HKEY(uintptr(0x80000004))
+HKEY_PERFORMANCE_TEXT :: HKEY(uintptr(0x80000050))
+HKEY_PERFORMANCE_NLSTEXT :: HKEY(uintptr(0x80000060))
+HKEY_CURRENT_CONFIG :: HKEY(uintptr(0x80000005))
+HKEY_DYN_DATA :: HKEY(uintptr(0x80000006))
+HKEY_CURRENT_USER_LOCAL_SETTINGS :: HKEY(uintptr(0x80000007))
+
+// The following are masks for the predefined standard access types
+DELETE : DWORD : 0x00010000
+READ_CONTROL : DWORD : 0x00020000
+WRITE_DAC : DWORD : 0x00040000
+WRITE_OWNER : DWORD : 0x00080000
+SYNCHRONIZE : DWORD : 0x00100000
+
+STANDARD_RIGHTS_REQUIRED : DWORD : 0x000F0000
+STANDARD_RIGHTS_READ : DWORD : READ_CONTROL
+STANDARD_RIGHTS_WRITE : DWORD : READ_CONTROL
+STANDARD_RIGHTS_EXECUTE : DWORD : READ_CONTROL
+STANDARD_RIGHTS_ALL : DWORD : 0x001F0000
+SPECIFIC_RIGHTS_ALL : DWORD : 0x0000FFFF
+
+// Registry Specific Access Rights.
+KEY_QUERY_VALUE :: 0x0001
+KEY_SET_VALUE :: 0x0002
+KEY_CREATE_SUB_KEY :: 0x0004
+KEY_ENUMERATE_SUB_KEYS :: 0x0008
+KEY_NOTIFY :: 0x0010
+KEY_CREATE_LINK :: 0x0020
+KEY_WOW64_32KEY :: 0x0200
+KEY_WOW64_64KEY :: 0x0100
+KEY_WOW64_RES :: 0x0300
+
+KEY_READ :: (STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE)
+KEY_WRITE :: (STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE)
+KEY_EXECUTE :: (KEY_READ) & (~SYNCHRONIZE)
+KEY_ALL_ACCESS :: (STANDARD_RIGHTS_ALL |
+ KEY_QUERY_VALUE |
+ KEY_SET_VALUE |
+ KEY_CREATE_SUB_KEY |
+ KEY_ENUMERATE_SUB_KEYS |
+ KEY_NOTIFY |
+ KEY_CREATE_LINK) & (~SYNCHRONIZE)
+
+// Open/Create Options
+REG_OPTION_RESERVED :: 0x00000000
+REG_OPTION_NON_VOLATILE :: 0x00000000
+REG_OPTION_VOLATILE :: 0x00000001
+REG_OPTION_CREATE_LINK :: 0x00000002
+REG_OPTION_BACKUP_RESTORE :: 0x00000004
+REG_OPTION_OPEN_LINK :: 0x00000008
+REG_OPTION_DONT_VIRTUALIZE :: 0x00000010
+
+REG_LEGAL_OPTION :: REG_OPTION_RESERVED |
+ REG_OPTION_NON_VOLATILE |
+ REG_OPTION_VOLATILE |
+ REG_OPTION_CREATE_LINK |
+ REG_OPTION_BACKUP_RESTORE |
+ REG_OPTION_OPEN_LINK |
+ REG_OPTION_DONT_VIRTUALIZE
+
+REG_OPEN_LEGAL_OPTION :: REG_OPTION_RESERVED |
+ REG_OPTION_BACKUP_RESTORE |
+ REG_OPTION_OPEN_LINK |
+ REG_OPTION_DONT_VIRTUALIZE
+
+// Key creation/open disposition
+REG_CREATED_NEW_KEY :: 0x00000001
+REG_OPENED_EXISTING_KEY :: 0x00000002
+
+// hive format to be used by Reg(Nt)SaveKeyEx
+REG_STANDARD_FORMAT :: 1
+REG_LATEST_FORMAT :: 2
+REG_NO_COMPRESSION :: 4
+
+// Key restore & hive load flags
+REG_WHOLE_HIVE_VOLATILE :: 0x00000001
+REG_REFRESH_HIVE :: 0x00000002
+REG_NO_LAZY_FLUSH :: 0x00000004
+REG_FORCE_RESTORE :: 0x00000008
+REG_APP_HIVE :: 0x00000010
+REG_PROCESS_PRIVATE :: 0x00000020
+REG_START_JOURNAL :: 0x00000040
+REG_HIVE_EXACT_FILE_GROWTH :: 0x00000080
+REG_HIVE_NO_RM :: 0x00000100
+REG_HIVE_SINGLE_LOG :: 0x00000200
+REG_BOOT_HIVE :: 0x00000400
+REG_LOAD_HIVE_OPEN_HANDLE :: 0x00000800
+REG_FLUSH_HIVE_FILE_GROWTH :: 0x00001000
+REG_OPEN_READ_ONLY :: 0x00002000
+REG_IMMUTABLE :: 0x00004000
+REG_NO_IMPERSONATION_FALLBACK :: 0x00008000
+REG_APP_HIVE_OPEN_READ_ONLY :: REG_OPEN_READ_ONLY
+
+// Unload Flags
+REG_FORCE_UNLOAD :: 1
+REG_UNLOAD_LEGAL_FLAGS :: REG_FORCE_UNLOAD
+
+// Notify filter values
+REG_NOTIFY_CHANGE_NAME :: 0x00000001
+REG_NOTIFY_CHANGE_ATTRIBUTES :: 0x00000002
+REG_NOTIFY_CHANGE_LAST_SET :: 0x00000004
+REG_NOTIFY_CHANGE_SECURITY :: 0x00000008
+REG_NOTIFY_THREAD_AGNOSTIC :: 0x10000000
+
+REG_LEGAL_CHANGE_FILTER :: REG_NOTIFY_CHANGE_NAME |
+ REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_SECURITY |
+ REG_NOTIFY_THREAD_AGNOSTIC
+
+// Predefined Value Types.
+REG_NONE :: 0
+REG_SZ :: 1
+REG_EXPAND_SZ :: 2
+REG_BINARY :: 3
+REG_DWORD :: 4
+REG_DWORD_LITTLE_ENDIAN :: 4
+REG_DWORD_BIG_ENDIAN :: 5
+REG_LINK :: 6
+REG_MULTI_SZ :: 7
+REG_RESOURCE_LIST :: 8
+REG_FULL_RESOURCE_DESCRIPTOR :: 9
+REG_RESOURCE_REQUIREMENTS_LIST :: 10
+REG_QWORD :: 11
+REG_QWORD_LITTLE_ENDIAN :: 11
+
+BSMINFO :: struct {
+ cbSize: UINT,
+ hdesk: HDESK,
+ hwnd: HWND,
+ luid: LUID,
+}
+PBSMINFO :: ^BSMINFO
+
+// Broadcast Special Message Recipient list
+BSM_ALLCOMPONENTS :: 0x00000000
+BSM_VXDS :: 0x00000001
+BSM_NETDRIVER :: 0x00000002
+BSM_INSTALLABLEDRIVERS :: 0x00000004
+BSM_APPLICATIONS :: 0x00000008
+BSM_ALLDESKTOPS :: 0x00000010
+
+// Broadcast Special Message Flags
+BSF_QUERY :: 0x00000001
+BSF_IGNORECURRENTTASK :: 0x00000002
+BSF_FLUSHDISK :: 0x00000004
+BSF_NOHANG :: 0x00000008
+BSF_POSTMESSAGE :: 0x00000010
+BSF_FORCEIFHUNG :: 0x00000020
+BSF_NOTIMEOUTIFNOTHUNG :: 0x00000040
+BSF_ALLOWSFW :: 0x00000080
+BSF_SENDNOTIFYMESSAGE :: 0x00000100
+BSF_RETURNHDESK :: 0x00000200
+BSF_LUID :: 0x00000400
+
+BROADCAST_QUERY_DENY :: 0x424D5144
+
+// Special HWND value for use with PostMessage() and SendMessage()
+HWND_BROADCAST :: HWND(uintptr(0xffff))
+HWND_MESSAGE :: HWND(~uintptr(0) - 2) // -3
+
+// Color Types
+CTLCOLOR_MSGBOX :: 0
+CTLCOLOR_EDIT :: 1
+CTLCOLOR_LISTBOX :: 2
+CTLCOLOR_BTN :: 3
+CTLCOLOR_DLG :: 4
+CTLCOLOR_SCROLLBAR :: 5
+CTLCOLOR_STATIC :: 6
+CTLCOLOR_MAX :: 7
+
+COLOR_SCROLLBAR :: 0
+COLOR_BACKGROUND :: 1
+COLOR_ACTIVECAPTION :: 2
+COLOR_INACTIVECAPTION :: 3
+COLOR_MENU :: 4
+COLOR_WINDOW :: 5
+COLOR_WINDOWFRAME :: 6
+COLOR_MENUTEXT :: 7
+COLOR_WINDOWTEXT :: 8
+COLOR_CAPTIONTEXT :: 9
+COLOR_ACTIVEBORDER :: 10
+COLOR_INACTIVEBORDER :: 11
+COLOR_APPWORKSPACE :: 12
+COLOR_HIGHLIGHT :: 13
+COLOR_HIGHLIGHTTEXT :: 14
+COLOR_BTNFACE :: 15
+COLOR_BTNSHADOW :: 16
+COLOR_GRAYTEXT :: 17
+COLOR_BTNTEXT :: 18
+COLOR_INACTIVECAPTIONTEXT :: 19
+COLOR_BTNHIGHLIGHT :: 20
+
+COLOR_3DDKSHADOW :: 21
+COLOR_3DLIGHT :: 22
+COLOR_INFOTEXT :: 23
+COLOR_INFOBK :: 24
+COLOR_HOTLIGHT :: 26
+COLOR_GRADIENTACTIVECAPTION :: 27
+COLOR_GRADIENTINACTIVECAPTION :: 28
+COLOR_MENUHILIGHT :: 29
+COLOR_MENUBAR :: 30
+
+COLOR_DESKTOP :: COLOR_BACKGROUND
+COLOR_3DFACE :: COLOR_BTNFACE
+COLOR_3DSHADOW :: COLOR_BTNSHADOW
+COLOR_3DHIGHLIGHT :: COLOR_BTNHIGHLIGHT
+COLOR_3DHILIGHT :: COLOR_BTNHIGHLIGHT
+COLOR_BTNHILIGHT :: COLOR_BTNHIGHLIGHT
+
+// Combo Box Notification Codes
+CBN_ERRSPACE :: -1
+CBN_SELCHANGE :: 1
+CBN_DBLCLK :: 2
+CBN_SETFOCUS :: 3
+CBN_KILLFOCUS :: 4
+CBN_EDITCHANGE :: 5
+CBN_EDITUPDATE :: 6
+CBN_DROPDOWN :: 7
+CBN_CLOSEUP :: 8
+CBN_SELENDOK :: 9
+CBN_SELENDCANCEL :: 10
+
+// Combo Box styles
+CBS_SIMPLE :: 0x0001
+CBS_DROPDOWN :: 0x0002
+CBS_DROPDOWNLIST :: 0x0003
+CBS_OWNERDRAWFIXED :: 0x0010
+CBS_OWNERDRAWVARIABLE :: 0x0020
+CBS_AUTOHSCROLL :: 0x0040
+CBS_OEMCONVERT :: 0x0080
+CBS_SORT :: 0x0100
+CBS_HASSTRINGS :: 0x0200
+CBS_NOINTEGRALHEIGHT :: 0x0400
+CBS_DISABLENOSCROLL :: 0x0800
+CBS_UPPERCASE :: 0x2000
+CBS_LOWERCASE :: 0x4000
+
+// User Button Notification Codes
+BN_CLICKED :: 0
+BN_PAINT :: 1
+BN_HILITE :: 2
+BN_UNHILITE :: 3
+BN_DISABLE :: 4
+BN_DOUBLECLICKED :: 5
+BN_PUSHED :: BN_HILITE
+BN_UNPUSHED :: BN_UNHILITE
+BN_DBLCLK :: BN_DOUBLECLICKED
+BN_SETFOCUS :: 6
+BN_KILLFOCUS :: 7
+
+// Button Control Styles
+BS_PUSHBUTTON :: 0x00000000
+BS_DEFPUSHBUTTON :: 0x00000001
+BS_CHECKBOX :: 0x00000002
+BS_AUTOCHECKBOX :: 0x00000003
+BS_RADIOBUTTON :: 0x00000004
+BS_3STATE :: 0x00000005
+BS_AUTO3STATE :: 0x00000006
+BS_GROUPBOX :: 0x00000007
+BS_USERBUTTON :: 0x00000008
+BS_AUTORADIOBUTTON :: 0x00000009
+BS_PUSHBOX :: 0x0000000A
+BS_OWNERDRAW :: 0x0000000B
+BS_TYPEMASK :: 0x0000000F
+BS_LEFTTEXT :: 0x00000020
+BS_TEXT :: 0x00000000
+BS_ICON :: 0x00000040
+BS_BITMAP :: 0x00000080
+BS_LEFT :: 0x00000100
+BS_RIGHT :: 0x00000200
+BS_CENTER :: 0x00000300
+BS_TOP :: 0x00000400
+BS_BOTTOM :: 0x00000800
+BS_VCENTER :: 0x00000C00
+BS_PUSHLIKE :: 0x00001000
+BS_MULTILINE :: 0x00002000
+BS_NOTIFY :: 0x00004000
+BS_FLAT :: 0x00008000
+BS_RIGHTBUTTON :: BS_LEFTTEXT
+
+// Button Control Messages
+BST_UNCHECKED :: 0x0000
+BST_CHECKED :: 0x0001
+BST_INDETERMINATE :: 0x0002
+BST_PUSHED :: 0x0004
+BST_FOCUS :: 0x0008
+
+// Static Control Constants
+SS_LEFT :: 0x00000000
+SS_CENTER :: 0x00000001
+SS_RIGHT :: 0x00000002
+SS_ICON :: 0x00000003
+SS_BLACKRECT :: 0x00000004
+SS_GRAYRECT :: 0x00000005
+SS_WHITERECT :: 0x00000006
+SS_BLACKFRAME :: 0x00000007
+SS_GRAYFRAME :: 0x00000008
+SS_WHITEFRAME :: 0x00000009
+SS_USERITEM :: 0x0000000A
+SS_SIMPLE :: 0x0000000B
+SS_LEFTNOWORDWRAP :: 0x0000000C
+SS_OWNERDRAW :: 0x0000000D
+SS_BITMAP :: 0x0000000E
+SS_ENHMETAFILE :: 0x0000000F
+SS_ETCHEDHORZ :: 0x00000010
+SS_ETCHEDVERT :: 0x00000011
+SS_ETCHEDFRAME :: 0x00000012
+SS_TYPEMASK :: 0x0000001F
+SS_REALSIZECONTROL :: 0x00000040
+SS_NOPREFIX :: 0x00000080
+SS_NOTIFY :: 0x00000100
+SS_CENTERIMAGE :: 0x00000200
+SS_RIGHTJUST :: 0x00000400
+SS_REALSIZEIMAGE :: 0x00000800
+SS_SUNKEN :: 0x00001000
+SS_EDITCONTROL :: 0x00002000
+SS_ENDELLIPSIS :: 0x00004000
+SS_PATHELLIPSIS :: 0x00008000
+SS_WORDELLIPSIS :: 0x0000C000
+SS_ELLIPSISMASK :: 0x0000C000
+
+// Edit Control Styles
+ES_LEFT :: 0x0000
+ES_CENTER :: 0x0001
+ES_RIGHT :: 0x0002
+ES_MULTILINE :: 0x0004
+ES_UPPERCASE :: 0x0008
+ES_LOWERCASE :: 0x0010
+ES_PASSWORD :: 0x0020
+ES_AUTOVSCROLL :: 0x0040
+ES_AUTOHSCROLL :: 0x0080
+ES_NOHIDESEL :: 0x0100
+ES_OEMCONVERT :: 0x0400
+ES_READONLY :: 0x0800
+ES_WANTRETURN :: 0x1000
+ES_NUMBER :: 0x2000
+
+// Edit Control Notification Codes
+EN_SETFOCUS :: 0x0100
+EN_KILLFOCUS :: 0x0200
+EN_CHANGE :: 0x0300
+EN_UPDATE :: 0x0400
+EN_ERRSPACE :: 0x0500
+EN_MAXTEXT :: 0x0501
+EN_HSCROLL :: 0x0601
+EN_VSCROLL :: 0x0602
+EN_ALIGN_LTR_EC :: 0x0700
+EN_ALIGN_RTL_EC :: 0x0701
+
+// Font Weights
+FW_DONTCARE :: 0
+FW_THIN :: 100
+FW_EXTRALIGHT :: 200
+FW_LIGHT :: 300
+FW_NORMAL :: 400
+FW_MEDIUM :: 500
+FW_SEMIBOLD :: 600
+FW_BOLD :: 700
+FW_EXTRABOLD :: 800
+FW_HEAVY :: 900
+
+FW_ULTRALIGHT :: FW_EXTRALIGHT
+FW_REGULAR :: FW_NORMAL
+FW_DEMIBOLD :: FW_SEMIBOLD
+FW_ULTRABOLD :: FW_EXTRABOLD
+FW_BLACK :: FW_HEAVY
+
+PTIMERAPCROUTINE :: #type proc "stdcall" (lpArgToCompletionRoutine: LPVOID, dwTimerLowValue, dwTimerHighValue: DWORD)
+
+TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD)
+
+WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
+
+HOOKPROC :: #type proc "stdcall" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
+
+CWPRETSTRUCT :: struct {
+ lResult: LRESULT,
+ lParam: LPARAM,
+ wParam: WPARAM,
+ message: UINT,
+ hwnd: HWND,
+}
+
+KBDLLHOOKSTRUCT :: struct {
+ vkCode: DWORD,
+ scanCode: DWORD,
+ flags: DWORD,
+ time: DWORD,
+ dwExtraInfo: ULONG_PTR,
+}
+
+WNDCLASSA :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+}
+
+WNDCLASSW :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+}
+
+WNDCLASSEXA :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+ hIconSm: HICON,
+}
+
+WNDCLASSEXW :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+ hIconSm: HICON,
+}
+
+MSG :: struct {
+ hwnd: HWND,
+ message: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ time: DWORD,
+ pt: POINT,
+}
+
+LPMSG :: ^MSG
+
+PAINTSTRUCT :: struct {
+ hdc: HDC,
+ fErase: BOOL,
+ rcPaint: RECT,
+ fRestore: BOOL,
+ fIncUpdate: BOOL,
+ rgbReserved: [32]BYTE,
+}
+
+TRACKMOUSEEVENT :: struct {
+ cbSize: DWORD,
+ dwFlags: DWORD,
+ hwndTrack: HWND,
+ dwHoverTime: DWORD,
+}
WIN32_FIND_DATAW :: struct {
dwFileAttributes: DWORD,
@@ -191,6 +797,763 @@ WIN32_FIND_DATAW :: struct {
cAlternateFileName: [14]wchar_t,
}
+CREATESTRUCTA :: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCSTR,
+ lpszClass: LPCSTR,
+ dwExStyle: DWORD,
+}
+
+CREATESTRUCTW:: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCWSTR,
+ lpszClass: LPCWSTR,
+ dwExStyle: DWORD,
+}
+
+DEVMODEW :: struct {
+ dmDeviceName: [32]wchar_t,
+ dmSpecVersion: WORD,
+ dmDriverVersion: WORD,
+ dmSize: WORD,
+ dmDriverExtra: WORD,
+ dmFields: DWORD,
+ using _: struct #raw_union {
+ // Printer only fields.
+ using _: struct {
+ dmOrientation: c_short,
+ dmPaperSize: c_short,
+ dmPaperLength: c_short,
+ dmPaperWidth: c_short,
+ dmScale: c_short,
+ dmCopies: c_short,
+ dmDefaultSource: c_short,
+ dmPrintQuality: c_short,
+ },
+ // Display only fields.
+ using _: struct {
+ dmPosition: POINT,
+ dmDisplayOrientation: DWORD,
+ dmDisplayFixedOutput: DWORD,
+ },
+ },
+ dmColor: c_short,
+ dmDuplex: c_short,
+ dmYResolution: c_short,
+ dmTTOption: c_short,
+ dmCollate: c_short,
+ dmFormName: [32]wchar_t,
+ dmLogPixels: WORD,
+ dmBitsPerPel: DWORD,
+ dmPelsWidth: DWORD,
+ dmPelsHeight: DWORD,
+ using _: struct #raw_union {
+ dmDisplayFlags: DWORD,
+ dmNup: DWORD,
+ },
+ dmDisplayFrequency: DWORD,
+ dmICMMethod: DWORD,
+ dmICMIntent: DWORD,
+ dmMediaType: DWORD,
+ dmDitherType: DWORD,
+ dmReserved1: DWORD,
+ dmReserved2: DWORD,
+ dmPanningWidth: DWORD,
+ dmPanningHeight: DWORD,
+}
+
+// MessageBox() Flags
+MB_OK :: 0x00000000
+MB_OKCANCEL :: 0x00000001
+MB_ABORTRETRYIGNORE :: 0x00000002
+MB_YESNOCANCEL :: 0x00000003
+MB_YESNO :: 0x00000004
+MB_RETRYCANCEL :: 0x00000005
+MB_CANCELTRYCONTINUE :: 0x00000006
+
+MB_ICONHAND :: 0x00000010
+MB_ICONQUESTION :: 0x00000020
+MB_ICONEXCLAMATION :: 0x00000030
+MB_ICONASTERISK :: 0x00000040
+MB_USERICON :: 0x00000080
+MB_ICONWARNING :: MB_ICONEXCLAMATION
+MB_ICONERROR :: MB_ICONHAND
+MB_ICONINFORMATION :: MB_ICONASTERISK
+MB_ICONSTOP :: MB_ICONHAND
+
+MB_DEFBUTTON1 :: 0x00000000
+MB_DEFBUTTON2 :: 0x00000100
+MB_DEFBUTTON3 :: 0x00000200
+MB_DEFBUTTON4 :: 0x00000300
+
+MB_APPLMODAL :: 0x00000000
+MB_SYSTEMMODAL :: 0x00001000
+MB_TASKMODAL :: 0x00002000
+MB_HELP :: 0x00004000 // Help Button
+
+MB_NOFOCUS :: 0x00008000
+MB_SETFOREGROUND :: 0x00010000
+MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
+MB_TOPMOST :: 0x00040000
+MB_RIGHT :: 0x00080000
+MB_RTLREADING :: 0x00100000
+
+MB_SERVICE_NOTIFICATION :: 0x00200000
+MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000
+
+MB_TYPEMASK :: 0x0000000F
+MB_ICONMASK :: 0x000000F0
+MB_DEFMASK :: 0x00000F00
+MB_MODEMASK :: 0x00003000
+MB_MISCMASK :: 0x0000C000
+
+// Dialog Box Command IDs
+IDOK :: 1
+IDCANCEL :: 2
+IDABORT :: 3
+IDRETRY :: 4
+IDIGNORE :: 5
+IDYES :: 6
+IDNO :: 7
+IDCLOSE :: 8
+IDHELP :: 9
+IDTRYAGAIN :: 10
+IDCONTINUE :: 11
+IDTIMEOUT :: 32000
+
+CS_VREDRAW : UINT : 0x0001
+CS_HREDRAW : UINT : 0x0002
+CS_DBLCLKS : UINT : 0x0008
+CS_OWNDC : UINT : 0x0020
+CS_CLASSDC : UINT : 0x0040
+CS_PARENTDC : UINT : 0x0080
+CS_NOCLOSE : UINT : 0x0200
+CS_SAVEBITS : UINT : 0x0800
+CS_BYTEALIGNCLIENT : UINT : 0x1000
+CS_BYTEALIGNWINDOW : UINT : 0x2000
+CS_GLOBALCLASS : UINT : 0x4000
+CS_DROPSHADOW : UINT : 0x0002_0000
+
+WS_BORDER : UINT : 0x0080_0000
+WS_CAPTION : UINT : 0x00C0_0000
+WS_CHILD : UINT : 0x4000_0000
+WS_CHILDWINDOW : UINT : WS_CHILD
+WS_CLIPCHILDREN : UINT : 0x0200_0000
+WS_CLIPSIBLINGS : UINT : 0x0400_0000
+WS_DISABLED : UINT : 0x0800_0000
+WS_DLGFRAME : UINT : 0x0040_0000
+WS_GROUP : UINT : 0x0002_0000
+WS_HSCROLL : UINT : 0x0010_0000
+WS_ICONIC : UINT : 0x2000_0000
+WS_MAXIMIZE : UINT : 0x0100_0000
+WS_MAXIMIZEBOX : UINT : 0x0001_0000
+WS_MINIMIZE : UINT : 0x2000_0000
+WS_MINIMIZEBOX : UINT : 0x0002_0000
+WS_OVERLAPPED : UINT : 0x0000_0000
+WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
+WS_POPUP : UINT : 0x8000_0000
+WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU
+WS_SIZEBOX : UINT : 0x0004_0000
+WS_SYSMENU : UINT : 0x0008_0000
+WS_TABSTOP : UINT : 0x0001_0000
+WS_THICKFRAME : UINT : 0x0004_0000
+WS_TILED : UINT : 0x0000_0000
+WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE
+WS_VISIBLE : UINT : 0x1000_0000
+WS_VSCROLL : UINT : 0x0020_0000
+
+PBS_SMOOTH :: 0x01
+PBS_VERTICAL :: 0x04
+
+QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
+QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
+QS_ALLPOSTMESSAGE : UINT : 0x0100
+QS_HOTKEY : UINT : 0x0080
+QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT
+QS_KEY : UINT : 0x0001
+QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON
+QS_MOUSEBUTTON : UINT : 0x0004
+QS_MOUSEMOVE : UINT : 0x0002
+QS_PAINT : UINT : 0x0020
+QS_POSTMESSAGE : UINT : 0x0008
+QS_RAWINPUT : UINT : 0x0400
+QS_SENDMESSAGE : UINT : 0x0040
+QS_TIMER : UINT : 0x0010
+
+PM_NOREMOVE : UINT : 0x0000
+PM_REMOVE : UINT : 0x0001
+PM_NOYIELD : UINT : 0x0002
+
+PM_QS_INPUT : UINT : QS_INPUT << 16
+PM_QS_PAINT : UINT : QS_PAINT << 16
+PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16
+PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16
+
+SW_HIDE : c_int : 0
+SW_SHOWNORMAL : c_int : SW_NORMAL
+SW_NORMAL : c_int : 1
+SW_SHOWMINIMIZED : c_int : 2
+SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE
+SW_MAXIMIZE : c_int : 3
+SW_SHOWNOACTIVATE : c_int : 4
+SW_SHOW : c_int : 5
+SW_MINIMIZE : c_int : 6
+SW_SHOWMINNOACTIVE : c_int : 7
+SW_SHOWNA : c_int : 8
+SW_RESTORE : c_int : 9
+SW_SHOWDEFAULT : c_int : 10
+SW_FORCEMINIMIZE : c_int : 11
+
+// SetWindowPos Flags
+SWP_NOSIZE :: 0x0001
+SWP_NOMOVE :: 0x0002
+SWP_NOZORDER :: 0x0004
+SWP_NOREDRAW :: 0x0008
+SWP_NOACTIVATE :: 0x0010
+SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE
+SWP_SHOWWINDOW :: 0x0040
+SWP_HIDEWINDOW :: 0x0080
+SWP_NOCOPYBITS :: 0x0100
+SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering
+SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING
+
+SWP_DRAWFRAME :: SWP_FRAMECHANGED
+SWP_NOREPOSITION :: SWP_NOOWNERZORDER
+
+SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
+SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
+
+HWND_TOP :: HWND( uintptr(0)) // 0
+HWND_BOTTOM :: HWND( uintptr(1)) // 1
+HWND_TOPMOST :: HWND(~uintptr(0)) // -1
+HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2
+
+// Window field offsets for GetWindowLong()
+GWL_STYLE :: -16
+GWL_EXSTYLE :: -20
+GWL_ID :: -12
+
+when ODIN_ARCH == .i386 {
+ GWL_WNDPROC :: -4
+ GWL_HINSTANCE :: -6
+ GWL_HWNDPARENT :: -8
+ GWL_USERDATA :: -21
+}
+
+GWLP_WNDPROC :: -4
+GWLP_HINSTANCE :: -6
+GWLP_HWNDPARENT :: -8
+GWLP_USERDATA :: -21
+GWLP_ID :: -12
+
+// Class field offsets for GetClassLong()
+GCL_CBWNDEXTRA :: -18
+GCL_CBCLSEXTRA :: -20
+GCL_STYLE :: -26
+GCW_ATOM :: -32
+
+when ODIN_ARCH == .i386 {
+ GCL_MENUNAME :: -8
+ GCL_HBRBACKGROUND :: -10
+ GCL_HCURSOR :: -12
+ GCL_HICON :: -14
+ GCL_HMODULE :: -16
+ GCL_WNDPROC :: -24
+ GCL_HICONSM :: -34
+}
+
+GCLP_MENUNAME :: -8
+GCLP_HBRBACKGROUND :: -10
+GCLP_HCURSOR :: -12
+GCLP_HICON :: -14
+GCLP_HMODULE :: -16
+GCLP_WNDPROC :: -24
+GCLP_HICONSM :: -34
+
+// GetSystemMetrics() codes
+SM_CXSCREEN :: 0
+SM_CYSCREEN :: 1
+SM_CXVSCROLL :: 2
+SM_CYHSCROLL :: 3
+SM_CYCAPTION :: 4
+SM_CXBORDER :: 5
+SM_CYBORDER :: 6
+SM_CXDLGFRAME :: 7
+SM_CYDLGFRAME :: 8
+SM_CYVTHUMB :: 9
+SM_CXHTHUMB :: 10
+SM_CXICON :: 11
+SM_CYICON :: 12
+SM_CXCURSOR :: 13
+SM_CYCURSOR :: 14
+SM_CYMENU :: 15
+SM_CXFULLSCREEN :: 16
+SM_CYFULLSCREEN :: 17
+SM_CYKANJIWINDOW :: 18
+SM_MOUSEPRESENT :: 19
+SM_CYVSCROLL :: 20
+SM_CXHSCROLL :: 21
+SM_DEBUG :: 22
+SM_SWAPBUTTON :: 23
+SM_RESERVED1 :: 24
+SM_RESERVED2 :: 25
+SM_RESERVED3 :: 26
+SM_RESERVED4 :: 27
+SM_CXMIN :: 28
+SM_CYMIN :: 29
+SM_CXSIZE :: 30
+SM_CYSIZE :: 31
+SM_CXFRAME :: 32
+SM_CYFRAME :: 33
+SM_CXMINTRACK :: 34
+SM_CYMINTRACK :: 35
+SM_CXDOUBLECLK :: 36
+SM_CYDOUBLECLK :: 37
+SM_CXICONSPACING :: 38
+SM_CYICONSPACING :: 39
+SM_MENUDROPALIGNMENT :: 40
+SM_PENWINDOWS :: 41
+SM_DBCSENABLED :: 42
+SM_CMOUSEBUTTONS :: 43
+
+SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change
+SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change
+SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change
+SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change
+
+SM_SECURE :: 44
+SM_CXEDGE :: 45
+SM_CYEDGE :: 46
+SM_CXMINSPACING :: 47
+SM_CYMINSPACING :: 48
+SM_CXSMICON :: 49
+SM_CYSMICON :: 50
+SM_CYSMCAPTION :: 51
+SM_CXSMSIZE :: 52
+SM_CYSMSIZE :: 53
+SM_CXMENUSIZE :: 54
+SM_CYMENUSIZE :: 55
+SM_ARRANGE :: 56
+SM_CXMINIMIZED :: 57
+SM_CYMINIMIZED :: 58
+SM_CXMAXTRACK :: 59
+SM_CYMAXTRACK :: 60
+SM_CXMAXIMIZED :: 61
+SM_CYMAXIMIZED :: 62
+SM_NETWORK :: 63
+SM_CLEANBOOT :: 67
+SM_CXDRAG :: 68
+SM_CYDRAG :: 69
+
+SM_SHOWSOUNDS :: 70
+SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()!
+SM_CYMENUCHECK :: 72
+SM_SLOWMACHINE :: 73
+SM_MIDEASTENABLED :: 74
+SM_MOUSEWHEELPRESENT :: 75
+SM_XVIRTUALSCREEN :: 76
+SM_YVIRTUALSCREEN :: 77
+SM_CXVIRTUALSCREEN :: 78
+SM_CYVIRTUALSCREEN :: 79
+SM_CMONITORS :: 80
+SM_SAMEDISPLAYFORMAT :: 81
+SM_IMMENABLED :: 82
+SM_CXFOCUSBORDER :: 83
+SM_CYFOCUSBORDER :: 84
+SM_TABLETPC :: 86
+SM_MEDIACENTER :: 87
+SM_STARTER :: 88
+SM_SERVERR2 :: 89
+
+SM_MOUSEHORIZONTALWHEELPRESENT :: 91
+
+SM_CXPADDEDBORDER :: 92
+SM_DIGITIZER :: 94
+SM_MAXIMUMTOUCHES :: 95
+SM_CMETRICS :: 97
+
+SM_REMOTESESSION :: 0x1000
+SM_SHUTTINGDOWN :: 0x2000
+SM_REMOTECONTROL :: 0x2001
+SM_CARETBLINKINGENABLED :: 0x2002
+SM_CONVERTIBLESLATEMODE :: 0x2003
+SM_SYSTEMDOCKED :: 0x2004
+
+// System Menu Command Values
+SC_SIZE :: 0xF000
+SC_MOVE :: 0xF010
+SC_MINIMIZE :: 0xF020
+SC_MAXIMIZE :: 0xF030
+SC_NEXTWINDOW :: 0xF040
+SC_PREVWINDOW :: 0xF050
+SC_CLOSE :: 0xF060
+SC_VSCROLL :: 0xF070
+SC_HSCROLL :: 0xF080
+SC_MOUSEMENU :: 0xF090
+SC_KEYMENU :: 0xF100
+SC_ARRANGE :: 0xF110
+SC_RESTORE :: 0xF120
+SC_TASKLIST :: 0xF130
+SC_SCREENSAVE :: 0xF140
+SC_HOTKEY :: 0xF150
+SC_DEFAULT :: 0xF160
+SC_MONITORPOWER :: 0xF170
+SC_CONTEXTHELP :: 0xF180
+SC_SEPARATOR :: 0xF00F
+SCF_ISSECURE :: 0x00000001
+SC_ICON :: SC_MINIMIZE
+SC_ZOOM :: SC_MAXIMIZE
+
+CW_USEDEFAULT : c_int : -2147483648
+
+SIZE_RESTORED :: 0
+SIZE_MINIMIZED :: 1
+SIZE_MAXIMIZED :: 2
+SIZE_MAXSHOW :: 3
+SIZE_MAXHIDE :: 4
+
+WMSZ_LEFT :: 1
+WMSZ_RIGHT :: 2
+WMSZ_TOP :: 3
+WMSZ_TOPLEFT :: 4
+WMSZ_TOPRIGHT :: 5
+WMSZ_BOTTOM :: 6
+WMSZ_BOTTOMLEFT :: 7
+WMSZ_BOTTOMRIGHT :: 8
+
+// Key State Masks for Mouse Messages
+MK_LBUTTON :: 0x0001
+MK_RBUTTON :: 0x0002
+MK_SHIFT :: 0x0004
+MK_CONTROL :: 0x0008
+MK_MBUTTON :: 0x0010
+MK_XBUTTON1 :: 0x0020
+MK_XBUTTON2 :: 0x0040
+
+// Value for rolling one detent
+WHEEL_DELTA :: 120
+
+// Setting to scroll one page for SPI_GET/SETWHEELSCROLLLINES
+WHEEL_PAGESCROLL :: max(UINT)
+
+// XButton values are WORD flags
+XBUTTON1 :: 0x0001
+XBUTTON2 :: 0x0002
+// Were there to be an XBUTTON3, its value would be 0x0004
+
+MAPVK_VK_TO_VSC :: 0
+MAPVK_VSC_TO_VK :: 1
+MAPVK_VK_TO_CHAR :: 2
+MAPVK_VSC_TO_VK_EX :: 3
+MAPVK_VK_TO_VSC_EX :: 4
+
+TME_HOVER :: 0x00000001
+TME_LEAVE :: 0x00000002
+TME_NONCLIENT :: 0x00000010
+TME_QUERY :: 0x40000000
+TME_CANCEL :: 0x80000000
+HOVER_DEFAULT :: 0xFFFFFFFF
+
+USER_TIMER_MAXIMUM :: 0x7FFFFFFF
+USER_TIMER_MINIMUM :: 0x0000000A
+
+// WM_ACTIVATE state values
+WA_INACTIVE :: 0
+WA_ACTIVE :: 1
+WA_CLICKACTIVE :: 2
+
+// SetWindowsHook() codes
+WH_MIN :: -1
+WH_MSGFILTER :: -1
+WH_JOURNALRECORD :: 0
+WH_JOURNALPLAYBACK :: 1
+WH_KEYBOARD :: 2
+WH_GETMESSAGE :: 3
+WH_CALLWNDPROC :: 4
+WH_CBT :: 5
+WH_SYSMSGFILTER :: 6
+WH_MOUSE :: 7
+WH_HARDWARE :: 8
+WH_DEBUG :: 9
+WH_SHELL :: 10
+WH_FOREGROUNDIDLE :: 11
+WH_CALLWNDPROCRET :: 12
+WH_KEYBOARD_LL :: 13
+WH_MOUSE_LL :: 14
+WH_MAX :: 14
+WH_MINHOOK :: WH_MIN
+WH_MAXHOOK :: WH_MAX
+
+// Hook Codes
+HC_ACTION :: 0
+HC_GETNEXT :: 1
+HC_SKIP :: 2
+HC_NOREMOVE :: 3
+HC_NOREM :: HC_NOREMOVE
+HC_SYSMODALON :: 4
+HC_SYSMODALOFF :: 5
+
+// CBT Hook Codes
+HCBT_MOVESIZE :: 0
+HCBT_MINMAX :: 1
+HCBT_QS :: 2
+HCBT_CREATEWND :: 3
+HCBT_DESTROYWND :: 4
+HCBT_ACTIVATE :: 5
+HCBT_CLICKSKIPPED :: 6
+HCBT_KEYSKIPPED :: 7
+HCBT_SYSCOMMAND :: 8
+HCBT_SETFOCUS :: 9
+
+_IDC_APPSTARTING := rawptr(uintptr(32650))
+_IDC_ARROW := rawptr(uintptr(32512))
+_IDC_CROSS := rawptr(uintptr(32515))
+_IDC_HAND := rawptr(uintptr(32649))
+_IDC_HELP := rawptr(uintptr(32651))
+_IDC_IBEAM := rawptr(uintptr(32513))
+_IDC_ICON := rawptr(uintptr(32641))
+_IDC_NO := rawptr(uintptr(32648))
+_IDC_SIZE := rawptr(uintptr(32640))
+_IDC_SIZEALL := rawptr(uintptr(32646))
+_IDC_SIZENESW := rawptr(uintptr(32643))
+_IDC_SIZENS := rawptr(uintptr(32645))
+_IDC_SIZENWSE := rawptr(uintptr(32642))
+_IDC_SIZEWE := rawptr(uintptr(32644))
+_IDC_UPARROW := rawptr(uintptr(32516))
+_IDC_WAIT := rawptr(uintptr(32514))
+
+IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
+IDC_ARROW := cstring(_IDC_ARROW)
+IDC_CROSS := cstring(_IDC_CROSS)
+IDC_HAND := cstring(_IDC_HAND)
+IDC_HELP := cstring(_IDC_HELP)
+IDC_IBEAM := cstring(_IDC_IBEAM)
+IDC_ICON := cstring(_IDC_ICON)
+IDC_NO := cstring(_IDC_NO)
+IDC_SIZE := cstring(_IDC_SIZE)
+IDC_SIZEALL := cstring(_IDC_SIZEALL)
+IDC_SIZENESW := cstring(_IDC_SIZENESW)
+IDC_SIZENS := cstring(_IDC_SIZENS)
+IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
+IDC_SIZEWE := cstring(_IDC_SIZEWE)
+IDC_UPARROW := cstring(_IDC_UPARROW)
+IDC_WAIT := cstring(_IDC_WAIT)
+
+
+_IDI_APPLICATION := rawptr(uintptr(32512))
+_IDI_HAND := rawptr(uintptr(32513))
+_IDI_QUESTION := rawptr(uintptr(32514))
+_IDI_EXCLAMATION := rawptr(uintptr(32515))
+_IDI_ASTERISK := rawptr(uintptr(32516))
+_IDI_WINLOGO := rawptr(uintptr(32517))
+_IDI_SHIELD := rawptr(uintptr(32518))
+IDI_APPLICATION := cstring(_IDI_APPLICATION)
+IDI_HAND := cstring(_IDI_HAND)
+IDI_QUESTION := cstring(_IDI_QUESTION)
+IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION)
+IDI_ASTERISK := cstring(_IDI_ASTERISK)
+IDI_WINLOGO := cstring(_IDI_WINLOGO)
+IDI_SHIELD := cstring(_IDI_SHIELD)
+IDI_WARNING := IDI_EXCLAMATION
+IDI_ERROR := IDI_HAND
+IDI_INFORMATION := IDI_ASTERISK
+
+
+// DIB color table identifiers
+DIB_RGB_COLORS :: 0
+DIB_PAL_COLORS :: 1
+
+// constants for CreateDIBitmap
+CBM_INIT :: 0x04 // initialize bitmap
+
+// Region Flags
+ERROR :: 0
+NULLREGION :: 1
+SIMPLEREGION :: 2
+COMPLEXREGION :: 3
+RGN_ERROR :: ERROR
+
+// StretchBlt() Modes
+BLACKONWHITE :: 1
+WHITEONBLACK :: 2
+COLORONCOLOR :: 3
+HALFTONE :: 4
+MAXSTRETCHBLTMODE :: 4
+
+// Binary raster ops
+R2_BLACK :: 1 // 0
+R2_NOTMERGEPEN :: 2 // DPon
+R2_MASKNOTPEN :: 3 // DPna
+R2_NOTCOPYPEN :: 4 // PN
+R2_MASKPENNOT :: 5 // PDna
+R2_NOT :: 6 // Dn
+R2_XORPEN :: 7 // DPx
+R2_NOTMASKPEN :: 8 // DPan
+R2_MASKPEN :: 9 // DPa
+R2_NOTXORPEN :: 10 // DPxn
+R2_NOP :: 11 // D
+R2_MERGENOTPEN :: 12 // DPno
+R2_COPYPEN :: 13 // P
+R2_MERGEPENNOT :: 14 // PDno
+R2_MERGEPEN :: 15 // DPo
+R2_WHITE :: 16 // 1
+R2_LAST :: 16
+
+// Ternary raster operations
+SRCCOPY : DWORD : 0x00CC0020 // dest = source
+SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest
+SRCAND : DWORD : 0x008800C6 // dest = source AND dest
+SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest
+SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest)
+NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source)
+NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest)
+MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern
+MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest
+PATCOPY : DWORD : 0x00F00021 // dest = pattern
+PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo
+PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest
+DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest)
+BLACKNESS : DWORD : 0x00000042 // dest = BLACK
+WHITENESS : DWORD : 0x00FF0062 // dest = WHITE
+NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call
+CAPTUREBLT : DWORD : 0x40000000 // Include layered windows
+
+// Stock Logical Objects
+WHITE_BRUSH :: 0
+LTGRAY_BRUSH :: 1
+GRAY_BRUSH :: 2
+DKGRAY_BRUSH :: 3
+BLACK_BRUSH :: 4
+NULL_BRUSH :: 5
+HOLLOW_BRUSH :: NULL_BRUSH
+WHITE_PEN :: 6
+BLACK_PEN :: 7
+NULL_PEN :: 8
+OEM_FIXED_FONT :: 10
+ANSI_FIXED_FONT :: 11
+ANSI_VAR_FONT :: 12
+SYSTEM_FONT :: 13
+DEVICE_DEFAULT_FONT :: 14
+DEFAULT_PALETTE :: 15
+SYSTEM_FIXED_FONT :: 16
+DEFAULT_GUI_FONT :: 17
+DC_BRUSH :: 18
+DC_PEN :: 19
+STOCK_LAST :: 19
+
+CLR_INVALID :: 0xFFFFFFFF
+
+RGBQUAD :: struct {
+ rgbBlue: BYTE,
+ rgbGreen: BYTE,
+ rgbRed: BYTE,
+ rgbReserved: BYTE,
+}
+
+PIXELFORMATDESCRIPTOR :: struct {
+ nSize: WORD,
+ nVersion: WORD,
+ dwFlags: DWORD,
+ iPixelType: BYTE,
+ cColorBits: BYTE,
+ cRedBits: BYTE,
+ cRedShift: BYTE,
+ cGreenBits: BYTE,
+ cGreenShift: BYTE,
+ cBlueBits: BYTE,
+ cBlueShift: BYTE,
+ cAlphaBits: BYTE,
+ cAlphaShift: BYTE,
+ cAccumBits: BYTE,
+ cAccumRedBits: BYTE,
+ cAccumGreenBits: BYTE,
+ cAccumBlueBits: BYTE,
+ cAccumAlphaBits: BYTE,
+ cDepthBits: BYTE,
+ cStencilBits: BYTE,
+ cAuxBuffers: BYTE,
+ iLayerType: BYTE,
+ bReserved: BYTE,
+ dwLayerMask: DWORD,
+ dwVisibleMask: DWORD,
+ dwDamageMask: DWORD,
+}
+
+BITMAPINFOHEADER :: struct {
+ biSize: DWORD,
+ biWidth: LONG,
+ biHeight: LONG,
+ biPlanes: WORD,
+ biBitCount: WORD,
+ biCompression: DWORD,
+ biSizeImage: DWORD,
+ biXPelsPerMeter: LONG,
+ biYPelsPerMeter: LONG,
+ biClrUsed: DWORD,
+ biClrImportant: DWORD,
+}
+
+BITMAPINFO :: struct {
+ bmiHeader: BITMAPINFOHEADER,
+ bmiColors: [1]RGBQUAD,
+}
+
+// pixel types
+PFD_TYPE_RGBA :: 0
+PFD_TYPE_COLORINDEX :: 1
+
+// layer types
+PFD_MAIN_PLANE :: 0
+PFD_OVERLAY_PLANE :: 1
+PFD_UNDERLAY_PLANE :: -1
+
+// PIXELFORMATDESCRIPTOR flags
+PFD_DOUBLEBUFFER :: 0x00000001
+PFD_STEREO :: 0x00000002
+PFD_DRAW_TO_WINDOW :: 0x00000004
+PFD_DRAW_TO_BITMAP :: 0x00000008
+PFD_SUPPORT_GDI :: 0x00000010
+PFD_SUPPORT_OPENGL :: 0x00000020
+PFD_GENERIC_FORMAT :: 0x00000040
+PFD_NEED_PALETTE :: 0x00000080
+PFD_NEED_SYSTEM_PALETTE :: 0x00000100
+PFD_SWAP_EXCHANGE :: 0x00000200
+PFD_SWAP_COPY :: 0x00000400
+PFD_SWAP_LAYER_BUFFERS :: 0x00000800
+PFD_GENERIC_ACCELERATED :: 0x00001000
+PFD_SUPPORT_DIRECTDRAW :: 0x00002000
+PFD_DIRECT3D_ACCELERATED :: 0x00004000
+PFD_SUPPORT_COMPOSITION :: 0x00008000
+
+// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only
+PFD_DEPTH_DONTCARE :: 0x20000000
+PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
+PFD_STEREO_DONTCARE :: 0x80000000
+
+// constants for the biCompression field
+BI_RGB :: 0
+BI_RLE8 :: 1
+BI_RLE4 :: 2
+BI_BITFIELDS :: 3
+BI_JPEG :: 4
+BI_PNG :: 5
+
WSA_FLAG_OVERLAPPED: DWORD : 0x01
WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
@@ -230,41 +1593,26 @@ STD_ERROR_HANDLE: DWORD : ~DWORD(0) -12 + 1
PROGRESS_CONTINUE: DWORD : 0
-ERROR_FILE_NOT_FOUND: DWORD : 2
-ERROR_PATH_NOT_FOUND: DWORD : 3
-ERROR_ACCESS_DENIED: DWORD : 5
-ERROR_NOT_ENOUGH_MEMORY: DWORD : 8
-ERROR_INVALID_HANDLE: DWORD : 6
-ERROR_NO_MORE_FILES: DWORD : 18
-ERROR_SHARING_VIOLATION: DWORD : 32
-ERROR_LOCK_VIOLATION: DWORD : 33
-ERROR_HANDLE_EOF: DWORD : 38
-ERROR_NOT_SUPPORTED: DWORD : 50
-ERROR_FILE_EXISTS: DWORD : 80
-ERROR_INVALID_PARAMETER: DWORD : 87
-ERROR_BROKEN_PIPE: DWORD : 109
-ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120
-ERROR_INSUFFICIENT_BUFFER: DWORD : 122
-ERROR_INVALID_NAME: DWORD : 123
-ERROR_LOCK_FAILED: DWORD : 167
-ERROR_ALREADY_EXISTS: DWORD : 183
-ERROR_NO_DATA: DWORD : 232
-ERROR_ENVVAR_NOT_FOUND: DWORD : 203
-ERROR_OPERATION_ABORTED: DWORD : 995
-ERROR_IO_PENDING: DWORD : 997
-ERROR_TIMEOUT: DWORD : 0x5B4
-ERROR_NO_UNICODE_TRANSLATION: DWORD : 1113
-
-E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
-
INVALID_HANDLE :: HANDLE(~uintptr(0))
INVALID_HANDLE_VALUE :: INVALID_HANDLE
FACILITY_NT_BIT: DWORD : 0x1000_0000
-FORMAT_MESSAGE_FROM_SYSTEM: DWORD : 0x00001000
-FORMAT_MESSAGE_FROM_HMODULE: DWORD : 0x00000800
-FORMAT_MESSAGE_IGNORE_INSERTS: DWORD : 0x00000200
+FORMAT_MESSAGE_ALLOCATE_BUFFER :: 0x00000100
+FORMAT_MESSAGE_IGNORE_INSERTS :: 0x00000200
+FORMAT_MESSAGE_FROM_STRING :: 0x00000400
+FORMAT_MESSAGE_FROM_HMODULE :: 0x00000800
+FORMAT_MESSAGE_FROM_SYSTEM :: 0x00001000
+FORMAT_MESSAGE_ARGUMENT_ARRAY :: 0x00002000
+FORMAT_MESSAGE_MAX_WIDTH_MASK :: 0x000000FF
+
+LMEM_FIXED :: 0x0000
+LMEM_MOVEABLE :: 0x0002
+LMEM_ZEROINIT :: 0x0040
+LHND :: 0x0042
+LPTR :: 0x0040
+NONZEROLHND :: LMEM_MOVEABLE
+NONZEROLPTR :: LMEM_FIXED
TLS_OUT_OF_INDEXES: DWORD : 0xFFFFFFFF
@@ -376,6 +1724,18 @@ FILE_TYPE_DISK :: 0x0001
FILE_TYPE_CHAR :: 0x0002
FILE_TYPE_PIPE :: 0x0003
+RECT :: struct {left, top, right, bottom: LONG}
+POINT :: struct {x, y: LONG}
+
+WINDOWPOS :: struct {
+ hwnd: HWND,
+ hwndInsertAfter: HWND,
+ x: c_int,
+ y: c_int,
+ cx: c_int,
+ cy: c_int,
+ flags: UINT,
+}
when size_of(uintptr) == 4 {
WSADATA :: struct {
@@ -475,6 +1835,13 @@ FILE_END_OF_FILE_INFO :: struct {
EndOfFile: LARGE_INTEGER,
}
+FILE_NOTIFY_INFORMATION :: struct {
+ next_entry_offset: DWORD,
+ action: DWORD,
+ file_name_length: DWORD,
+ file_name: [1]WCHAR,
+}
+
REPARSE_DATA_BUFFER :: struct {
ReparseTag: c_uint,
ReparseDataLength: c_ushort,
@@ -554,7 +1921,41 @@ PGUID :: ^GUID
PCGUID :: ^GUID
LPGUID :: ^GUID
LPCGUID :: ^GUID
+REFIID :: ^GUID
+REFGUID :: GUID
+IID :: GUID
+CLSID :: GUID
+REFCLSID :: ^CLSID
+
+CLSCTX_INPROC_SERVER :: 0x1
+CLSCTX_INPROC_HANDLER :: 0x2
+CLSCTX_LOCAL_SERVER :: 0x4
+CLSCTX_INPROC_SERVER16 :: 0x8
+CLSCTX_REMOTE_SERVER :: 0x10
+CLSCTX_INPROC_HANDLER16 :: 0x20
+CLSCTX_RESERVED1 :: 0x40
+CLSCTX_RESERVED2 :: 0x80
+CLSCTX_RESERVED3 :: 0x100
+CLSCTX_RESERVED4 :: 0x200
+CLSCTX_NO_CODE_DOWNLOAD :: 0x400
+CLSCTX_RESERVED5 :: 0x800
+CLSCTX_NO_CUSTOM_MARSHAL :: 0x1000
+CLSCTX_ENABLE_CODE_DOWNLOAD :: 0x2000
+CLSCTX_NO_FAILURE_LOG :: 0x4000
+CLSCTX_DISABLE_AAA :: 0x8000
+CLSCTX_ENABLE_AAA :: 0x10000
+CLSCTX_FROM_DEFAULT_CONTEXT :: 0x20000
+CLSCTX_ACTIVATE_X86_SERVER :: 0x40000
+CLSCTX_ACTIVATE_32_BIT_SERVER :: CLSCTX_ACTIVATE_X86_SERVER
+CLSCTX_ACTIVATE_64_BIT_SERVER :: 0x80000
+CLSCTX_ENABLE_CLOAKING :: 0x100000
+CLSCTX_APPCONTAINER :: 0x400000
+CLSCTX_ACTIVATE_AAA_AS_IU :: 0x800000
+CLSCTX_RESERVED6 :: 0x1000000
+CLSCTX_ACTIVATE_ARM32_SERVER :: 0x2000000
+CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION :: 0x4000000
+CLSCTX_PS_DLL :: 0x80000000
WSAPROTOCOLCHAIN :: struct {
ChainLen: c_int,
@@ -575,7 +1976,7 @@ PROCESS_INFORMATION :: struct {
}
// FYI: This is STARTUPINFOW, not STARTUPINFOA
-STARTUPINFO :: struct #packed {
+STARTUPINFO :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -619,6 +2020,12 @@ OVERLAPPED :: struct {
hEvent: HANDLE,
}
+LPOVERLAPPED_COMPLETION_ROUTINE :: #type proc "stdcall" (
+ dwErrorCode: DWORD,
+ dwNumberOfBytesTransfered: DWORD,
+ lpOverlapped: LPOVERLAPPED,
+)
+
ADDRESS_MODE :: enum c_int {
AddrMode1616,
AddrMode1632,
@@ -644,6 +2051,33 @@ ADDRINFOA :: struct {
ai_next: ^ADDRINFOA,
}
+PADDRINFOEXW :: ^ADDRINFOEXW
+LPADDRINFOEXW :: ^ADDRINFOEXW
+ADDRINFOEXW :: struct {
+ ai_flags: c_int,
+ ai_family: c_int,
+ ai_socktype: c_int,
+ ai_protocol: c_int,
+ ai_addrlen: size_t,
+ ai_canonname: wstring,
+ ai_addr: ^sockaddr,
+ ai_blob: rawptr,
+ ai_bloblen: size_t,
+ ai_provider: LPGUID,
+ ai_next: ^ADDRINFOEXW,
+}
+
+LPLOOKUPSERVICE_COMPLETION_ROUTINE :: #type proc "stdcall" (
+ dwErrorCode: DWORD,
+ dwNumberOfBytesTransfered: DWORD,
+ lpOverlapped: LPOVERLAPPED,
+)
+
+sockaddr :: struct {
+ sa_family: USHORT,
+ sa_data: [14]byte,
+}
+
sockaddr_in :: struct {
sin_family: ADDRESS_FAMILY,
sin_port: USHORT,
@@ -781,17 +2215,17 @@ SYSTEM_INFO :: struct {
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
OSVERSIONINFOEXW :: struct {
- dwOSVersionInfoSize: ULONG,
- dwMajorVersion: ULONG,
- dwMinorVersion: ULONG,
- dwBuildNumber: ULONG,
- dwPlatformId: ULONG,
- szCSDVersion: [128]WCHAR,
- wServicePackMajor: USHORT,
- wServicePackMinor: USHORT,
- wSuiteMask: USHORT,
- wProductType: UCHAR,
- wReserved: UCHAR,
+ dwOSVersionInfoSize: ULONG,
+ dwMajorVersion: ULONG,
+ dwMinorVersion: ULONG,
+ dwBuildNumber: ULONG,
+ dwPlatformId: ULONG,
+ szCSDVersion: [128]WCHAR,
+ wServicePackMajor: USHORT,
+ wServicePackMinor: USHORT,
+ wSuiteMask: USHORT,
+ wProductType: UCHAR,
+ wReserved: UCHAR,
}
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
@@ -834,24 +2268,24 @@ PROFILEINFOW :: struct {
lpDefaultPath: LPWSTR,
lpServerName: LPWSTR,
lpPolicyPath: LPWSTR,
- hProfile: HANDLE,
+ hProfile: HANDLE,
}
// Used in LookupAccountNameW
SID_NAME_USE :: distinct DWORD
SID_TYPE :: enum SID_NAME_USE {
- User = 1,
- Group,
- Domain,
- Alias,
- WellKnownGroup,
- DeletedAccount,
- Invalid,
- Unknown,
- Computer,
- Label,
- LogonSession,
+ User = 1,
+ Group,
+ Domain,
+ Alias,
+ WellKnownGroup,
+ DeletedAccount,
+ Invalid,
+ Unknown,
+ Computer,
+ Label,
+ LogonSession,
}
SECURITY_MAX_SID_SIZE :: 68
@@ -866,7 +2300,7 @@ SID :: struct #packed {
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE)
SID_IDENTIFIER_AUTHORITY :: struct #packed {
- Value: [6]u8,
+ Value: [6]u8,
}
// For NetAPI32
@@ -898,11 +2332,11 @@ USER_INFO_FLAG :: enum DWORD {
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
- Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
- Normal_Account = 9, // 1 << 9: 0x0200,
- InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
- Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
- Server_Trust_Account = 13, // 1 << 13: 0x2000,
+ Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
+ Normal_Account = 9, // 1 << 9: 0x0200,
+ InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
+ Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
+ Server_Trust_Account = 13, // 1 << 13: 0x2000,
}
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG]
@@ -1249,4 +2683,585 @@ SYSTEMTIME :: struct {
minute: WORD,
second: WORD,
milliseconds: WORD,
-}
\ No newline at end of file
+}
+
+
+@(private="file")
+IMAGE_DOS_HEADER :: struct {
+ e_magic: WORD,
+ e_cblp: WORD,
+ e_cp: WORD,
+ e_crlc: WORD,
+ e_cparhdr: WORD,
+ e_minalloc: WORD,
+ e_maxalloc: WORD,
+ e_ss: WORD,
+ e_sp: WORD,
+ e_csum: WORD,
+ e_ip: WORD,
+ e_cs: WORD,
+ e_lfarlc: WORD,
+ e_ovno: WORD,
+ e_res_0: WORD,
+ e_res_1: WORD,
+ e_res_2: WORD,
+ e_res_3: WORD,
+ e_oemid: WORD,
+ e_oeminfo: WORD,
+ e_res2_0: WORD,
+ e_res2_1: WORD,
+ e_res2_2: WORD,
+ e_res2_3: WORD,
+ e_res2_4: WORD,
+ e_res2_5: WORD,
+ e_res2_6: WORD,
+ e_res2_7: WORD,
+ e_res2_8: WORD,
+ e_res2_9: WORD,
+ e_lfanew: DWORD,
+}
+
+IMAGE_DATA_DIRECTORY :: struct {
+ VirtualAddress: DWORD,
+ Size: DWORD,
+}
+
+IMAGE_FILE_HEADER :: struct {
+ Machine: WORD,
+ NumberOfSections: WORD,
+ TimeDateStamp: DWORD,
+ PointerToSymbolTable: DWORD,
+ NumberOfSymbols: DWORD,
+ SizeOfOptionalHeader: WORD,
+ Characteristics: WORD,
+}
+
+IMAGE_OPTIONAL_HEADER64 :: struct {
+ Magic: WORD,
+ MajorLinkerVersion: BYTE,
+ MinorLinkerVersion: BYTE,
+ SizeOfCode: DWORD,
+ SizeOfInitializedData: DWORD,
+ SizeOfUninitializedData: DWORD,
+ AddressOfEntryPoint: DWORD,
+ BaseOfCode: DWORD,
+ ImageBase: QWORD,
+ SectionAlignment: DWORD,
+ FileAlignment: DWORD,
+ MajorOperatingSystemVersion: WORD,
+ MinorOperatingSystemVersion: WORD,
+ MajorImageVersion: WORD,
+ MinorImageVersion: WORD,
+ MajorSubsystemVersion: WORD,
+ MinorSubsystemVersion: WORD,
+ Win32VersionValue: DWORD,
+ SizeOfImage: DWORD,
+ SizeOfHeaders: DWORD,
+ CheckSum: DWORD,
+ Subsystem: WORD,
+ DllCharacteristics: WORD,
+ SizeOfStackReserve: QWORD,
+ SizeOfStackCommit: QWORD,
+ SizeOfHeapReserve: QWORD,
+ SizeOfHeapCommit: QWORD,
+ LoaderFlags: DWORD,
+ NumberOfRvaAndSizes: DWORD,
+ ExportTable: IMAGE_DATA_DIRECTORY,
+ ImportTable: IMAGE_DATA_DIRECTORY,
+ ResourceTable: IMAGE_DATA_DIRECTORY,
+ ExceptionTable: IMAGE_DATA_DIRECTORY,
+ CertificateTable: IMAGE_DATA_DIRECTORY,
+ BaseRelocationTable: IMAGE_DATA_DIRECTORY,
+ Debug: IMAGE_DATA_DIRECTORY,
+ Architecture: IMAGE_DATA_DIRECTORY,
+ GlobalPtr: IMAGE_DATA_DIRECTORY,
+ TLSTable: IMAGE_DATA_DIRECTORY,
+ LoadConfigTable: IMAGE_DATA_DIRECTORY,
+ BoundImport: IMAGE_DATA_DIRECTORY,
+ IAT: IMAGE_DATA_DIRECTORY,
+ DelayImportDescriptor: IMAGE_DATA_DIRECTORY,
+ CLRRuntimeHeader: IMAGE_DATA_DIRECTORY,
+ Reserved: IMAGE_DATA_DIRECTORY,
+}
+
+IMAGE_NT_HEADERS64 :: struct {
+ Signature: DWORD,
+ FileHeader: IMAGE_FILE_HEADER,
+ OptionalHeader: IMAGE_OPTIONAL_HEADER64,
+}
+
+IMAGE_EXPORT_DIRECTORY :: struct {
+ Characteristics: DWORD,
+ TimeDateStamp: DWORD,
+ MajorVersion: WORD,
+ MinorVersion: WORD,
+ Name: DWORD,
+ Base: DWORD,
+ NumberOfFunctions: DWORD,
+ NumberOfNames: DWORD,
+ AddressOfFunctions: DWORD, // RVA from base of image
+ AddressOfNames: DWORD, // RVA from base of image
+ AddressOfNameOrdinals: DWORD, // RVA from base of image
+}
+
+SICHINTF :: DWORD
+SHCONTF :: DWORD
+SFGAOF :: ULONG
+FILEOPENDIALOGOPTIONS :: DWORD
+REFPROPERTYKEY :: ^PROPERTYKEY
+REFPROPVARIANT :: ^PROPVARIANT
+
+SIGDN :: enum c_int {
+ NORMALDISPLAY = 0,
+ PARENTRELATIVEPARSING = -2147385343, // 0x80018001
+ DESKTOPABSOLUTEPARSING = -2147319808, // 0x80028000
+ PARENTRELATIVEEDITING = -2147282943, // 0x80031001
+ DESKTOPABSOLUTEEDITING = -2147172352, // 0x8004c000
+ FILESYSPATH = -2147123200, // 0x80058000
+ URL = -2147057664, // 0x80068000
+ PARENTRELATIVEFORADDRESSBAR = -2146975743, // 0x8007c001
+ PARENTRELATIVE = -2146959359, // 0x80080001
+ PARENTRELATIVEFORUI = -2146877439, // 0x80094001
+}
+
+SIATTRIBFLAGS :: enum c_int {
+ AND = 0x1,
+ OR = 0x2,
+ APPCOMPAT = 0x3,
+ MASK = 0x3,
+ ALLITEMS = 0x4000,
+}
+
+FDAP :: enum c_int {
+ BOTTOM = 0,
+ TOP = 1,
+}
+
+FDE_SHAREVIOLATION_RESPONSE :: enum c_int {
+ DEFAULT = 0,
+ ACCEPT = 1,
+ REFUSE = 2,
+}
+
+GETPROPERTYSTOREFLAGS :: enum c_int {
+ DEFAULT = 0,
+ HANDLERPROPERTIESONLY = 0x1,
+ READWRITE = 0x2,
+ TEMPORARY = 0x4,
+ FASTPROPERTIESONLY = 0x8,
+ OPENSLOWITEM = 0x10,
+ DELAYCREATION = 0x20,
+ BESTEFFORT = 0x40,
+ NO_OPLOCK = 0x80,
+ PREFERQUERYPROPERTIES = 0x100,
+ EXTRINSICPROPERTIES = 0x200,
+ EXTRINSICPROPERTIESONLY = 0x400,
+ VOLATILEPROPERTIES = 0x800,
+ VOLATILEPROPERTIESONLY = 0x1000,
+ MASK_VALID = 0x1fff,
+}
+
+PROPERTYKEY :: struct {
+ fmtid: GUID,
+ pid: DWORD,
+}
+
+BIND_OPTS :: struct {
+ cbStruct: DWORD,
+ grfFlags: DWORD,
+ grfMode: DWORD,
+ dwTickCountDeadline: DWORD,
+}
+
+STATSTG :: struct {
+ pwcsName: LPOLESTR,
+ type: DWORD,
+ cbSize: ULARGE_INTEGER,
+ mtime: FILETIME,
+ ctime: FILETIME,
+ atime: FILETIME,
+ grfMode: DWORD,
+ grfLocksSupported: DWORD,
+ clsid: CLSID,
+ grfStateBits: DWORD,
+ reserved: DWORD,
+}
+
+COMDLG_FILTERSPEC :: struct {
+ pszName, pszSpec: LPCWSTR,
+}
+
+DECIMAL :: struct {
+ wReserved: USHORT,
+ _: struct #raw_union {
+ _: struct {
+ scale, sign: BYTE,
+ },
+ signscale: USHORT,
+ },
+ Hi32: ULONG,
+ _: struct #raw_union {
+ _: struct {
+ Lo32, Mid32: ULONG,
+ },
+ Lo64: ULONGLONG,
+ },
+}
+
+// NOTE(ftphikari): bigger definition of this struct is ignored
+PROPVARIANT :: struct {
+ decVal: DECIMAL,
+}
+
+SICHINT_DISPLAY :: 0
+SICHINT_ALLFIELDS :: -2147483648 // 0x80000000
+SICHINT_CANONICAL :: 0x10000000
+SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL :: 0x20000000
+
+FOS_OVERWRITEPROMPT :: 0x2
+FOS_STRICTFILETYPES :: 0x4
+FOS_NOCHANGEDIR :: 0x8
+FOS_PICKFOLDERS :: 0x20
+FOS_FORCEFILESYSTEM :: 0x40
+FOS_ALLNONSTORAGEITEMS :: 0x80
+FOS_NOVALIDATE :: 0x100
+FOS_ALLOWMULTISELECT :: 0x200
+FOS_PATHMUSTEXIST :: 0x800
+FOS_FILEMUSTEXIST :: 0x1000
+FOS_CREATEPROMPT :: 0x2000
+FOS_SHAREAWARE :: 0x4000
+FOS_NOREADONLYRETURN :: 0x8000
+FOS_NOTESTFILECREATE :: 0x10000
+FOS_HIDEMRUPLACES :: 0x20000
+FOS_HIDEPINNEDPLACES :: 0x40000
+FOS_NODEREFERENCELINKS :: 0x100000
+FOS_OKBUTTONNEEDSINTERACTION :: 0x200000
+FOS_DONTADDTORECENT :: 0x2000000
+FOS_FORCESHOWHIDDEN :: 0x10000000
+FOS_DEFAULTNOMINIMODE :: 0x20000000
+FOS_FORCEPREVIEWPANEON :: 0x40000000
+FOS_SUPPORTSTREAMABLEITEMS :: 0x80000000
+
+SHCONTF_CHECKING_FOR_CHILDREN :: 0x10
+SHCONTF_FOLDERS :: 0x20
+SHCONTF_NONFOLDERS :: 0x40
+SHCONTF_INCLUDEHIDDEN :: 0x80
+SHCONTF_INIT_ON_FIRST_NEXT :: 0x100
+SHCONTF_NETPRINTERSRCH :: 0x200
+SHCONTF_SHAREABLE :: 0x400
+SHCONTF_STORAGE :: 0x800
+SHCONTF_NAVIGATION_ENUM :: 0x1000
+SHCONTF_FASTITEMS :: 0x2000
+SHCONTF_FLATLIST :: 0x4000
+SHCONTF_ENABLE_ASYNC :: 0x8000
+SHCONTF_INCLUDESUPERHIDDEN :: 0x10000
+
+CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}}
+CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}}
+
+IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}}
+IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}}
+IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}}
+
+IModalWindow :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IModalWindowVtbl,
+}
+IModalWindowVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Show: proc "stdcall" (this: ^IModalWindow, hwndOwner: HWND) -> HRESULT,
+}
+
+ISequentialStream :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^ISequentialStreamVtbl,
+}
+ISequentialStreamVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Read: proc "stdcall" (this: ^ISequentialStream, pv: rawptr, cb: ULONG, pcbRead: ^ULONG) -> HRESULT,
+ Write: proc "stdcall" (this: ^ISequentialStream, pv: rawptr, cb: ULONG, pcbWritten: ^ULONG) -> HRESULT,
+}
+
+IStream :: struct #raw_union {
+ #subtype ISequentialStream: ISequentialStream,
+ using Vtbl: ^IStreamVtbl,
+}
+IStreamVtbl :: struct {
+ using ISequentialStreamVtbl: ISequentialStreamVtbl,
+ Seek: proc "stdcall" (this: ^IStream, dlibMove: LARGE_INTEGER, dwOrigin: DWORD, plibNewPosition: ^ULARGE_INTEGER) -> HRESULT,
+ SetSize: proc "stdcall" (this: ^IStream, libNewSize: ULARGE_INTEGER) -> HRESULT,
+ CopyTo: proc "stdcall" (this: ^IStream, pstm: ^IStream, cb: ULARGE_INTEGER, pcbRead: ^ULARGE_INTEGER, pcbWritten: ^ULARGE_INTEGER) -> HRESULT,
+ Commit: proc "stdcall" (this: ^IStream, grfCommitFlags: DWORD) -> HRESULT,
+ Revert: proc "stdcall" (this: ^IStream) -> HRESULT,
+ LockRegion: proc "stdcall" (this: ^IStream, libOffset: ULARGE_INTEGER, cb: ULARGE_INTEGER, dwLockType: DWORD) -> HRESULT,
+ UnlockRegion: proc "stdcall" (this: ^IStream, libOffset: ULARGE_INTEGER, cb: ULARGE_INTEGER, dwLockType: DWORD) -> HRESULT,
+ Stat: proc "stdcall" (this: ^IStream, pstatstg: ^STATSTG, grfStatFlag: DWORD) -> HRESULT,
+ Clone: proc "stdcall" (this: ^IStream, ppstm: ^^IStream) -> HRESULT,
+}
+
+IPersist :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IPersistVtbl,
+}
+IPersistVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ GetClassID: proc "stdcall" (this: ^IPersist, pClassID: ^CLSID) -> HRESULT,
+}
+
+IPersistStream :: struct #raw_union {
+ #subtype IPersist: IPersist,
+ using Vtbl: ^IPersistStreamVtbl,
+}
+IPersistStreamVtbl :: struct {
+ using IPersistVtbl: IPersistVtbl,
+ IsDirty: proc "stdcall" (this: ^IPersistStream) -> HRESULT,
+ Load: proc "stdcall" (this: ^IPersistStream, pStm: ^IStream) -> HRESULT,
+ Save: proc "stdcall" (this: ^IPersistStream, pStm: ^IStream, fClearDirty: BOOL) -> HRESULT,
+ GetSizeMax: proc "stdcall" (this: ^IPersistStream, pcbSize: ^ULARGE_INTEGER) -> HRESULT,
+}
+
+IMoniker :: struct #raw_union {
+ #subtype IPersistStream: IPersistStream,
+ using Vtbl: ^IMonikerVtbl,
+}
+IMonikerVtbl :: struct {
+ using IPersistStreamVtbl: IPersistStreamVtbl,
+ BindToObject: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, riidResult: REFIID, ppvResult: ^rawptr) -> HRESULT,
+ BindToStorage: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, riid: REFIID, ppvObj: ^rawptr) -> HRESULT,
+ Reduce: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, dwReduceHowFar: DWORD, ppmkToLeft: ^^IMoniker, ppmkReduced: ^^IMoniker) -> HRESULT,
+ ComposeWith: proc "stdcall" (this: ^IMoniker, pmkRight: ^IMoniker, fOnlyIfNotGeneric: BOOL, ppmkComposite: ^^IMoniker) -> HRESULT,
+ Enum: proc "stdcall" (this: ^IMoniker, fForward: BOOL, ppenumMoniker: ^^IEnumMoniker) -> HRESULT,
+ IsEqual: proc "stdcall" (this: ^IMoniker, pmkOtherMoniker: ^IMoniker) -> HRESULT,
+ Hash: proc "stdcall" (this: ^IMoniker, pdwHash: ^DWORD) -> HRESULT,
+ IsRunning: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pmkNewlyRunning: ^IMoniker) -> HRESULT,
+ GetTimeOfLastChange: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pFileTime: ^FILETIME) -> HRESULT,
+ Inverse: proc "stdcall" (this: ^IMoniker, ppmk: ^^IMoniker) -> HRESULT,
+ CommonPrefixWith: proc "stdcall" (this: ^IMoniker, pmkOther: ^IMoniker, ppmkPrefix: ^^IMoniker) -> HRESULT,
+ RelativePathTo: proc "stdcall" (this: ^IMoniker, pmkOther: ^IMoniker, ppmkRelPath: ^^IMoniker) -> HRESULT,
+ GetDisplayName: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, ppszDisplayName: ^LPOLESTR) -> HRESULT,
+ ParseDisplayName: proc "stdcall" (this: ^IMoniker, pbc: ^IBindCtx, pmkToLeft: ^IMoniker, pszDisplayName: LPOLESTR, pchEaten: ^ULONG, ppmkOut: ^^IMoniker) -> HRESULT,
+ IsSystemMoniker: proc "stdcall" (this: ^IMoniker, pdwMksys: ^DWORD) -> HRESULT,
+}
+
+IEnumMoniker :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IEnumMonikerVtbl,
+}
+IEnumMonikerVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Next: proc "stdcall" (this: ^IEnumMoniker, celt: ULONG, rgelt: ^^IMoniker, pceltFetched: ^ULONG) -> HRESULT,
+ Skip: proc "stdcall" (this: ^IEnumMoniker, celt: ULONG) -> HRESULT,
+ Reset: proc "stdcall" (this: ^IEnumMoniker) -> HRESULT,
+ Clone: proc "stdcall" (this: ^IEnumMoniker, ppenum: ^^IEnumMoniker) -> HRESULT,
+}
+
+IRunningObjectTable :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IRunningObjectTableVtbl,
+}
+IRunningObjectTableVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Register: proc "stdcall" (this: ^IRunningObjectTable, grfFlags: DWORD, punkObject: ^IUnknown, pmkObjectName: ^IMoniker, pdwRegister: ^DWORD) -> HRESULT,
+ Revoke: proc "stdcall" (this: ^IRunningObjectTable, dwRegister: DWORD) -> HRESULT,
+ IsRunning: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker) -> HRESULT,
+ GetObject: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker, ppunkObject: ^^IUnknown) -> HRESULT,
+ NoteChangeTime: proc "stdcall" (this: ^IRunningObjectTable, dwRegister: DWORD, pfiletime: ^FILETIME) -> HRESULT,
+ GetTimeOfLastChange: proc "stdcall" (this: ^IRunningObjectTable, pmkObjectName: ^IMoniker, pfiletime: ^FILETIME) -> HRESULT,
+ EnumRunning: proc "stdcall" (this: ^IRunningObjectTable, ppenumMoniker: ^^IEnumMoniker) -> HRESULT,
+}
+
+IEnumString :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IEnumStringVtbl,
+}
+IEnumStringVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Next: proc "stdcall" (this: ^IEnumString, celt: ULONG, rgelt: ^LPOLESTR, pceltFetched: ^ULONG) -> HRESULT,
+ Skip: proc "stdcall" (this: ^IEnumString, celt: ULONG) -> HRESULT,
+ Reset: proc "stdcall" (this: ^IEnumString) -> HRESULT,
+ Clone: proc "stdcall" (this: ^IEnumString, ppenum: ^^IEnumString) -> HRESULT,
+}
+
+IBindCtx :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IBindCtxVtbl,
+}
+IBindCtxVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ RegisterObjectBound: proc "stdcall" (this: ^IBindCtx, punk: ^IUnknown) -> HRESULT,
+ RevokeObjectBound: proc "stdcall" (this: ^IBindCtx, punk: ^IUnknown) -> HRESULT,
+ ReleaseBoundObjects: proc "stdcall" (this: ^IBindCtx) -> HRESULT,
+ SetBindOptions: proc "stdcall" (this: ^IBindCtx, pbindopts: ^BIND_OPTS) -> HRESULT,
+ GetBindOptions: proc "stdcall" (this: ^IBindCtx, pbindopts: ^BIND_OPTS) -> HRESULT,
+ GetRunningObjectTable: proc "stdcall" (this: ^IBindCtx, pprot: ^^IRunningObjectTable) -> HRESULT,
+ RegisterObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR, punk: ^IUnknown) -> HRESULT,
+ GetObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR, ppunk: ^^IUnknown) -> HRESULT,
+ EnumObjectParam: proc "stdcall" (this: ^IBindCtx, ppenum: ^^IEnumString) -> HRESULT,
+ RevokeObjectParam: proc "stdcall" (this: ^IBindCtx, pszKey: LPOLESTR) -> HRESULT,
+}
+
+IEnumShellItems :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IEnumShellItemsVtbl,
+}
+IEnumShellItemsVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ Next: proc "stdcall" (this: ^IEnumShellItems, celt: ULONG, rgelt: ^^IShellItem, pceltFetched: ^ULONG) -> HRESULT,
+ Skip: proc "stdcall" (this: ^IEnumShellItems, celt: ULONG) -> HRESULT,
+ Reset: proc "stdcall" (this: ^IEnumShellItems) -> HRESULT,
+ Clone: proc "stdcall" (this: ^IEnumShellItems, ppenum: ^^IEnumShellItems) -> HRESULT,
+}
+
+IShellItem :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IShellItemVtbl,
+}
+IShellItemVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ BindToHandler: proc "stdcall" (this: ^IShellItem, pbc: ^IBindCtx, bhid: REFGUID, riid: REFIID, ppv: ^rawptr) -> HRESULT,
+ GetParent: proc "stdcall" (this: ^IShellItem, ppsiFolder: ^^IShellItem) -> HRESULT,
+ GetDisplayName: proc "stdcall" (this: ^IShellItem, sigdnName: SIGDN, ppszName: ^LPWSTR) -> HRESULT,
+ GetAttributes: proc "stdcall" (this: ^IShellItem, sfgaoMask: SFGAOF, psfgaoAttribs: ^SFGAOF) -> HRESULT,
+ Compare: proc "stdcall" (this: ^IShellItem, psi: ^IShellItem, hint: SICHINTF, piOrder: ^c_int) -> HRESULT,
+}
+
+IShellItemArray :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IShellItemArrayVtbl,
+}
+IShellItemArrayVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ BindToHandler: proc "stdcall" (this: ^IShellItemArray, pbc: ^IBindCtx, bhid: REFGUID, riid: REFIID, ppvOut: ^rawptr) -> HRESULT,
+ GetPropertyStore: proc "stdcall" (this: ^IShellItemArray, flags: GETPROPERTYSTOREFLAGS, riid: REFIID, ppv: ^rawptr) -> HRESULT,
+ GetPropertyDescriptionList: proc "stdcall" (this: ^IShellItemArray, keyType: REFPROPERTYKEY, riid: REFIID, ppv: ^rawptr) -> HRESULT,
+ GetAttributes: proc "stdcall" (this: ^IShellItemArray, AttribFlags: SIATTRIBFLAGS, sfgaoMask: SFGAOF, psfgaoAttribs: ^SFGAOF) -> HRESULT,
+ GetCount: proc "stdcall" (this: ^IShellItemArray, pdwNumItems: ^DWORD) -> HRESULT,
+ GetItemAt: proc "stdcall" (this: ^IShellItemArray, dwIndex: DWORD, ppsi: ^^IShellItem) -> HRESULT,
+ EnumItems: proc "stdcall" (this: ^IShellItemArray, ppenumShellItems: ^^IEnumShellItems) -> HRESULT,
+}
+
+IFileDialogEvents :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IFileDialogEventsVtbl,
+}
+IFileDialogEventsVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ OnFileOk: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT,
+ OnFolderChanging: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psiFolder: ^IShellItem) -> HRESULT,
+ OnFolderChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT,
+ OnSelectionChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT,
+ OnShareViolation: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psi: ^IShellItem, pResponse: ^FDE_SHAREVIOLATION_RESPONSE) -> HRESULT,
+ OnTypeChange: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog) -> HRESULT,
+ OnOverwrite: proc "stdcall" (this: ^IFileDialogEvents, pfd: ^IFileDialog, psi: ^IShellItem, pResponse: ^FDE_SHAREVIOLATION_RESPONSE) -> HRESULT,
+}
+
+IShellItemFilter :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IShellItemFilterVtbl,
+}
+IShellItemFilterVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ IncludeItem: proc "stdcall" (this: ^IShellItemFilter, psi: ^IShellItem) -> HRESULT,
+ GetEnumFlagsForItem: proc "stdcall" (this: ^IShellItemFilter, psi: ^IShellItem, pgrfFlags: ^SHCONTF) -> HRESULT,
+}
+
+IFileDialog :: struct #raw_union {
+ #subtype IModalWindow: IModalWindow,
+ using Vtbl: ^IFileDialogVtbl,
+}
+IFileDialogVtbl :: struct {
+ using IModalWindowVtbl: IModalWindowVtbl,
+ SetFileTypes: proc "stdcall" (this: ^IFileDialog, cFileTypes: UINT, rgFilterSpec: ^COMDLG_FILTERSPEC) -> HRESULT,
+ SetFileTypeIndex: proc "stdcall" (this: ^IFileDialog, iFileType: UINT) -> HRESULT,
+ GetFileTypeIndex: proc "stdcall" (this: ^IFileDialog, piFileType: ^UINT) -> HRESULT,
+ Advise: proc "stdcall" (this: ^IFileDialog, pfde: ^IFileDialogEvents, pdwCookie: ^DWORD) -> HRESULT,
+ Unadvise: proc "stdcall" (this: ^IFileDialog, dwCookie: DWORD) -> HRESULT,
+ SetOptions: proc "stdcall" (this: ^IFileDialog, fos: FILEOPENDIALOGOPTIONS) -> HRESULT,
+ GetOptions: proc "stdcall" (this: ^IFileDialog, pfos: ^FILEOPENDIALOGOPTIONS) -> HRESULT,
+ SetDefaultFolder: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem) -> HRESULT,
+ SetFolder: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem) -> HRESULT,
+ GetFolder: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT,
+ GetCurrentSelection: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT,
+ SetFileName: proc "stdcall" (this: ^IFileDialog, pszName: LPCWSTR) -> HRESULT,
+ GetFileName: proc "stdcall" (this: ^IFileDialog, pszName: ^LPCWSTR) -> HRESULT,
+ SetTitle: proc "stdcall" (this: ^IFileDialog, pszTitle: LPCWSTR) -> HRESULT,
+ SetOkButtonLabel: proc "stdcall" (this: ^IFileDialog, pszText: LPCWSTR) -> HRESULT,
+ SetFileNameLabel: proc "stdcall" (this: ^IFileDialog, pszLabel: LPCWSTR) -> HRESULT,
+ GetResult: proc "stdcall" (this: ^IFileDialog, ppsi: ^^IShellItem) -> HRESULT,
+ AddPlace: proc "stdcall" (this: ^IFileDialog, psi: ^IShellItem, fdap: FDAP) -> HRESULT,
+ SetDefaultExtension: proc "stdcall" (this: ^IFileDialog, pszDefaultExtension: LPCWSTR) -> HRESULT,
+ Close: proc "stdcall" (this: ^IFileDialog, hr: HRESULT) -> HRESULT,
+ SetClientGuid: proc "stdcall" (this: ^IFileDialog, guid: REFGUID) -> HRESULT,
+ ClearClientData: proc "stdcall" (this: ^IFileDialog) -> HRESULT,
+ SetFilter: proc "stdcall" (this: ^IFileDialog, pFilter: ^IShellItemFilter) -> HRESULT,
+}
+
+IFileOpenDialog :: struct #raw_union {
+ #subtype IFileDialog: IFileDialog,
+ using Vtbl: ^IFileOpenDialogVtbl,
+}
+IFileOpenDialogVtbl :: struct {
+ using IFileDialogVtbl: IFileDialogVtbl,
+ GetResults: proc "stdcall" (this: ^IFileOpenDialog, ppenum: ^^IShellItemArray) -> HRESULT,
+ GetSelectedItems: proc "stdcall" (this: ^IFileOpenDialog, ppsai: ^^IShellItemArray) -> HRESULT,
+}
+
+IPropertyStore :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IPropertyStoreVtbl,
+}
+IPropertyStoreVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ GetCount: proc "stdcall" (this: ^IPropertyStore, cProps: ^DWORD) -> HRESULT,
+ GetAt: proc "stdcall" (this: ^IPropertyStore, iProp: DWORD, pkey: ^PROPERTYKEY) -> HRESULT,
+ GetValue: proc "stdcall" (this: ^IPropertyStore, key: REFPROPERTYKEY, pv: ^PROPVARIANT) -> HRESULT,
+ SetValue: proc "stdcall" (this: ^IPropertyStore, key: REFPROPERTYKEY, propvar: REFPROPVARIANT) -> HRESULT,
+ Commit: proc "stdcall" (this: ^IPropertyStore) -> HRESULT,
+}
+
+IPropertyDescriptionList :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IPropertyDescriptionListVtbl,
+}
+IPropertyDescriptionListVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ GetCount: proc "stdcall" (this: ^IPropertyDescriptionList, pcElem: ^UINT) -> HRESULT,
+ GetAt: proc "stdcall" (this: ^IPropertyDescriptionList, iElem: UINT, riid: REFIID, ppv: ^rawptr) -> HRESULT,
+}
+
+IFileOperationProgressSink :: struct #raw_union {
+ #subtype IUnknown: IUnknown,
+ using Vtbl: ^IFileOperationProgressSinkVtbl,
+}
+IFileOperationProgressSinkVtbl :: struct {
+ using IUnknownVtbl: IUnknownVtbl,
+ StartOperations: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT,
+ FinishOperations: proc "stdcall" (this: ^IFileOperationProgressSink, hrResult: HRESULT) -> HRESULT,
+ PreRenameItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT,
+ PostRenameItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, pszNewName: LPCWSTR, hrRename: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT,
+ PreMoveItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT,
+ PostMoveItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, hrMove: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT,
+ PreCopyItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT,
+ PostCopyItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, hrMove: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT,
+ PreDeleteItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem) -> HRESULT,
+ PostDeleteItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiItem: ^IShellItem, hrDelete: HRESULT, psiNewlyCreated: ^IShellItem) -> HRESULT,
+ PreNewItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR) -> HRESULT,
+ PostNewItem: proc "stdcall" (this: ^IFileOperationProgressSink, dwFlags: DWORD, psiDestinationFolder: ^IShellItem, pszNewName: LPCWSTR, pszTemplateName: LPCWSTR, dwFileAttributes: DWORD, hrNew: HRESULT, psiNewItem: ^IShellItem) -> HRESULT,
+ UpdateProgress: proc "stdcall" (this: ^IFileOperationProgressSink, iWorkTotal: UINT, iWorkSoFar: UINT) -> HRESULT,
+ ResetTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT,
+ PauseTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT,
+ ResumeTimer: proc "stdcall" (this: ^IFileOperationProgressSink) -> HRESULT,
+}
+
+IFileSaveDialog :: struct #raw_union {
+ #subtype IFileDialog: IFileDialog,
+ using Vtbl: ^IFileSaveDialogVtbl,
+}
+IFileSaveDialogVtbl :: struct {
+ using IFileDialogVtbl: IFileDialogVtbl,
+ SetSaveAsItem: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem) -> HRESULT,
+ SetProperties: proc "stdcall" (this: ^IFileSaveDialog, pStore: ^IPropertyStore) -> HRESULT,
+ SetCollectedProperties: proc "stdcall" (this: ^IFileSaveDialog, pList: ^IPropertyDescriptionList, fAppendDefault: BOOL) -> HRESULT,
+ GetProperties: proc "stdcall" (this: ^IFileSaveDialog, ppStore: ^^IPropertyStore) -> HRESULT,
+ ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
+}
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
new file mode 100644
index 000000000..47de354b6
--- /dev/null
+++ b/core/sys/windows/user32.odin
@@ -0,0 +1,248 @@
+// +build windows
+package sys_windows
+
+foreign import user32 "system:User32.lib"
+
+@(default_calling_convention="stdcall")
+foreign user32 {
+ GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
+ GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
+
+ GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+ SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
+
+ GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
+ SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
+
+ GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
+
+ RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
+ RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
+
+ CreateWindowExW :: proc(
+ dwExStyle: DWORD,
+ lpClassName: LPCWSTR,
+ lpWindowName: LPCWSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+ ) -> HWND ---
+
+ DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
+
+ ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
+ BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
+ GetTopWindow :: proc(hWnd: HWND) -> HWND ---
+ SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
+ GetForegroundWindow :: proc() -> HWND ---
+ SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
+ GetActiveWindow :: proc() -> HWND ---
+
+ GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
+
+ TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
+ DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
+
+ PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
+ PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
+
+ PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+
+ PostQuitMessage :: proc(nExitCode: c_int) ---
+
+ GetQueueStatus :: proc(flags: UINT) -> DWORD ---
+
+ DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND ---
+ FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND ---
+ FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND ---
+ FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND ---
+
+ LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
+ LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
+ LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
+ LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
+
+ GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
+ ScreenToClient :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
+ SetWindowPos :: proc(
+ hWnd: HWND,
+ hWndInsertAfter: HWND,
+ X: c_int,
+ Y: c_int,
+ cx: c_int,
+ cy: c_int,
+ uFlags: UINT,
+ ) -> BOOL ---
+ MoveWindow :: proc(hWnd: HWND, X, Y, hWidth, hHeight: c_int, bRepaint: BOOL) -> BOOL ---
+ GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
+ AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
+ AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
+
+ SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
+
+ GetWindowDC :: proc(hWnd: HWND) -> HDC ---
+ GetDC :: proc(hWnd: HWND) -> HDC ---
+ ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
+
+ 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 ---
+
+ BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC ---
+ EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL ---
+
+ GetCapture :: proc() -> HWND ---
+ SetCapture :: proc(hWnd: HWND) -> HWND ---
+ ReleaseCapture :: proc() -> BOOL ---
+ TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
+
+ GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
+ GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+
+ MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
+
+ SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
+ UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL ---
+ CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
+ KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
+
+ // MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
+ MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
+ // MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+ MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+
+ ClipCursor :: proc(lpRect: LPRECT) -> BOOL ---
+ GetCursorPos :: proc(lpPoint: LPPOINT) -> BOOL ---
+ SetCursorPos :: proc(X: c_int, Y: c_int) -> BOOL ---
+ SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR ---
+
+ EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL ---
+
+ BroadcastSystemMessageW :: proc(
+ flags: DWORD,
+ lpInfo: LPDWORD,
+ Msg: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ ) -> c_long ---
+
+ BroadcastSystemMessageExW :: proc(
+ flags: DWORD,
+ lpInfo: LPDWORD,
+ Msg: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ pbsmInfo: PBSMINFO,
+ ) -> c_long ---
+
+ SendMessageTimeoutW :: proc(
+ hWnd: HWND,
+ Msg: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ fuFlags: UINT,
+ uTimeout: UINT,
+ lpdwResult: PDWORD_PTR,
+ ) -> LRESULT ---
+
+ GetSysColor :: proc(nIndex: c_int) -> DWORD ---
+ GetSysColorBrush :: proc(nIndex: c_int) -> HBRUSH ---
+ SetSysColors :: proc(cElements: c_int, lpaElements: ^INT, lpaRgbValues: ^COLORREF) -> BOOL ---
+ MessageBeep :: proc(uType: UINT) -> BOOL ---
+
+ IsDialogMessageW :: proc(hDlg: HWND, lpMsg: LPMSG) -> BOOL ---
+ GetWindowTextLengthW :: proc(hWnd: HWND) -> c_int ---
+ GetWindowTextW :: proc(hWnd: HWND, lpString: LPWSTR, nMaxCount: c_int) -> c_int ---
+ SetWindowTextW :: proc(hWnd: HWND, lpString: LPCWSTR) -> BOOL ---
+ CallWindowProcW :: proc(lpPrevWndFunc: WNDPROC, hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ EnableWindow :: proc(hWnd: HWND, bEnable: BOOL) -> BOOL ---
+}
+
+CreateWindowW :: #force_inline proc "stdcall" (
+ lpClassName: LPCTSTR,
+ lpWindowName: LPCTSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+) -> HWND {
+ return CreateWindowExW(
+ 0,
+ lpClassName,
+ lpWindowName,
+ dwStyle,
+ X,
+ Y,
+ nWidth,
+ nHeight,
+ hWndParent,
+ hMenu,
+ hInstance,
+ lpParam,
+ )
+}
+
+when ODIN_ARCH == .amd64 {
+ @(default_calling_convention="stdcall")
+ foreign user32 {
+ GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
+ SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+
+ GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
+ SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
+ }
+} else when ODIN_ARCH == .i386 {
+ GetClassLongPtrW :: GetClassLongW
+ SetClassLongPtrW :: SetClassLongW
+
+ GetWindowLongPtrW :: GetWindowLongW
+ SetWindowLongPtrW :: GetWindowLongW
+}
+
+GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
+ return c_int(wParam) & 0xFFF0
+}
+
+GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+ return cast(c_short)HIWORD(cast(DWORD)wParam)
+}
+
+GET_KEYSTATE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+ return LOWORD(cast(DWORD)wParam)
+}
+
+GET_NCHITTEST_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+ return cast(c_short)LOWORD(cast(DWORD)wParam)
+}
+
+GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+ return HIWORD(cast(DWORD)wParam)
+}
+
+MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR {
+ return cast(LPWSTR)uintptr(WORD(i))
+}
diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin
index efb37dbc0..2bdd72ed2 100644
--- a/core/sys/windows/util.odin
+++ b/core/sys/windows/util.odin
@@ -1,8 +1,10 @@
// +build windows
package sys_windows
-import "core:strings"
-import "core:sys/win32"
+import "core:runtime"
+import "core:intrinsics"
+
+L :: intrinsics.constant_utf16_cstring
LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
return WORD(x & 0xffff)
@@ -12,6 +14,18 @@ HIWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
return WORD(x >> 16)
}
+GET_X_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
+ return cast(c_int)cast(c_short)LOWORD(cast(DWORD)lp)
+}
+
+GET_Y_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
+ return cast(c_int)cast(c_short)HIWORD(cast(DWORD)lp)
+}
+
+MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD {
+ return x << 8 | y
+}
+
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
if len(s) < 1 {
return nil
@@ -45,14 +59,16 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
return nil
}
-wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> string {
+wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
+ context.allocator = allocator
+
if N <= 0 {
- return ""
+ return
}
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
if n == 0 {
- return ""
+ return
}
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
@@ -60,12 +76,12 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
- text := make([]byte, n+1 if N != -1 else n, allocator)
+ text := make([]byte, n+1 if N != -1 else n) or_return
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
if n1 == 0 {
delete(text, allocator)
- return ""
+ return
}
for i in 0.. string {
+utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
if len(s) == 0 {
- return ""
+ return "", nil
}
return wstring_to_utf8(raw_data(s), len(s), allocator)
}
@@ -88,6 +103,20 @@ utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
// AdvAPI32, NetAPI32 and UserENV helpers.
allowed_username :: proc(username: string) -> bool {
+ contains_any :: proc(s, chars: string) -> bool {
+ if chars == "" {
+ return false
+ }
+ for c in transmute([]byte)s {
+ for b in transmute([]byte)chars {
+ if c == b {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
/*
User account names are limited to 20 characters and group names are limited to 256 characters.
In addition, account names cannot be terminated by a period and they cannot include commas or any of the following printable characters:
@@ -108,7 +137,7 @@ allowed_username :: proc(username: string) -> bool {
return false
}
}
- if strings.contains_any(username, _DISALLOWED) {
+ if contains_any(username, _DISALLOWED) {
return false
}
@@ -204,7 +233,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
if !res {
return "", {}, false
}
- computer_name = utf16_to_utf8(cname_w, context.temp_allocator)
+ computer_name = utf16_to_utf8(cname_w, context.temp_allocator) or_else ""
ok = true
return
@@ -294,7 +323,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
if res == false {
return false, ""
}
- defer win32.local_free(sb)
+ defer LocalFree(sb)
pszProfilePath := make([]u16, 257, context.temp_allocator)
res2 := CreateProfile(
@@ -306,7 +335,7 @@ add_user_profile :: proc(username: string) -> (ok: bool, profile_path: string) {
if res2 != 0 {
return false, ""
}
- profile_path = wstring_to_utf8(&pszProfilePath[0], 257)
+ profile_path = wstring_to_utf8(&pszProfilePath[0], 257) or_else ""
return true, profile_path
}
@@ -324,7 +353,7 @@ delete_user_profile :: proc(username: string) -> (ok: bool) {
if res == false {
return false
}
- defer win32.local_free(sb)
+ defer LocalFree(sb)
res2 := DeleteProfileW(
sb,
@@ -439,20 +468,20 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
nil, // lpProcessAttributes,
nil, // lpThreadAttributes,
false, // bInheritHandles,
- 0, // creation flags
- nil, // environment,
- nil, // current directory: inherit from parent if nil
- &si,
- pi,
- ))
- if ok {
- if wait {
- WaitForSingleObject(pi.hProcess, INFINITE)
- CloseHandle(pi.hProcess)
- CloseHandle(pi.hThread)
- }
- return true
- } else {
- return false
- }
-}
\ No newline at end of file
+ 0, // creation flags
+ nil, // environment,
+ nil, // current directory: inherit from parent if nil
+ &si,
+ pi,
+ ))
+ if ok {
+ if wait {
+ WaitForSingleObject(pi.hProcess, INFINITE)
+ CloseHandle(pi.hProcess)
+ CloseHandle(pi.hThread)
+ }
+ return true
+ } else {
+ return false
+ }
+}
diff --git a/core/sys/windows/wgl.odin b/core/sys/windows/wgl.odin
new file mode 100644
index 000000000..689a41dea
--- /dev/null
+++ b/core/sys/windows/wgl.odin
@@ -0,0 +1,87 @@
+// +build windows
+package sys_windows
+
+import "core:c"
+
+foreign import "system:Opengl32.lib"
+
+CONTEXT_MAJOR_VERSION_ARB :: 0x2091
+CONTEXT_MINOR_VERSION_ARB :: 0x2092
+CONTEXT_FLAGS_ARB :: 0x2094
+CONTEXT_PROFILE_MASK_ARB :: 0x9126
+CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002
+CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001
+CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002
+
+HGLRC :: distinct HANDLE
+
+LPLAYERPLANEDESCRIPTOR :: ^LAYERPLANEDESCRIPTOR
+LAYERPLANEDESCRIPTOR :: struct {
+ nSize: WORD,
+ nVersion: WORD,
+ dwFlags: DWORD,
+ iPixelType: BYTE,
+ cColorBits: BYTE,
+ cRedBits: BYTE,
+ cRedShift: BYTE,
+ cGreenBits: BYTE,
+ cGreenShift: BYTE,
+ cBlueBits: BYTE,
+ cBlueShift: BYTE,
+ cAlphaBits: BYTE,
+ cAlphaShift: BYTE,
+ cAccumBits: BYTE,
+ cAccumRedBits: BYTE,
+ cAccumGreenBits: BYTE,
+ cAccumBlueBits: BYTE,
+ cAccumAlphaBits: BYTE,
+ cDepthBits: BYTE,
+ cStencilBits: BYTE,
+ cAuxBuffers: BYTE,
+ iLayerPlane: BYTE,
+ bReserved: BYTE,
+ crTransparent: COLORREF,
+}
+
+POINTFLOAT :: struct {x, y: f32}
+
+LPGLYPHMETRICSFLOAT :: ^GLYPHMETRICSFLOAT
+GLYPHMETRICSFLOAT :: struct {
+ gmfBlackBoxX: f32,
+ gmfBlackBoxY: f32,
+ gmfptGlyphOrigin: POINTFLOAT,
+ gmfCellIncX: f32,
+ gmfCellIncY: f32,
+}
+
+CreateContextAttribsARBType :: #type proc "c" (hdc: HDC, hShareContext: rawptr, attribList: [^]c.int) -> HGLRC
+ChoosePixelFormatARBType :: #type proc "c" (hdc: HDC, attribIList: [^]c.int, attribFList: [^]f32, maxFormats: DWORD, formats: [^]c.int, numFormats: [^]DWORD) -> BOOL
+SwapIntervalEXTType :: #type proc "c" (interval: c.int) -> bool
+GetExtensionsStringARBType :: #type proc "c" (HDC) -> cstring
+
+// Procedures
+ wglCreateContextAttribsARB: CreateContextAttribsARBType
+ wglChoosePixelFormatARB: ChoosePixelFormatARBType
+ wglSwapIntervalExt: SwapIntervalEXTType
+ wglGetExtensionsStringARB: GetExtensionsStringARBType
+
+
+@(default_calling_convention="stdcall")
+foreign Opengl32 {
+ wglCreateContext :: proc(hdc: HDC) -> HGLRC ---
+ wglMakeCurrent :: proc(hdc: HDC, HGLRC: HGLRC) -> BOOL ---
+ wglGetProcAddress :: proc(c_str: cstring) -> rawptr ---
+ wglDeleteContext :: proc(HGLRC: HGLRC) -> BOOL ---
+ wglCopyContext :: proc(src, dst: HGLRC, mask: UINT) -> BOOL ---
+ wglCreateLayerContext :: proc(hdc: HDC, layer_plane: c.int) -> HGLRC ---
+ wglDescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: c.int, bytes: UINT, pd: LPLAYERPLANEDESCRIPTOR) -> BOOL ---
+ wglGetCurrentContext :: proc() -> HGLRC ---
+ wglGetCurrentDC :: proc() -> HDC ---
+ wglGetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: c.int, cr: ^COLORREF) -> c.int ---
+ wglRealizeLayerPalette :: proc(hdc: HDC, layer_plane: c.int, realize: BOOL) -> BOOL ---
+ wglSetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: c.int, cr: ^COLORREF) -> c.int ---
+ wglShareLists :: proc(HGLRC1, HGLRC2: HGLRC) -> BOOL ---
+ wglSwapLayerBuffers :: proc(hdc: HDC, planes: DWORD) -> BOOL ---
+ wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL ---
+ wglUseFontOutlines :: proc(hdc: HDC, first, count, list_base: DWORD, deviation, extrusion: f32, format: c.int, gmf: LPGLYPHMETRICSFLOAT) -> BOOL ---
+}
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
new file mode 100644
index 000000000..d6b883916
--- /dev/null
+++ b/core/sys/windows/window_messages.odin
@@ -0,0 +1,1049 @@
+// +build windows
+package sys_windows
+
+WM_NULL :: 0x0000
+WM_CREATE :: 0x0001
+WM_DESTROY :: 0x0002
+WM_MOVE :: 0x0003
+WM_SIZE :: 0x0005
+WM_ACTIVATE :: 0x0006
+WM_SETFOCUS :: 0x0007
+WM_KILLFOCUS :: 0x0008
+WM_ENABLE :: 0x000a
+WM_SETREDRAW :: 0x000b
+WM_SETTEXT :: 0x000c
+WM_GETTEXT :: 0x000d
+WM_GETTEXTLENGTH :: 0x000e
+WM_PAINT :: 0x000f
+WM_CLOSE :: 0x0010
+WM_QUERYENDSESSION :: 0x0011
+WM_QUIT :: 0x0012
+WM_QUERYOPEN :: 0x0013
+WM_ERASEBKGND :: 0x0014
+WM_SYSCOLORCHANGE :: 0x0015
+WM_ENDSESSION :: 0x0016
+WM_SHOWWINDOW :: 0x0018
+WM_CTLCOLOR :: 0x0019
+WM_WININICHANGE :: 0x001a
+WM_SETTINGCHANGE :: WM_WININICHANGE
+WM_DEVMODECHANGE :: 0x001b
+WM_ACTIVATEAPP :: 0x001c
+WM_FONTCHANGE :: 0x001d
+WM_TIMECHANGE :: 0x001e
+WM_CANCELMODE :: 0x001f
+WM_SETCURSOR :: 0x0020
+WM_MOUSEACTIVATE :: 0x0021
+WM_CHILDACTIVATE :: 0x0022
+WM_QUEUESYNC :: 0x0023
+WM_GETMINMAXINFO :: 0x0024
+WM_PAINTICON :: 0x0026
+WM_ICONERASEBKGND :: 0x0027
+WM_NEXTDLGCTL :: 0x0028
+WM_SPOOLERSTATUS :: 0x002a
+WM_DRAWITEM :: 0x002b
+WM_MEASUREITEM :: 0x002c
+WM_DELETEITEM :: 0x002d
+WM_VKEYTOITEM :: 0x002e
+WM_CHARTOITEM :: 0x002f
+WM_SETFONT :: 0x0030
+WM_GETFONT :: 0x0031
+WM_SETHOTKEY :: 0x0032
+WM_GETHOTKEY :: 0x0033
+WM_QUERYDRAGICON :: 0x0037
+WM_COMPAREITEM :: 0x0039
+WM_GETOBJECT :: 0x003d
+WM_COMPACTING :: 0x0041
+WM_COMMNOTIFY :: 0x0044
+WM_WINDOWPOSCHANGING :: 0x0046
+WM_WINDOWPOSCHANGED :: 0x0047
+WM_POWER :: 0x0048
+WM_COPYGLOBALDATA :: 0x0049
+WM_COPYDATA :: 0x004a
+WM_CANCELJOURNAL :: 0x004b
+WM_NOTIFY :: 0x004e
+WM_INPUTLANGCHANGEREQUEST :: 0x0050
+WM_INPUTLANGCHANGE :: 0x0051
+WM_TCARD :: 0x0052
+WM_HELP :: 0x0053
+WM_USERCHANGED :: 0x0054
+WM_NOTIFYFORMAT :: 0x0055
+WM_CONTEXTMENU :: 0x007b
+WM_STYLECHANGING :: 0x007c
+WM_STYLECHANGED :: 0x007d
+WM_DISPLAYCHANGE :: 0x007e
+WM_GETICON :: 0x007f
+WM_SETICON :: 0x0080
+WM_NCCREATE :: 0x0081
+WM_NCDESTROY :: 0x0082
+WM_NCCALCSIZE :: 0x0083
+WM_NCHITTEST :: 0x0084
+WM_NCPAINT :: 0x0085
+WM_NCACTIVATE :: 0x0086
+WM_GETDLGCODE :: 0x0087
+WM_SYNCPAINT :: 0x0088
+WM_NCMOUSEMOVE :: 0x00a0
+WM_NCLBUTTONDOWN :: 0x00a1
+WM_NCLBUTTONUP :: 0x00a2
+WM_NCLBUTTONDBLCLK :: 0x00a3
+WM_NCRBUTTONDOWN :: 0x00a4
+WM_NCRBUTTONUP :: 0x00a5
+WM_NCRBUTTONDBLCLK :: 0x00a6
+WM_NCMBUTTONDOWN :: 0x00a7
+WM_NCMBUTTONUP :: 0x00a8
+WM_NCMBUTTONDBLCLK :: 0x00a9
+WM_NCXBUTTONDOWN :: 0x00ab
+WM_NCXBUTTONUP :: 0x00ac
+WM_NCXBUTTONDBLCLK :: 0x00ad
+EM_GETSEL :: 0x00b0
+EM_SETSEL :: 0x00b1
+EM_GETRECT :: 0x00b2
+EM_SETRECT :: 0x00b3
+EM_SETRECTNP :: 0x00b4
+EM_SCROLL :: 0x00b5
+EM_LINESCROLL :: 0x00b6
+EM_SCROLLCARET :: 0x00b7
+EM_GETMODIFY :: 0x00b8
+EM_SETMODIFY :: 0x00b9
+EM_GETLINECOUNT :: 0x00ba
+EM_LINEINDEX :: 0x00bb
+EM_SETHANDLE :: 0x00bc
+EM_GETHANDLE :: 0x00bd
+EM_GETTHUMB :: 0x00be
+EM_LINELENGTH :: 0x00c1
+EM_REPLACESEL :: 0x00c2
+EM_SETFONT :: 0x00c3
+EM_GETLINE :: 0x00c4
+EM_LIMITTEXT :: 0x00c5
+EM_SETLIMITTEXT :: 0x00c5
+EM_CANUNDO :: 0x00c6
+EM_UNDO :: 0x00c7
+EM_FMTLINES :: 0x00c8
+EM_LINEFROMCHAR :: 0x00c9
+EM_SETWORDBREAK :: 0x00ca
+EM_SETTABSTOPS :: 0x00cb
+EM_SETPASSWORDCHAR :: 0x00cc
+EM_EMPTYUNDOBUFFER :: 0x00cd
+EM_GETFIRSTVISIBLELINE :: 0x00ce
+EM_SETREADONLY :: 0x00cf
+EM_SETWORDBREAKPROC :: 0x00d0
+EM_GETWORDBREAKPROC :: 0x00d1
+EM_GETPASSWORDCHAR :: 0x00d2
+EM_SETMARGINS :: 0x00d3
+EM_GETMARGINS :: 0x00d4
+EM_GETLIMITTEXT :: 0x00d5
+EM_POSFROMCHAR :: 0x00d6
+EM_CHARFROMPOS :: 0x00d7
+EM_SETIMESTATUS :: 0x00d8
+EM_GETIMESTATUS :: 0x00d9
+SBM_SETPOS :: 0x00e0
+SBM_GETPOS :: 0x00e1
+SBM_SETRANGE :: 0x00e2
+SBM_GETRANGE :: 0x00e3
+SBM_ENABLE_ARROWS :: 0x00e4
+SBM_SETRANGEREDRAW :: 0x00e6
+SBM_SETSCROLLINFO :: 0x00e9
+SBM_GETSCROLLINFO :: 0x00ea
+SBM_GETSCROLLBARINFO :: 0x00eb
+BM_GETCHECK :: 0x00f0
+BM_SETCHECK :: 0x00f1
+BM_GETSTATE :: 0x00f2
+BM_SETSTATE :: 0x00f3
+BM_SETSTYLE :: 0x00f4
+BM_CLICK :: 0x00f5
+BM_GETIMAGE :: 0x00f6
+BM_SETIMAGE :: 0x00f7
+BM_SETDONTCLICK :: 0x00f8
+WM_INPUT :: 0x00ff
+WM_KEYDOWN :: 0x0100
+WM_KEYFIRST :: 0x0100
+WM_KEYUP :: 0x0101
+WM_CHAR :: 0x0102
+WM_DEADCHAR :: 0x0103
+WM_SYSKEYDOWN :: 0x0104
+WM_SYSKEYUP :: 0x0105
+WM_SYSCHAR :: 0x0106
+WM_SYSDEADCHAR :: 0x0107
+WM_UNICHAR :: 0x0109
+WM_KEYLAST :: 0x0109
+WM_WNT_CONVERTREQUESTEX :: 0x0109
+WM_CONVERTREQUEST :: 0x010a
+WM_CONVERTRESULT :: 0x010b
+WM_INTERIM :: 0x010c
+WM_IME_STARTCOMPOSITION :: 0x010d
+WM_IME_ENDCOMPOSITION :: 0x010e
+WM_IME_COMPOSITION :: 0x010f
+WM_IME_KEYLAST :: 0x010f
+WM_INITDIALOG :: 0x0110
+WM_COMMAND :: 0x0111
+WM_SYSCOMMAND :: 0x0112
+WM_TIMER :: 0x0113
+WM_HSCROLL :: 0x0114
+WM_VSCROLL :: 0x0115
+WM_INITMENU :: 0x0116
+WM_INITMENUPOPUP :: 0x0117
+WM_SYSTIMER :: 0x0118
+WM_MENUSELECT :: 0x011f
+WM_MENUCHAR :: 0x0120
+WM_ENTERIDLE :: 0x0121
+WM_MENURBUTTONUP :: 0x0122
+WM_MENUDRAG :: 0x0123
+WM_MENUGETOBJECT :: 0x0124
+WM_UNINITMENUPOPUP :: 0x0125
+WM_MENUCOMMAND :: 0x0126
+WM_CHANGEUISTATE :: 0x0127
+WM_UPDATEUISTATE :: 0x0128
+WM_QUERYUISTATE :: 0x0129
+WM_LBTRACKPOINT :: 0x0131
+WM_CTLCOLORMSGBOX :: 0x0132
+WM_CTLCOLOREDIT :: 0x0133
+WM_CTLCOLORLISTBOX :: 0x0134
+WM_CTLCOLORBTN :: 0x0135
+WM_CTLCOLORDLG :: 0x0136
+WM_CTLCOLORSCROLLBAR :: 0x0137
+WM_CTLCOLORSTATIC :: 0x0138
+CB_GETEDITSEL :: 0x0140
+CB_LIMITTEXT :: 0x0141
+CB_SETEDITSEL :: 0x0142
+CB_ADDSTRING :: 0x0143
+CB_DELETESTRING :: 0x0144
+CB_DIR :: 0x0145
+CB_GETCOUNT :: 0x0146
+CB_GETCURSEL :: 0x0147
+CB_GETLBTEXT :: 0x0148
+CB_GETLBTEXTLEN :: 0x0149
+CB_INSERTSTRING :: 0x014a
+CB_RESETCONTENT :: 0x014b
+CB_FINDSTRING :: 0x014c
+CB_SELECTSTRING :: 0x014d
+CB_SETCURSEL :: 0x014e
+CB_SHOWDROPDOWN :: 0x014f
+CB_GETITEMDATA :: 0x0150
+CB_SETITEMDATA :: 0x0151
+CB_GETDROPPEDCONTROLRECT :: 0x0152
+CB_SETITEMHEIGHT :: 0x0153
+CB_GETITEMHEIGHT :: 0x0154
+CB_SETEXTENDEDUI :: 0x0155
+CB_GETEXTENDEDUI :: 0x0156
+CB_GETDROPPEDSTATE :: 0x0157
+CB_FINDSTRINGEXACT :: 0x0158
+CB_SETLOCALE :: 0x0159
+CB_GETLOCALE :: 0x015a
+CB_GETTOPINDEX :: 0x015b
+CB_SETTOPINDEX :: 0x015c
+CB_GETHORIZONTALEXTENT :: 0x015d
+CB_SETHORIZONTALEXTENT :: 0x015e
+CB_GETDROPPEDWIDTH :: 0x015f
+CB_SETDROPPEDWIDTH :: 0x0160
+CB_INITSTORAGE :: 0x0161
+CB_MULTIPLEADDSTRING :: 0x0163
+CB_GETCOMBOBOXINFO :: 0x0164
+CB_MSGMAX :: 0x0165
+WM_MOUSEFIRST :: 0x0200
+WM_MOUSEMOVE :: 0x0200
+WM_LBUTTONDOWN :: 0x0201
+WM_LBUTTONUP :: 0x0202
+WM_LBUTTONDBLCLK :: 0x0203
+WM_RBUTTONDOWN :: 0x0204
+WM_RBUTTONUP :: 0x0205
+WM_RBUTTONDBLCLK :: 0x0206
+WM_MBUTTONDOWN :: 0x0207
+WM_MBUTTONUP :: 0x0208
+WM_MBUTTONDBLCLK :: 0x0209
+WM_MOUSELAST :: 0x0209
+WM_MOUSEWHEEL :: 0x020a
+WM_XBUTTONDOWN :: 0x020b
+WM_XBUTTONUP :: 0x020c
+WM_XBUTTONDBLCLK :: 0x020d
+WM_MOUSEHWHEEL :: 0x020e
+WM_PARENTNOTIFY :: 0x0210
+WM_ENTERMENULOOP :: 0x0211
+WM_EXITMENULOOP :: 0x0212
+WM_NEXTMENU :: 0x0213
+WM_SIZING :: 0x0214
+WM_CAPTURECHANGED :: 0x0215
+WM_MOVING :: 0x0216
+WM_POWERBROADCAST :: 0x0218
+WM_DEVICECHANGE :: 0x0219
+WM_MDICREATE :: 0x0220
+WM_MDIDESTROY :: 0x0221
+WM_MDIACTIVATE :: 0x0222
+WM_MDIRESTORE :: 0x0223
+WM_MDINEXT :: 0x0224
+WM_MDIMAXIMIZE :: 0x0225
+WM_MDITILE :: 0x0226
+WM_MDICASCADE :: 0x0227
+WM_MDIICONARRANGE :: 0x0228
+WM_MDIGETACTIVE :: 0x0229
+WM_MDISETMENU :: 0x0230
+WM_ENTERSIZEMOVE :: 0x0231
+WM_EXITSIZEMOVE :: 0x0232
+WM_DROPFILES :: 0x0233
+WM_MDIREFRESHMENU :: 0x0234
+WM_IME_REPORT :: 0x0280
+WM_IME_SETCONTEXT :: 0x0281
+WM_IME_NOTIFY :: 0x0282
+WM_IME_CONTROL :: 0x0283
+WM_IME_COMPOSITIONFULL :: 0x0284
+WM_IME_SELECT :: 0x0285
+WM_IME_CHAR :: 0x0286
+WM_IME_REQUEST :: 0x0288
+WM_IMEKEYDOWN :: 0x0290
+WM_IME_KEYDOWN :: 0x0290
+WM_IMEKEYUP :: 0x0291
+WM_IME_KEYUP :: 0x0291
+WM_NCMOUSEHOVER :: 0x02a0
+WM_MOUSEHOVER :: 0x02a1
+WM_NCMOUSELEAVE :: 0x02a2
+WM_MOUSELEAVE :: 0x02a3
+WM_CUT :: 0x0300
+WM_COPY :: 0x0301
+WM_PASTE :: 0x0302
+WM_CLEAR :: 0x0303
+WM_UNDO :: 0x0304
+WM_RENDERFORMAT :: 0x0305
+WM_RENDERALLFORMATS :: 0x0306
+WM_DESTROYCLIPBOARD :: 0x0307
+WM_DRAWCLIPBOARD :: 0x0308
+WM_PAINTCLIPBOARD :: 0x0309
+WM_VSCROLLCLIPBOARD :: 0x030a
+WM_SIZECLIPBOARD :: 0x030b
+WM_ASKCBFORMATNAME :: 0x030c
+WM_CHANGECBCHAIN :: 0x030d
+WM_HSCROLLCLIPBOARD :: 0x030e
+WM_QUERYNEWPALETTE :: 0x030f
+WM_PALETTEISCHANGING :: 0x0310
+WM_PALETTECHANGED :: 0x0311
+WM_HOTKEY :: 0x0312
+WM_PRINT :: 0x0317
+WM_PRINTCLIENT :: 0x0318
+WM_APPCOMMAND :: 0x0319
+WM_HANDHELDFIRST :: 0x0358
+WM_HANDHELDLAST :: 0x035f
+WM_AFXFIRST :: 0x0360
+WM_AFXLAST :: 0x037f
+WM_PENWINFIRST :: 0x0380
+WM_RCRESULT :: 0x0381
+WM_HOOKRCRESULT :: 0x0382
+WM_GLOBALRCCHANGE :: 0x0383
+WM_PENMISCINFO :: 0x0383
+WM_SKB :: 0x0384
+WM_HEDITCTL :: 0x0385
+WM_PENCTL :: 0x0385
+WM_PENMISC :: 0x0386
+WM_CTLINIT :: 0x0387
+WM_PENEVENT :: 0x0388
+WM_PENWINLAST :: 0x038f
+DDM_SETFMT :: 0x0400
+DM_GETDEFID :: 0x0400
+NIN_SELECT :: 0x0400
+TBM_GETPOS :: 0x0400
+WM_PSD_PAGESETUPDLG :: 0x0400
+WM_USER :: 0x0400
+CBEM_INSERTITEMA :: 0x0401
+DDM_DRAW :: 0x0401
+DM_SETDEFID :: 0x0401
+HKM_SETHOTKEY :: 0x0401
+PBM_SETRANGE :: 0x0401
+RB_INSERTBANDA :: 0x0401
+SB_SETTEXTA :: 0x0401
+TB_ENABLEBUTTON :: 0x0401
+TBM_GETRANGEMIN :: 0x0401
+TTM_ACTIVATE :: 0x0401
+WM_CHOOSEFONT_GETLOGFONT :: 0x0401
+WM_PSD_FULLPAGERECT :: 0x0401
+CBEM_SETIMAGELIST :: 0x0402
+DDM_CLOSE :: 0x0402
+DM_REPOSITION :: 0x0402
+HKM_GETHOTKEY :: 0x0402
+PBM_SETPOS :: 0x0402
+RB_DELETEBAND :: 0x0402
+SB_GETTEXTA :: 0x0402
+TB_CHECKBUTTON :: 0x0402
+TBM_GETRANGEMAX :: 0x0402
+WM_PSD_MINMARGINRECT :: 0x0402
+CBEM_GETIMAGELIST :: 0x0403
+DDM_BEGIN :: 0x0403
+HKM_SETRULES :: 0x0403
+PBM_DELTAPOS :: 0x0403
+RB_GETBARINFO :: 0x0403
+SB_GETTEXTLENGTHA :: 0x0403
+TBM_GETTIC :: 0x0403
+TB_PRESSBUTTON :: 0x0403
+TTM_SETDELAYTIME :: 0x0403
+WM_PSD_MARGINRECT :: 0x0403
+CBEM_GETITEMA :: 0x0404
+DDM_END :: 0x0404
+PBM_SETSTEP :: 0x0404
+RB_SETBARINFO :: 0x0404
+SB_SETPARTS :: 0x0404
+TB_HIDEBUTTON :: 0x0404
+TBM_SETTIC :: 0x0404
+TTM_ADDTOOLA :: 0x0404
+WM_PSD_GREEKTEXTRECT :: 0x0404
+CBEM_SETITEMA :: 0x0405
+PBM_STEPIT :: 0x0405
+TB_INDETERMINATE :: 0x0405
+TBM_SETPOS :: 0x0405
+TTM_DELTOOLA :: 0x0405
+WM_PSD_ENVSTAMPRECT :: 0x0405
+CBEM_GETCOMBOCONTROL :: 0x0406
+PBM_SETRANGE32 :: 0x0406
+RB_SETBANDINFOA :: 0x0406
+SB_GETPARTS :: 0x0406
+TB_MARKBUTTON :: 0x0406
+TBM_SETRANGE :: 0x0406
+TTM_NEWTOOLRECTA :: 0x0406
+WM_PSD_YAFULLPAGERECT :: 0x0406
+CBEM_GETEDITCONTROL :: 0x0407
+PBM_GETRANGE :: 0x0407
+RB_SETPARENT :: 0x0407
+SB_GETBORDERS :: 0x0407
+TBM_SETRANGEMIN :: 0x0407
+TTM_RELAYEVENT :: 0x0407
+CBEM_SETEXSTYLE :: 0x0408
+PBM_GETPOS :: 0x0408
+RB_HITTEST :: 0x0408
+SB_SETMINHEIGHT :: 0x0408
+TBM_SETRANGEMAX :: 0x0408
+TTM_GETTOOLINFOA :: 0x0408
+CBEM_GETEXSTYLE :: 0x0409
+CBEM_GETEXTENDEDSTYLE :: 0x0409
+PBM_SETBARCOLOR :: 0x0409
+RB_GETRECT :: 0x0409
+SB_SIMPLE :: 0x0409
+TB_ISBUTTONENABLED :: 0x0409
+TBM_CLEARTICS :: 0x0409
+TTM_SETTOOLINFOA :: 0x0409
+CBEM_HASEDITCHANGED :: 0x040a
+RB_INSERTBANDW :: 0x040a
+SB_GETRECT :: 0x040a
+TB_ISBUTTONCHECKED :: 0x040a
+TBM_SETSEL :: 0x040a
+TTM_HITTESTA :: 0x040a
+WIZ_QUERYNUMPAGES :: 0x040a
+CBEM_INSERTITEMW :: 0x040b
+RB_SETBANDINFOW :: 0x040b
+SB_SETTEXTW :: 0x040b
+TB_ISBUTTONPRESSED :: 0x040b
+TBM_SETSELSTART :: 0x040b
+TTM_GETTEXTA :: 0x040b
+WIZ_NEXT :: 0x040b
+CBEM_SETITEMW :: 0x040c
+RB_GETBANDCOUNT :: 0x040c
+SB_GETTEXTLENGTHW :: 0x040c
+TB_ISBUTTONHIDDEN :: 0x040c
+TBM_SETSELEND :: 0x040c
+TTM_UPDATETIPTEXTA :: 0x040c
+WIZ_PREV :: 0x040c
+CBEM_GETITEMW :: 0x040d
+RB_GETROWCOUNT :: 0x040d
+SB_GETTEXTW :: 0x040d
+TB_ISBUTTONINDETERMINATE :: 0x040d
+TTM_GETTOOLCOUNT :: 0x040d
+CBEM_SETEXTENDEDSTYLE :: 0x040e
+RB_GETROWHEIGHT :: 0x040e
+SB_ISSIMPLE :: 0x040e
+TB_ISBUTTONHIGHLIGHTED :: 0x040e
+TBM_GETPTICS :: 0x040e
+TTM_ENUMTOOLSA :: 0x040e
+SB_SETICON :: 0x040f
+TBM_GETTICPOS :: 0x040f
+TTM_GETCURRENTTOOLA :: 0x040f
+RB_IDTOINDEX :: 0x0410
+SB_SETTIPTEXTA :: 0x0410
+TBM_GETNUMTICS :: 0x0410
+TTM_WINDOWFROMPOINT :: 0x0410
+RB_GETTOOLTIPS :: 0x0411
+SB_SETTIPTEXTW :: 0x0411
+TBM_GETSELSTART :: 0x0411
+TB_SETSTATE :: 0x0411
+TTM_TRACKACTIVATE :: 0x0411
+RB_SETTOOLTIPS :: 0x0412
+SB_GETTIPTEXTA :: 0x0412
+TB_GETSTATE :: 0x0412
+TBM_GETSELEND :: 0x0412
+TTM_TRACKPOSITION :: 0x0412
+RB_SETBKCOLOR :: 0x0413
+SB_GETTIPTEXTW :: 0x0413
+TB_ADDBITMAP :: 0x0413
+TBM_CLEARSEL :: 0x0413
+TTM_SETTIPBKCOLOR :: 0x0413
+RB_GETBKCOLOR :: 0x0414
+SB_GETICON :: 0x0414
+TB_ADDBUTTONSA :: 0x0414
+TBM_SETTICFREQ :: 0x0414
+TTM_SETTIPTEXTCOLOR :: 0x0414
+RB_SETTEXTCOLOR :: 0x0415
+TB_INSERTBUTTONA :: 0x0415
+TBM_SETPAGESIZE :: 0x0415
+TTM_GETDELAYTIME :: 0x0415
+RB_GETTEXTCOLOR :: 0x0416
+TB_DELETEBUTTON :: 0x0416
+TBM_GETPAGESIZE :: 0x0416
+TTM_GETTIPBKCOLOR :: 0x0416
+RB_SIZETORECT :: 0x0417
+TB_GETBUTTON :: 0x0417
+TBM_SETLINESIZE :: 0x0417
+TTM_GETTIPTEXTCOLOR :: 0x0417
+RB_BEGINDRAG :: 0x0418
+TB_BUTTONCOUNT :: 0x0418
+TBM_GETLINESIZE :: 0x0418
+TTM_SETMAXTIPWIDTH :: 0x0418
+RB_ENDDRAG :: 0x0419
+TB_COMMANDTOINDEX :: 0x0419
+TBM_GETTHUMBRECT :: 0x0419
+TTM_GETMAXTIPWIDTH :: 0x0419
+RB_DRAGMOVE :: 0x041a
+TBM_GETCHANNELRECT :: 0x041a
+TB_SAVERESTOREA :: 0x041a
+TTM_SETMARGIN :: 0x041a
+RB_GETBARHEIGHT :: 0x041b
+TB_CUSTOMIZE :: 0x041b
+TBM_SETTHUMBLENGTH :: 0x041b
+TTM_GETMARGIN :: 0x041b
+RB_GETBANDINFOW :: 0x041c
+TB_ADDSTRINGA :: 0x041c
+TBM_GETTHUMBLENGTH :: 0x041c
+TTM_POP :: 0x041c
+RB_GETBANDINFOA :: 0x041d
+TB_GETITEMRECT :: 0x041d
+TBM_SETTOOLTIPS :: 0x041d
+TTM_UPDATE :: 0x041d
+RB_MINIMIZEBAND :: 0x041e
+TB_BUTTONSTRUCTSIZE :: 0x041e
+TBM_GETTOOLTIPS :: 0x041e
+TTM_GETBUBBLESIZE :: 0x041e
+RB_MAXIMIZEBAND :: 0x041f
+TBM_SETTIPSIDE :: 0x041f
+TB_SETBUTTONSIZE :: 0x041f
+TTM_ADJUSTRECT :: 0x041f
+TBM_SETBUDDY :: 0x0420
+TB_SETBITMAPSIZE :: 0x0420
+TTM_SETTITLEA :: 0x0420
+MSG_FTS_JUMP_VA :: 0x0421
+TB_AUTOSIZE :: 0x0421
+TBM_GETBUDDY :: 0x0421
+TTM_SETTITLEW :: 0x0421
+RB_GETBANDBORDERS :: 0x0422
+MSG_FTS_JUMP_QWORD :: 0x0423
+RB_SHOWBAND :: 0x0423
+TB_GETTOOLTIPS :: 0x0423
+MSG_REINDEX_REQUEST :: 0x0424
+TB_SETTOOLTIPS :: 0x0424
+MSG_FTS_WHERE_IS_IT :: 0x0425
+RB_SETPALETTE :: 0x0425
+TB_SETPARENT :: 0x0425
+RB_GETPALETTE :: 0x0426
+RB_MOVEBAND :: 0x0427
+TB_SETROWS :: 0x0427
+TB_GETROWS :: 0x0428
+TB_GETBITMAPFLAGS :: 0x0429
+TB_SETCMDID :: 0x042a
+RB_PUSHCHEVRON :: 0x042b
+TB_CHANGEBITMAP :: 0x042b
+TB_GETBITMAP :: 0x042c
+MSG_GET_DEFFONT :: 0x042d
+TB_GETBUTTONTEXTA :: 0x042d
+TB_REPLACEBITMAP :: 0x042e
+TB_SETINDENT :: 0x042f
+TB_SETIMAGELIST :: 0x0430
+TB_GETIMAGELIST :: 0x0431
+TB_LOADIMAGES :: 0x0432
+EM_CANPASTE :: 0x0432
+TTM_ADDTOOLW :: 0x0432
+EM_DISPLAYBAND :: 0x0433
+TB_GETRECT :: 0x0433
+TTM_DELTOOLW :: 0x0433
+EM_EXGETSEL :: 0x0434
+TB_SETHOTIMAGELIST :: 0x0434
+TTM_NEWTOOLRECTW :: 0x0434
+EM_EXLIMITTEXT :: 0x0435
+TB_GETHOTIMAGELIST :: 0x0435
+TTM_GETTOOLINFOW :: 0x0435
+EM_EXLINEFROMCHAR :: 0x0436
+TB_SETDISABLEDIMAGELIST :: 0x0436
+TTM_SETTOOLINFOW :: 0x0436
+EM_EXSETSEL :: 0x0437
+TB_GETDISABLEDIMAGELIST :: 0x0437
+TTM_HITTESTW :: 0x0437
+EM_FINDTEXT :: 0x0438
+TB_SETSTYLE :: 0x0438
+TTM_GETTEXTW :: 0x0438
+EM_FORMATRANGE :: 0x0439
+TB_GETSTYLE :: 0x0439
+TTM_UPDATETIPTEXTW :: 0x0439
+EM_GETCHARFORMAT :: 0x043a
+TB_GETBUTTONSIZE :: 0x043a
+TTM_ENUMTOOLSW :: 0x043a
+EM_GETEVENTMASK :: 0x043b
+TB_SETBUTTONWIDTH :: 0x043b
+TTM_GETCURRENTTOOLW :: 0x043b
+EM_GETOLEINTERFACE :: 0x043c
+TB_SETMAXTEXTROWS :: 0x043c
+EM_GETPARAFORMAT :: 0x043d
+TB_GETTEXTROWS :: 0x043d
+EM_GETSELTEXT :: 0x043e
+TB_GETOBJECT :: 0x043e
+EM_HIDESELECTION :: 0x043f
+TB_GETBUTTONINFOW :: 0x043f
+EM_PASTESPECIAL :: 0x0440
+TB_SETBUTTONINFOW :: 0x0440
+EM_REQUESTRESIZE :: 0x0441
+TB_GETBUTTONINFOA :: 0x0441
+EM_SELECTIONTYPE :: 0x0442
+TB_SETBUTTONINFOA :: 0x0442
+EM_SETBKGNDCOLOR :: 0x0443
+TB_INSERTBUTTONW :: 0x0443
+EM_SETCHARFORMAT :: 0x0444
+TB_ADDBUTTONSW :: 0x0444
+EM_SETEVENTMASK :: 0x0445
+TB_HITTEST :: 0x0445
+EM_SETOLECALLBACK :: 0x0446
+TB_SETDRAWTEXTFLAGS :: 0x0446
+EM_SETPARAFORMAT :: 0x0447
+TB_GETHOTITEM :: 0x0447
+EM_SETTARGETDEVICE :: 0x0448
+TB_SETHOTITEM :: 0x0448
+EM_STREAMIN :: 0x0449
+TB_SETANCHORHIGHLIGHT :: 0x0449
+EM_STREAMOUT :: 0x044a
+TB_GETANCHORHIGHLIGHT :: 0x044a
+EM_GETTEXTRANGE :: 0x044b
+TB_GETBUTTONTEXTW :: 0x044b
+EM_FINDWORDBREAK :: 0x044c
+TB_SAVERESTOREW :: 0x044c
+EM_SETOPTIONS :: 0x044d
+TB_ADDSTRINGW :: 0x044d
+EM_GETOPTIONS :: 0x044e
+TB_MAPACCELERATORA :: 0x044e
+EM_FINDTEXTEX :: 0x044f
+TB_GETINSERTMARK :: 0x044f
+EM_GETWORDBREAKPROCEX :: 0x0450
+TB_SETINSERTMARK :: 0x0450
+EM_SETWORDBREAKPROCEX :: 0x0451
+TB_INSERTMARKHITTEST :: 0x0451
+EM_SETUNDOLIMIT :: 0x0452
+TB_MOVEBUTTON :: 0x0452
+TB_GETMAXSIZE :: 0x0453
+EM_REDO :: 0x0454
+TB_SETEXTENDEDSTYLE :: 0x0454
+EM_CANREDO :: 0x0455
+TB_GETEXTENDEDSTYLE :: 0x0455
+EM_GETUNDONAME :: 0x0456
+TB_GETPADDING :: 0x0456
+EM_GETREDONAME :: 0x0457
+TB_SETPADDING :: 0x0457
+EM_STOPGROUPTYPING :: 0x0458
+TB_SETINSERTMARKCOLOR :: 0x0458
+EM_SETTEXTMODE :: 0x0459
+TB_GETINSERTMARKCOLOR :: 0x0459
+EM_GETTEXTMODE :: 0x045a
+TB_MAPACCELERATORW :: 0x045a
+EM_AUTOURLDETECT :: 0x045b
+TB_GETSTRINGW :: 0x045b
+EM_GETAUTOURLDETECT :: 0x045c
+TB_GETSTRINGA :: 0x045c
+EM_SETPALETTE :: 0x045d
+EM_GETTEXTEX :: 0x045e
+EM_GETTEXTLENGTHEX :: 0x045f
+EM_SHOWSCROLLBAR :: 0x0460
+EM_SETTEXTEX :: 0x0461
+TAPI_REPLY :: 0x0463
+ACM_OPENA :: 0x0464
+BFFM_SETSTATUSTEXTA :: 0x0464
+CDM_FIRST :: 0x0464
+CDM_GETSPEC :: 0x0464
+EM_SETPUNCTUATION :: 0x0464
+IPM_CLEARADDRESS :: 0x0464
+WM_CAP_UNICODE_START :: 0x0464
+ACM_PLAY :: 0x0465
+BFFM_ENABLEOK :: 0x0465
+CDM_GETFILEPATH :: 0x0465
+EM_GETPUNCTUATION :: 0x0465
+IPM_SETADDRESS :: 0x0465
+PSM_SETCURSEL :: 0x0465
+UDM_SETRANGE :: 0x0465
+WM_CHOOSEFONT_SETLOGFONT :: 0x0465
+ACM_STOP :: 0x0466
+BFFM_SETSELECTIONA :: 0x0466
+CDM_GETFOLDERPATH :: 0x0466
+EM_SETWORDWRAPMODE :: 0x0466
+IPM_GETADDRESS :: 0x0466
+PSM_REMOVEPAGE :: 0x0466
+UDM_GETRANGE :: 0x0466
+WM_CAP_SET_CALLBACK_ERRORW :: 0x0466
+WM_CHOOSEFONT_SETFLAGS :: 0x0466
+ACM_OPENW :: 0x0467
+BFFM_SETSELECTIONW :: 0x0467
+CDM_GETFOLDERIDLIST :: 0x0467
+EM_GETWORDWRAPMODE :: 0x0467
+IPM_SETRANGE :: 0x0467
+PSM_ADDPAGE :: 0x0467
+UDM_SETPOS :: 0x0467
+WM_CAP_SET_CALLBACK_STATUSW :: 0x0467
+BFFM_SETSTATUSTEXTW :: 0x0468
+CDM_SETCONTROLTEXT :: 0x0468
+EM_SETIMECOLOR :: 0x0468
+IPM_SETFOCUS :: 0x0468
+PSM_CHANGED :: 0x0468
+UDM_GETPOS :: 0x0468
+CDM_HIDECONTROL :: 0x0469
+EM_GETIMECOLOR :: 0x0469
+IPM_ISBLANK :: 0x0469
+PSM_RESTARTWINDOWS :: 0x0469
+UDM_SETBUDDY :: 0x0469
+CDM_SETDEFEXT :: 0x046a
+EM_SETIMEOPTIONS :: 0x046a
+PSM_REBOOTSYSTEM :: 0x046a
+UDM_GETBUDDY :: 0x046a
+EM_GETIMEOPTIONS :: 0x046b
+PSM_CANCELTOCLOSE :: 0x046b
+UDM_SETACCEL :: 0x046b
+EM_CONVPOSITION :: 0x046c
+PSM_QUERYSIBLINGS :: 0x046c
+UDM_GETACCEL :: 0x046c
+MCIWNDM_GETZOOM :: 0x046d
+PSM_UNCHANGED :: 0x046d
+UDM_SETBASE :: 0x046d
+PSM_APPLY :: 0x046e
+UDM_GETBASE :: 0x046e
+PSM_SETTITLEA :: 0x046f
+UDM_SETRANGE32 :: 0x046f
+PSM_SETWIZBUTTONS :: 0x0470
+UDM_GETRANGE32 :: 0x0470
+WM_CAP_DRIVER_GET_NAMEW :: 0x0470
+PSM_PRESSBUTTON :: 0x0471
+UDM_SETPOS32 :: 0x0471
+WM_CAP_DRIVER_GET_VERSIONW :: 0x0471
+PSM_SETCURSELID :: 0x0472
+UDM_GETPOS32 :: 0x0472
+PSM_SETFINISHTEXTA :: 0x0473
+PSM_GETTABCONTROL :: 0x0474
+PSM_ISDIALOGMESSAGE :: 0x0475
+MCIWNDM_REALIZE :: 0x0476
+PSM_GETCURRENTPAGEHWND :: 0x0476
+MCIWNDM_SETTIMEFORMATA :: 0x0477
+PSM_INSERTPAGE :: 0x0477
+EM_SETLANGOPTIONS :: 0x0478
+MCIWNDM_GETTIMEFORMATA :: 0x0478
+PSM_SETTITLEW :: 0x0478
+WM_CAP_FILE_SET_CAPTURE_FILEW :: 0x0478
+EM_GETLANGOPTIONS :: 0x0479
+MCIWNDM_VALIDATEMEDIA :: 0x0479
+PSM_SETFINISHTEXTW :: 0x0479
+WM_CAP_FILE_GET_CAPTURE_FILEW :: 0x0479
+EM_GETIMECOMPMODE :: 0x047a
+EM_FINDTEXTW :: 0x047b
+MCIWNDM_PLAYTO :: 0x047b
+WM_CAP_FILE_SAVEASW :: 0x047b
+EM_FINDTEXTEXW :: 0x047c
+MCIWNDM_GETFILENAMEA :: 0x047c
+EM_RECONVERSION :: 0x047d
+MCIWNDM_GETDEVICEA :: 0x047d
+PSM_SETHEADERTITLEA :: 0x047d
+WM_CAP_FILE_SAVEDIBW :: 0x047d
+EM_SETIMEMODEBIAS :: 0x047e
+MCIWNDM_GETPALETTE :: 0x047e
+PSM_SETHEADERTITLEW :: 0x047e
+EM_GETIMEMODEBIAS :: 0x047f
+MCIWNDM_SETPALETTE :: 0x047f
+PSM_SETHEADERSUBTITLEA :: 0x047f
+MCIWNDM_GETERRORA :: 0x0480
+PSM_SETHEADERSUBTITLEW :: 0x0480
+PSM_HWNDTOINDEX :: 0x0481
+PSM_INDEXTOHWND :: 0x0482
+MCIWNDM_SETINACTIVETIMER :: 0x0483
+PSM_PAGETOINDEX :: 0x0483
+PSM_INDEXTOPAGE :: 0x0484
+DL_BEGINDRAG :: 0x0485
+MCIWNDM_GETINACTIVETIMER :: 0x0485
+PSM_IDTOINDEX :: 0x0485
+DL_DRAGGING :: 0x0486
+PSM_INDEXTOID :: 0x0486
+DL_DROPPED :: 0x0487
+PSM_GETRESULT :: 0x0487
+DL_CANCELDRAG :: 0x0488
+PSM_RECALCPAGESIZES :: 0x0488
+MCIWNDM_GET_SOURCE :: 0x048c
+MCIWNDM_PUT_SOURCE :: 0x048d
+MCIWNDM_GET_DEST :: 0x048e
+MCIWNDM_PUT_DEST :: 0x048f
+MCIWNDM_CAN_PLAY :: 0x0490
+MCIWNDM_CAN_WINDOW :: 0x0491
+MCIWNDM_CAN_RECORD :: 0x0492
+MCIWNDM_CAN_SAVE :: 0x0493
+MCIWNDM_CAN_EJECT :: 0x0494
+MCIWNDM_CAN_CONFIG :: 0x0495
+IE_GETINK :: 0x0496
+IE_MSGFIRST :: 0x0496
+MCIWNDM_PALETTEKICK :: 0x0496
+IE_SETINK :: 0x0497
+IE_GETPENTIP :: 0x0498
+IE_SETPENTIP :: 0x0499
+IE_GETERASERTIP :: 0x049a
+IE_SETERASERTIP :: 0x049b
+IE_GETBKGND :: 0x049c
+IE_SETBKGND :: 0x049d
+IE_GETGRIDORIGIN :: 0x049e
+IE_SETGRIDORIGIN :: 0x049f
+IE_GETGRIDPEN :: 0x04a0
+IE_SETGRIDPEN :: 0x04a1
+IE_GETGRIDSIZE :: 0x04a2
+IE_SETGRIDSIZE :: 0x04a3
+IE_GETMODE :: 0x04a4
+IE_SETMODE :: 0x04a5
+IE_GETINKRECT :: 0x04a6
+WM_CAP_SET_MCI_DEVICEW :: 0x04a6
+WM_CAP_GET_MCI_DEVICEW :: 0x04a7
+WM_CAP_PAL_OPENW :: 0x04b4
+WM_CAP_PAL_SAVEW :: 0x04b5
+IE_GETAPPDATA :: 0x04b8
+IE_SETAPPDATA :: 0x04b9
+IE_GETDRAWOPTS :: 0x04ba
+IE_SETDRAWOPTS :: 0x04bb
+IE_GETFORMAT :: 0x04bc
+IE_SETFORMAT :: 0x04bd
+IE_GETINKINPUT :: 0x04be
+IE_SETINKINPUT :: 0x04bf
+IE_GETNOTIFY :: 0x04c0
+IE_SETNOTIFY :: 0x04c1
+IE_GETRECOG :: 0x04c2
+IE_SETRECOG :: 0x04c3
+IE_GETSECURITY :: 0x04c4
+IE_SETSECURITY :: 0x04c5
+IE_GETSEL :: 0x04c6
+IE_SETSEL :: 0x04c7
+CDM_LAST :: 0x04c8
+EM_SETBIDIOPTIONS :: 0x04c8
+IE_DOCOMMAND :: 0x04c8
+MCIWNDM_NOTIFYMODE :: 0x04c8
+EM_GETBIDIOPTIONS :: 0x04c9
+IE_GETCOMMAND :: 0x04c9
+EM_SETTYPOGRAPHYOPTIONS :: 0x04ca
+IE_GETCOUNT :: 0x04ca
+EM_GETTYPOGRAPHYOPTIONS :: 0x04cb
+IE_GETGESTURE :: 0x04cb
+MCIWNDM_NOTIFYMEDIA :: 0x04cb
+EM_SETEDITSTYLE :: 0x04cc
+IE_GETMENU :: 0x04cc
+EM_GETEDITSTYLE :: 0x04cd
+IE_GETPAINTDC :: 0x04cd
+MCIWNDM_NOTIFYERROR :: 0x04cd
+IE_GETPDEVENT :: 0x04ce
+IE_GETSELCOUNT :: 0x04cf
+IE_GETSELITEMS :: 0x04d0
+IE_GETSTYLE :: 0x04d1
+MCIWNDM_SETTIMEFORMATW :: 0x04db
+EM_OUTLINE :: 0x04dc
+MCIWNDM_GETTIMEFORMATW :: 0x04dc
+EM_GETSCROLLPOS :: 0x04dd
+EM_SETSCROLLPOS :: 0x04de
+EM_SETFONTSIZE :: 0x04df
+EM_GETZOOM :: 0x04e0
+MCIWNDM_GETFILENAMEW :: 0x04e0
+EM_SETZOOM :: 0x04e1
+MCIWNDM_GETDEVICEW :: 0x04e1
+EM_GETVIEWKIND :: 0x04e2
+EM_SETVIEWKIND :: 0x04e3
+EM_GETPAGE :: 0x04e4
+MCIWNDM_GETERRORW :: 0x04e4
+EM_SETPAGE :: 0x04e5
+EM_GETHYPHENATEINFO :: 0x04e6
+EM_SETHYPHENATEINFO :: 0x04e7
+EM_GETPAGEROTATE :: 0x04eb
+EM_SETPAGEROTATE :: 0x04ec
+EM_GETCTFMODEBIAS :: 0x04ed
+EM_SETCTFMODEBIAS :: 0x04ee
+EM_GETCTFOPENSTATUS :: 0x04f0
+EM_SETCTFOPENSTATUS :: 0x04f1
+EM_GETIMECOMPTEXT :: 0x04f2
+EM_ISIME :: 0x04f3
+EM_GETIMEPROPERTY :: 0x04f4
+EM_GETQUERYRTFOBJ :: 0x050d
+EM_SETQUERYRTFOBJ :: 0x050e
+FM_GETFOCUS :: 0x0600
+FM_GETDRIVEINFOA :: 0x0601
+FM_GETSELCOUNT :: 0x0602
+FM_GETSELCOUNTLFN :: 0x0603
+FM_GETFILESELA :: 0x0604
+FM_GETFILESELLFNA :: 0x0605
+FM_REFRESH_WINDOWS :: 0x0606
+FM_RELOAD_EXTENSIONS :: 0x0607
+FM_GETDRIVEINFOW :: 0x0611
+FM_GETFILESELW :: 0x0614
+FM_GETFILESELLFNW :: 0x0615
+WLX_WM_SAS :: 0x0659
+SM_GETSELCOUNT :: 0x07e8
+UM_GETSELCOUNT :: 0x07e8
+WM_CPL_LAUNCH :: 0x07e8
+SM_GETSERVERSELA :: 0x07e9
+UM_GETUSERSELA :: 0x07e9
+WM_CPL_LAUNCHED :: 0x07e9
+SM_GETSERVERSELW :: 0x07ea
+UM_GETUSERSELW :: 0x07ea
+SM_GETCURFOCUSA :: 0x07eb
+UM_GETGROUPSELA :: 0x07eb
+SM_GETCURFOCUSW :: 0x07ec
+UM_GETGROUPSELW :: 0x07ec
+SM_GETOPTIONS :: 0x07ed
+UM_GETCURFOCUSA :: 0x07ed
+UM_GETCURFOCUSW :: 0x07ee
+UM_GETOPTIONS :: 0x07ef
+UM_GETOPTIONS2 :: 0x07f0
+LVM_FIRST :: 0x1000
+LVM_GETBKCOLOR :: 0x1000
+LVM_SETBKCOLOR :: 0x1001
+LVM_GETIMAGELIST :: 0x1002
+LVM_SETIMAGELIST :: 0x1003
+LVM_GETITEMCOUNT :: 0x1004
+LVM_GETITEMA :: 0x1005
+LVM_SETITEMA :: 0x1006
+LVM_INSERTITEMA :: 0x1007
+LVM_DELETEITEM :: 0x1008
+LVM_DELETEALLITEMS :: 0x1009
+LVM_GETCALLBACKMASK :: 0x100a
+LVM_SETCALLBACKMASK :: 0x100b
+LVM_GETNEXTITEM :: 0x100c
+LVM_FINDITEMA :: 0x100d
+LVM_GETITEMRECT :: 0x100e
+LVM_SETITEMPOSITION :: 0x100f
+LVM_GETITEMPOSITION :: 0x1010
+LVM_GETSTRINGWIDTHA :: 0x1011
+LVM_HITTEST :: 0x1012
+LVM_ENSUREVISIBLE :: 0x1013
+LVM_SCROLL :: 0x1014
+LVM_REDRAWITEMS :: 0x1015
+LVM_ARRANGE :: 0x1016
+LVM_EDITLABELA :: 0x1017
+LVM_GETEDITCONTROL :: 0x1018
+LVM_GETCOLUMNA :: 0x1019
+LVM_SETCOLUMNA :: 0x101a
+LVM_INSERTCOLUMNA :: 0x101b
+LVM_DELETECOLUMN :: 0x101c
+LVM_GETCOLUMNWIDTH :: 0x101d
+LVM_SETCOLUMNWIDTH :: 0x101e
+LVM_GETHEADER :: 0x101f
+LVM_CREATEDRAGIMAGE :: 0x1021
+LVM_GETVIEWRECT :: 0x1022
+LVM_GETTEXTCOLOR :: 0x1023
+LVM_SETTEXTCOLOR :: 0x1024
+LVM_GETTEXTBKCOLOR :: 0x1025
+LVM_SETTEXTBKCOLOR :: 0x1026
+LVM_GETTOPINDEX :: 0x1027
+LVM_GETCOUNTPERPAGE :: 0x1028
+LVM_GETORIGIN :: 0x1029
+LVM_UPDATE :: 0x102a
+LVM_SETITEMSTATE :: 0x102b
+LVM_GETITEMSTATE :: 0x102c
+LVM_GETITEMTEXTA :: 0x102d
+LVM_SETITEMTEXTA :: 0x102e
+LVM_SETITEMCOUNT :: 0x102f
+LVM_SORTITEMS :: 0x1030
+LVM_SETITEMPOSITION32 :: 0x1031
+LVM_GETSELECTEDCOUNT :: 0x1032
+LVM_GETITEMSPACING :: 0x1033
+LVM_GETISEARCHSTRINGA :: 0x1034
+LVM_SETICONSPACING :: 0x1035
+LVM_SETEXTENDEDLISTVIEWSTYLE :: 0x1036
+LVM_GETEXTENDEDLISTVIEWSTYLE :: 0x1037
+LVM_GETSUBITEMRECT :: 0x1038
+LVM_SUBITEMHITTEST :: 0x1039
+LVM_SETCOLUMNORDERARRAY :: 0x103a
+LVM_GETCOLUMNORDERARRAY :: 0x103b
+LVM_SETHOTITEM :: 0x103c
+LVM_GETHOTITEM :: 0x103d
+LVM_SETHOTCURSOR :: 0x103e
+LVM_GETHOTCURSOR :: 0x103f
+LVM_APPROXIMATEVIEWRECT :: 0x1040
+LVM_SETWORKAREAS :: 0x1041
+LVM_GETSELECTIONMARK :: 0x1042
+LVM_SETSELECTIONMARK :: 0x1043
+LVM_SETBKIMAGEA :: 0x1044
+LVM_GETBKIMAGEA :: 0x1045
+LVM_GETWORKAREAS :: 0x1046
+LVM_SETHOVERTIME :: 0x1047
+LVM_GETHOVERTIME :: 0x1048
+LVM_GETNUMBEROFWORKAREAS :: 0x1049
+LVM_SETTOOLTIPS :: 0x104a
+LVM_GETITEMW :: 0x104b
+LVM_SETITEMW :: 0x104c
+LVM_INSERTITEMW :: 0x104d
+LVM_GETTOOLTIPS :: 0x104e
+LVM_FINDITEMW :: 0x1053
+LVM_GETSTRINGWIDTHW :: 0x1057
+LVM_GETCOLUMNW :: 0x105f
+LVM_SETCOLUMNW :: 0x1060
+LVM_INSERTCOLUMNW :: 0x1061
+LVM_GETITEMTEXTW :: 0x1073
+LVM_SETITEMTEXTW :: 0x1074
+LVM_GETISEARCHSTRINGW :: 0x1075
+LVM_EDITLABELW :: 0x1076
+LVM_GETBKIMAGEW :: 0x108b
+LVM_SETSELECTEDCOLUMN :: 0x108c
+LVM_SETTILEWIDTH :: 0x108d
+LVM_SETVIEW :: 0x108e
+LVM_GETVIEW :: 0x108f
+LVM_INSERTGROUP :: 0x1091
+LVM_SETGROUPINFO :: 0x1093
+LVM_GETGROUPINFO :: 0x1095
+LVM_REMOVEGROUP :: 0x1096
+LVM_MOVEGROUP :: 0x1097
+LVM_MOVEITEMTOGROUP :: 0x109a
+LVM_SETGROUPMETRICS :: 0x109b
+LVM_GETGROUPMETRICS :: 0x109c
+LVM_ENABLEGROUPVIEW :: 0x109d
+LVM_SORTGROUPS :: 0x109e
+LVM_INSERTGROUPSORTED :: 0x109f
+LVM_REMOVEALLGROUPS :: 0x10a0
+LVM_HASGROUP :: 0x10a1
+LVM_SETTILEVIEWINFO :: 0x10a2
+LVM_GETTILEVIEWINFO :: 0x10a3
+LVM_SETTILEINFO :: 0x10a4
+LVM_GETTILEINFO :: 0x10a5
+LVM_SETINSERTMARK :: 0x10a6
+LVM_GETINSERTMARK :: 0x10a7
+LVM_INSERTMARKHITTEST :: 0x10a8
+LVM_GETINSERTMARKRECT :: 0x10a9
+LVM_SETINSERTMARKCOLOR :: 0x10aa
+LVM_GETINSERTMARKCOLOR :: 0x10ab
+LVM_SETINFOTIP :: 0x10ad
+LVM_GETSELECTEDCOLUMN :: 0x10ae
+LVM_ISGROUPVIEWENABLED :: 0x10af
+LVM_GETOUTLINECOLOR :: 0x10b0
+LVM_SETOUTLINECOLOR :: 0x10b1
+LVM_CANCELEDITLABEL :: 0x10b3
+LVM_MAPINDEXTOID :: 0x10b4
+LVM_MAPIDTOINDEX :: 0x10b5
+LVM_ISITEMVISIBLE :: 0x10b6
+LVM_GETEMPTYTEXT :: 0x10cc
+LVM_GETFOOTERRECT :: 0x10cd
+LVM_GETFOOTERINFO :: 0x10ce
+LVM_GETFOOTERITEMRECT :: 0x10cf
+LVM_GETFOOTERITEM :: 0x10d0
+LVM_GETITEMINDEXRECT :: 0x10d1
+LVM_SETITEMINDEXSTATE :: 0x10d2
+LVM_GETNEXTITEMINDEX :: 0x10d3
+OCM__BASE :: 0x2000
+LVM_SETUNICODEFORMAT :: 0x2005
+LVM_GETUNICODEFORMAT :: 0x2006
+OCM_CTLCOLOR :: 0x2019
+OCM_DRAWITEM :: 0x202b
+OCM_MEASUREITEM :: 0x202c
+OCM_DELETEITEM :: 0x202d
+OCM_VKEYTOITEM :: 0x202e
+OCM_CHARTOITEM :: 0x202f
+OCM_COMPAREITEM :: 0x2039
+OCM_NOTIFY :: 0x204e
+OCM_COMMAND :: 0x2111
+OCM_HSCROLL :: 0x2114
+OCM_VSCROLL :: 0x2115
+OCM_CTLCOLORMSGBOX :: 0x2132
+OCM_CTLCOLOREDIT :: 0x2133
+OCM_CTLCOLORLISTBOX :: 0x2134
+OCM_CTLCOLORBTN :: 0x2135
+OCM_CTLCOLORDLG :: 0x2136
+OCM_CTLCOLORSCROLLBAR :: 0x2137
+OCM_CTLCOLORSTATIC :: 0x2138
+OCM_PARENTNOTIFY :: 0x2210
+WM_APP :: 0x8000
+WM_RASDIALEVENT :: 0xcccd
diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin
new file mode 100644
index 000000000..7bd0bfe9f
--- /dev/null
+++ b/core/sys/windows/winerror.odin
@@ -0,0 +1,48 @@
+// +build windows
+package sys_windows
+
+ERROR_SUCCESS : DWORD : 0
+NO_ERROR :: 0
+SEC_E_OK : HRESULT : 0x00000000
+
+ERROR_INVALID_FUNCTION : DWORD : 1
+ERROR_FILE_NOT_FOUND : DWORD : 2
+ERROR_PATH_NOT_FOUND : DWORD : 3
+ERROR_ACCESS_DENIED : DWORD : 5
+ERROR_INVALID_HANDLE : DWORD : 6
+ERROR_NOT_ENOUGH_MEMORY : DWORD : 8
+ERROR_INVALID_BLOCK : DWORD : 9
+ERROR_BAD_ENVIRONMENT : DWORD : 10
+ERROR_BAD_FORMAT : DWORD : 11
+ERROR_INVALID_ACCESS : DWORD : 12
+ERROR_INVALID_DATA : DWORD : 13
+ERROR_OUTOFMEMORY : DWORD : 14
+ERROR_INVALID_DRIVE : DWORD : 15
+ERROR_CURRENT_DIRECTORY : DWORD : 16
+ERROR_NO_MORE_FILES : DWORD : 18
+ERROR_SHARING_VIOLATION : DWORD : 32
+ERROR_LOCK_VIOLATION : DWORD : 33
+ERROR_HANDLE_EOF : DWORD : 38
+ERROR_NOT_SUPPORTED : DWORD : 50
+ERROR_FILE_EXISTS : DWORD : 80
+ERROR_INVALID_PARAMETER : DWORD : 87
+ERROR_BROKEN_PIPE : DWORD : 109
+ERROR_CALL_NOT_IMPLEMENTED : DWORD : 120
+ERROR_INSUFFICIENT_BUFFER : DWORD : 122
+ERROR_INVALID_NAME : DWORD : 123
+ERROR_BAD_ARGUMENTS : DWORD : 160
+ERROR_LOCK_FAILED : DWORD : 167
+ERROR_ALREADY_EXISTS : DWORD : 183
+ERROR_NO_DATA : DWORD : 232
+ERROR_ENVVAR_NOT_FOUND : DWORD : 203
+ERROR_OPERATION_ABORTED : DWORD : 995
+ERROR_IO_PENDING : DWORD : 997
+ERROR_NO_UNICODE_TRANSLATION : DWORD : 1113
+ERROR_TIMEOUT : DWORD : 1460
+ERROR_DATATYPE_MISMATCH : DWORD : 1629
+ERROR_UNSUPPORTED_TYPE : DWORD : 1630
+ERROR_NOT_SAME_OBJECT : DWORD : 1656
+
+E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
+
+SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 }
diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin
new file mode 100644
index 000000000..17f4d8e86
--- /dev/null
+++ b/core/sys/windows/winmm.odin
@@ -0,0 +1,10 @@
+// +build windows
+package sys_windows
+
+foreign import winmm "system:Winmm.lib"
+
+@(default_calling_convention="stdcall")
+foreign winmm {
+ timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
+ timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
+}
diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin
index 0cff5c2da..09af86bce 100644
--- a/core/sys/windows/ws2_32.odin
+++ b/core/sys/windows/ws2_32.odin
@@ -87,6 +87,19 @@ foreign ws2_32 {
res: ^^ADDRINFOA,
) -> c_int ---
freeaddrinfo :: proc(res: ^ADDRINFOA) ---
+ FreeAddrInfoExW :: proc(pAddrInfoEx: PADDRINFOEXW) ---
+ GetAddrInfoExW :: proc(
+ pName: PCWSTR,
+ pServiceName: PCWSTR,
+ dwNameSpace: DWORD,
+ lpNspId: LPGUID,
+ hints: ^ADDRINFOEXW,
+ ppResult: ^PADDRINFOEXW,
+ timeout: ^timeval,
+ lpOverlapped: LPOVERLAPPED,
+ lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
+ lpHandle: LPHANDLE) -> INT ---
+
select :: proc(
nfds: c_int,
readfds: ^fd_set,
diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin
index 3978a3c83..f3271d209 100644
--- a/core/testing/runner_other.odin
+++ b/core/testing/runner_other.odin
@@ -6,7 +6,7 @@ import "core:time"
run_internal_test :: proc(t: ^T, it: Internal_Test) {
// TODO(bill): Catch panics on other platforms
- it.p(t);
+ it.p(t)
}
_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin
index 560f23956..525eae685 100644
--- a/core/testing/runner_windows.odin
+++ b/core/testing/runner_windows.odin
@@ -21,7 +21,7 @@ sema_wait :: proc "contextless" (s: ^Sema) {
win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE)
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
@@ -46,7 +46,7 @@ sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration)
}
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}
@@ -203,7 +203,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) {
thread.it.p(t)
sema_post(&global_fail_timeout_semaphore)
- thread_join_and_destroy(global_fail_timeout_thread)
+ if global_fail_timeout_thread != nil do thread_join_and_destroy(global_fail_timeout_thread)
thread.success = true
sema_post(&global_threaded_runner_semaphore)
diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin
new file mode 100644
index 000000000..cff1ce11f
--- /dev/null
+++ b/core/text/i18n/doc.odin
@@ -0,0 +1,111 @@
+//+ignore
+package i18n
+
+/*
+ The i18n package is flexible and easy to use.
+
+ It has one call to get a translation: `get`, which the user can alias into something like `T`.
+
+ `get`, referred to as `T` here, has a few different signatures.
+ All of them will return the key if the entry can't be found in the active translation catalog.
+
+ - `T(key)` returns the translation of `key`.
+ - `T(key, n)` returns a pluralized translation of `key` according to value `n`.
+
+ - `T(section, key)` returns the translation of `key` in `section`.
+ - `T(section, key, n)` returns a pluralized translation of `key` in `section` according to value `n`.
+
+ By default lookup take place in the global `i18n.ACTIVE` catalog for ease of use.
+ If you want to override which translation to use, for example in a language preview dialog, you can use the following:
+
+ - `T(key, n, catalog)` returns the pluralized version of `key` from explictly supplied catalog.
+ - `T(section, key, n, catalog)` returns the pluralized version of `key` in `section` from explictly supplied catalog.
+
+ If a catalog has translation contexts or sections, then ommitting it in the above calls looks up in section "".
+
+ The default pluralization rule is n != 1, which is to say that passing n == 1 (or not passing n) returns the singular form.
+ Passing n != 1 returns plural form 1.
+
+ Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser.
+ This is a procedure that maps an integer to an integer, taking a value and returning which plural slot should be used.
+
+ You can also assign it to a loaded catalog after parsing, of course.
+
+ Some code examples follow.
+*/
+
+/*
+```cpp
+import "core:fmt"
+import "core:text/i18n"
+
+T :: i18n.get
+
+mo :: proc() {
+ using fmt
+
+ err: i18n.Error
+
+ /*
+ Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter.
+ */
+ i18n.ACTIVE, err = i18n.parse_mo(#load("translations/nl_NL.mo"))
+ defer i18n.destroy()
+
+ if err != .None { return }
+
+ /*
+ These are in the .MO catalog.
+ */
+ println("-----")
+ println(T(""))
+ println("-----")
+ println(T("There are 69,105 leaves here."))
+ println("-----")
+ println(T("Hellope, World!"))
+ println("-----")
+ // We pass 1 into `T` to get the singular format string, then 1 again into printf.
+ printf(T("There is %d leaf.\n", 1), 1)
+ // We pass 42 into `T` to get the plural format string, then 42 again into printf.
+ printf(T("There is %d leaf.\n", 42), 42)
+
+ /*
+ This isn't in the translation catalog, so the key is passed back untranslated.
+ */
+ println("-----")
+ println(T("Come visit us on Discord!"))
+}
+
+qt :: proc() {
+ using fmt
+
+ err: i18n.Error
+
+ /*
+ Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter.
+ */
+ i18n.ACTIVE, err = i18n.parse_qt(#load("translations/nl_NL-qt-ts.ts"))
+ defer i18n.destroy()
+
+ if err != .None {
+ return
+ }
+
+ /*
+ These are in the .TS catalog. As you can see they have sections.
+ */
+ println("--- Page section ---")
+ println("Page:Text for translation =", T("Page", "Text for translation"))
+ println("-----")
+ println("Page:Also text to translate =", T("Page", "Also text to translate"))
+ println("-----")
+ println("--- installscript section ---")
+ println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall"))
+ println("-----")
+ println("--- apple_count section ---")
+ println("apple_count:%d apple(s) =")
+ println("\t 1 =", T("apple_count", "%d apple(s)", 1))
+ println("\t 42 =", T("apple_count", "%d apple(s)", 42))
+}
+```
+*/
\ No newline at end of file
diff --git a/core/text/i18n/gettext.odin b/core/text/i18n/gettext.odin
new file mode 100644
index 000000000..d99ec1c9b
--- /dev/null
+++ b/core/text/i18n/gettext.odin
@@ -0,0 +1,167 @@
+package i18n
+/*
+ A parser for GNU GetText .MO files.
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ A from-scratch implementation based after the specification found here:
+ https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
+
+ Options are ignored as they're not applicable to this format.
+ They're part of the signature for consistency with other catalog formats.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+import "core:os"
+import "core:strings"
+import "core:bytes"
+
+parse_mo_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
+ context.allocator = allocator
+ /*
+ An MO file should have at least a 4-byte magic, 2 x 2 byte version info,
+ a 4-byte number of strings value, and 2 x 4-byte offsets.
+ */
+ if len(data) < 20 {
+ return {}, .MO_File_Invalid
+ }
+
+ /*
+ Check magic. Should be 0x950412de in native Endianness.
+ */
+ native := true
+ magic := read_u32(data, native) or_return
+
+ if magic != 0x950412de {
+ native = false
+ magic = read_u32(data, native) or_return
+
+ if magic != 0x950412de { return {}, .MO_File_Invalid_Signature }
+ }
+
+ /*
+ We can ignore version_minor at offset 6.
+ */
+ version_major := read_u16(data[4:]) or_return
+ if version_major > 1 { return {}, .MO_File_Unsupported_Version }
+
+ count := read_u32(data[ 8:]) or_return
+ original_offset := read_u32(data[12:]) or_return
+ translated_offset := read_u32(data[16:]) or_return
+
+ if count == 0 { return {}, .Empty_Translation_Catalog }
+
+ /*
+ Initalize Translation, interner and optional pluralizer.
+ */
+ translation = new(Translation)
+ translation.pluralize = pluralizer
+ strings.intern_init(&translation.intern, allocator, allocator)
+
+ // Gettext MO files only have one section.
+ translation.k_v[""] = {}
+ section := &translation.k_v[""]
+
+ for n := u32(0); n < count; n += 1 {
+ /*
+ Grab string's original length and offset.
+ */
+ offset := original_offset + 8 * n
+ if len(data) < int(offset + 8) { return translation, .MO_File_Invalid }
+
+ o_length := read_u32(data[offset :], native) or_return
+ o_offset := read_u32(data[offset + 4:], native) or_return
+
+ offset = translated_offset + 8 * n
+ if len(data) < int(offset + 8) { return translation, .MO_File_Invalid }
+
+ t_length := read_u32(data[offset :], native) or_return
+ t_offset := read_u32(data[offset + 4:], native) or_return
+
+ max_offset := int(max(o_offset + o_length + 1, t_offset + t_length + 1))
+ if len(data) < max_offset { return translation, .Premature_EOF }
+
+ key := data[o_offset:][:o_length]
+ val := data[t_offset:][:t_length]
+
+ /*
+ Could be a pluralized string.
+ */
+ zero := []byte{0}
+
+ keys := bytes.split(key, zero)
+ vals := bytes.split(val, zero)
+
+ if len(keys) != len(vals) || max(len(keys), len(vals)) > MAX_PLURALS {
+ return translation, .MO_File_Incorrect_Plural_Count
+ }
+
+ for k in keys {
+ interned_key := strings.intern_get(&translation.intern, string(k))
+
+ interned_vals := make([]string, len(keys))
+ last_val: string
+
+ i := 0
+ for v in vals {
+ interned_vals[i] = strings.intern_get(&translation.intern, string(v))
+ last_val = interned_vals[i]
+ i += 1
+ }
+ section[interned_key] = interned_vals
+ }
+ delete(vals)
+ delete(keys)
+ }
+ return
+}
+
+parse_mo_file :: proc(filename: string, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
+ context.allocator = allocator
+
+ data, data_ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if !data_ok { return {}, .File_Error }
+
+ return parse_mo_from_bytes(data, options, pluralizer, allocator)
+}
+
+parse_mo :: proc { parse_mo_file, parse_mo_from_bytes }
+
+/*
+ Helpers.
+*/
+read_u32 :: proc(data: []u8, native_endian := true) -> (res: u32, err: Error) {
+ if len(data) < size_of(u32) { return 0, .Premature_EOF }
+
+ val := (^u32)(raw_data(data))^
+
+ if native_endian {
+ return val, .None
+ } else {
+ when ODIN_ENDIAN == .Little {
+ return u32(transmute(u32be)val), .None
+ } else {
+ return u32(transmute(u32le)val), .None
+ }
+ }
+}
+
+read_u16 :: proc(data: []u8, native_endian := true) -> (res: u16, err: Error) {
+ if len(data) < size_of(u16) { return 0, .Premature_EOF }
+
+ val := (^u16)(raw_data(data))^
+
+ if native_endian {
+ return val, .None
+ } else {
+ when ODIN_ENDIAN == .Little {
+ return u16(transmute(u16be)val), .None
+ } else {
+ return u16(transmute(u16le)val), .None
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin
new file mode 100644
index 000000000..9d030db16
--- /dev/null
+++ b/core/text/i18n/i18n.odin
@@ -0,0 +1,182 @@
+package i18n
+/*
+ Internationalization helpers.
+
+ Copyright 2021-2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+import "core:strings"
+
+/*
+ TODO:
+ - Support for more translation catalog file formats.
+*/
+
+/*
+ Currently active catalog.
+*/
+ACTIVE: ^Translation
+
+// Allow between 1 and 255 plural forms. Default: 10.
+MAX_PLURALS :: min(max(#config(ODIN_i18N_MAX_PLURAL_FORMS, 10), 1), 255)
+
+/*
+ The main data structure. This can be generated from various different file formats, as long as we have a parser for them.
+*/
+
+Section :: map[string][]string
+
+Translation :: struct {
+ k_v: map[string]Section, // k_v[section][key][plural_form] = ...
+ intern: strings.Intern,
+
+ pluralize: proc(number: int) -> int,
+}
+
+Error :: enum {
+ /*
+ General return values.
+ */
+ None = 0,
+ Empty_Translation_Catalog,
+ Duplicate_Key,
+
+ /*
+ Couldn't find, open or read file.
+ */
+ File_Error,
+
+ /*
+ File too short.
+ */
+ Premature_EOF,
+
+ /*
+ GNU Gettext *.MO file errors.
+ */
+ MO_File_Invalid_Signature,
+ MO_File_Unsupported_Version,
+ MO_File_Invalid,
+ MO_File_Incorrect_Plural_Count,
+
+ /*
+ Qt Linguist *.TS file errors.
+ */
+ TS_File_Parse_Error,
+ TS_File_Expected_Context,
+ TS_File_Expected_Context_Name,
+ TS_File_Expected_Source,
+ TS_File_Expected_Translation,
+ TS_File_Expected_NumerusForm,
+
+}
+
+Parse_Options :: struct {
+ merge_sections: bool,
+}
+
+DEFAULT_PARSE_OPTIONS :: Parse_Options{
+ merge_sections = false,
+}
+
+/*
+ Several ways to use:
+ - get(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get(key, number), which returns the appropriate plural from the active catalog, or
+ - get(key, number, catalog) to grab text from a specific one.
+*/
+get_single_section :: proc(key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ /*
+ A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
+ */
+ plural := 1 if number != 1 else 0
+
+ if catalog.pluralize != nil {
+ plural = catalog.pluralize(number)
+ }
+ return get_by_slot(key, plural, catalog)
+}
+
+/*
+ Several ways to use:
+ - get(section, key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get(section, key, number), which returns the appropriate plural from the active catalog, or
+ - get(section, key, number, catalog) to grab text from a specific one.
+*/
+get_by_section :: proc(section, key: string, number := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ /*
+ A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
+ */
+ plural := 1 if number != 1 else 0
+
+ if catalog.pluralize != nil {
+ plural = catalog.pluralize(number)
+ }
+ return get_by_slot(section, key, plural, catalog)
+}
+get :: proc{get_single_section, get_by_section}
+
+/*
+ Several ways to use:
+ - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get_by_slot(key, slot), which returns the requested plural from the active catalog, or
+ - get_by_slot(key, slot, catalog) to grab text from a specific one.
+
+ If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+*/
+get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ return get_by_slot_by_section("", key, slot, catalog)
+}
+
+/*
+ Several ways to use:
+ - get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+ - get_by_slot(key, slot), which returns the requested plural from the active catalog, or
+ - get_by_slot(key, slot, catalog) to grab text from a specific one.
+
+ If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+*/
+get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+ if catalog == nil || section not_in catalog.k_v {
+ /*
+ Return the key if the catalog catalog hasn't been initialized yet, or the section is not present.
+ */
+ return key
+ }
+
+ /*
+ Return the translation from the requested slot if this key is known, else return the key.
+ */
+ if translations, ok := catalog.k_v[section][key]; ok {
+ plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1)
+ return translations[plural]
+ }
+ return key
+}
+get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section}
+
+/*
+ Same for destroy:
+ - destroy(), to clean up the currently active catalog catalog i18n.ACTIVE
+ - destroy(catalog), to clean up a specific catalog.
+*/
+destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator) {
+ context.allocator = allocator
+
+ if catalog == nil {
+ return
+ }
+
+ for section in &catalog.k_v {
+ for key in &catalog.k_v[section] {
+ delete(catalog.k_v[section][key])
+ }
+ delete(catalog.k_v[section])
+ }
+ delete(catalog.k_v)
+ strings.intern_destroy(&catalog.intern)
+ free(catalog)
+}
\ No newline at end of file
diff --git a/core/text/i18n/qt_linguist.odin b/core/text/i18n/qt_linguist.odin
new file mode 100644
index 000000000..036a89eeb
--- /dev/null
+++ b/core/text/i18n/qt_linguist.odin
@@ -0,0 +1,156 @@
+package i18n
+/*
+ A parser for Qt Linguist TS files.
+
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ A from-scratch implementation based after the specification found here:
+ https://doc.qt.io/qt-5/linguist-ts-file-format.html
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+import "core:os"
+import "core:encoding/xml"
+import "core:strings"
+
+TS_XML_Options := xml.Options{
+ flags = {
+ .Input_May_Be_Modified,
+ .Must_Have_Prolog,
+ .Must_Have_DocType,
+ .Ignore_Unsupported,
+ .Unbox_CDATA,
+ .Decode_SGML_Entities,
+ },
+ expected_doctype = "TS",
+}
+
+parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
+ context.allocator = allocator
+
+ ts, xml_err := xml.parse(data, TS_XML_Options)
+ defer xml.destroy(ts)
+
+ if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].children) == 0 {
+ return nil, .TS_File_Parse_Error
+ }
+
+ /*
+ Initalize Translation, interner and optional pluralizer.
+ */
+ translation = new(Translation)
+ translation.pluralize = pluralizer
+ strings.intern_init(&translation.intern, allocator, allocator)
+
+ section: ^Section
+
+ for child_id in ts.elements[0].children {
+ // These should be s.
+ child := ts.elements[child_id]
+ if child.ident != "context" {
+ return translation, .TS_File_Expected_Context
+ }
+
+ // Find section name.
+ section_name_id, section_name_found := xml.find_child_by_ident(ts, child_id, "name")
+ if !section_name_found {
+ return translation, .TS_File_Expected_Context_Name,
+ }
+
+ section_name := strings.intern_get(&translation.intern, "")
+ if !options.merge_sections {
+ section_name = strings.intern_get(&translation.intern, ts.elements[section_name_id].value)
+ }
+
+ if section_name not_in translation.k_v {
+ translation.k_v[section_name] = {}
+ }
+ section = &translation.k_v[section_name]
+
+ // Find messages in section.
+ nth: int
+ for {
+ message_id, message_found := xml.find_child_by_ident(ts, child_id, "message", nth)
+ if !message_found {
+ break
+ }
+
+ numerus_tag, _ := xml.find_attribute_val_by_key(ts, message_id, "numerus")
+ has_plurals := numerus_tag == "yes"
+
+ // We must have a = key
+ source_id, source_found := xml.find_child_by_ident(ts, message_id, "source")
+ if !source_found {
+ return translation, .TS_File_Expected_Source
+ }
+
+ // We must have a
+ translation_id, translation_found := xml.find_child_by_ident(ts, message_id, "translation")
+ if !translation_found {
+ return translation, .TS_File_Expected_Translation
+ }
+
+ source := strings.intern_get(&translation.intern, ts.elements[source_id].value)
+ xlat := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
+
+ if source in section {
+ return translation, .Duplicate_Key
+ }
+
+ if has_plurals {
+ if xlat != "" {
+ return translation, .TS_File_Expected_NumerusForm
+ }
+
+ num_plurals: int
+ for {
+ numerus_id, numerus_found := xml.find_child_by_ident(ts, translation_id, "numerusform", num_plurals)
+ if !numerus_found {
+ break
+ }
+ num_plurals += 1
+ }
+
+ if num_plurals < 2 {
+ return translation, .TS_File_Expected_NumerusForm
+ }
+ section[source] = make([]string, num_plurals)
+
+ num_plurals = 0
+ for {
+ numerus_id, numerus_found := xml.find_child_by_ident(ts, translation_id, "numerusform", num_plurals)
+ if !numerus_found {
+ break
+ }
+ numerus := strings.intern_get(&translation.intern, ts.elements[numerus_id].value)
+ section[source][num_plurals] = numerus
+
+ num_plurals += 1
+ }
+ } else {
+ // Single translation
+ section[source] = make([]string, 1)
+ section[source][0] = xlat
+ }
+
+ nth += 1
+ }
+ }
+
+ return
+}
+
+parse_qt_linguist_file :: proc(filename: string, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
+ context.allocator = allocator
+
+ data, data_ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if !data_ok { return {}, .File_Error }
+
+ return parse_qt_linguist_from_bytes(data, options, pluralizer, allocator)
+}
+
+parse_qt :: proc { parse_qt_linguist_file, parse_qt_linguist_from_bytes }
\ No newline at end of file
diff --git a/core/thread/thread.odin b/core/thread/thread.odin
index d1b95a2fd..90230ae75 100644
--- a/core/thread/thread.odin
+++ b/core/thread/thread.odin
@@ -53,7 +53,7 @@ join :: proc(thread: ^Thread) {
}
-join_mulitple :: proc(threads: ..^Thread) {
+join_multiple :: proc(threads: ..^Thread) {
_join_multiple(..threads)
}
diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin
index 37ee4fa98..820de8ad4 100644
--- a/core/thread/thread_pool.odin
+++ b/core/thread/thread_pool.odin
@@ -1,67 +1,76 @@
package thread
+/*
+ thread.Pool
+ Copyright 2022 eisbehr
+ Made available under Odin's BSD-3 license.
+*/
+
import "core:intrinsics"
import "core:sync"
import "core:mem"
-Task_Status :: enum i32 {
- Ready,
- Busy,
- Waiting,
- Term,
-}
-
-Task_Proc :: #type proc(task: ^Task)
+Task_Proc :: #type proc(task: Task)
Task :: struct {
- procedure: Task_Proc,
- data: rawptr,
+ procedure: Task_Proc,
+ data: rawptr,
user_index: int,
+ allocator: mem.Allocator,
}
-Task_Id :: distinct i32
-INVALID_TASK_ID :: Task_Id(-1)
-
-
+// Do not access the pool's members directly while the pool threads are running,
+// since they use different kinds of locking and mutual exclusion devices.
+// Careless access can and will lead to nasty bugs. Once initialized, the
+// pool's memory address is not allowed to change until it is destroyed.
Pool :: struct {
- allocator: mem.Allocator,
- mutex: sync.Mutex,
- sem_available: sync.Semaphore,
- processing_task_count: int, // atomic
- is_running: bool,
+ allocator: mem.Allocator,
+ mutex: sync.Mutex,
+ sem_available: sync.Sema,
+
+ // the following values are atomic
+ num_waiting: int,
+ num_in_processing: int,
+ num_outstanding: int, // num_waiting + num_in_processing
+ num_done: int,
+ // end of atomics
+
+ is_running: bool,
threads: []^Thread,
- tasks: [dynamic]Task,
+
+ tasks: [dynamic]Task,
+ tasks_done: [dynamic]Task,
}
-pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
- worker_thread_internal :: proc(t: ^Thread) {
- pool := (^Pool)(t.data)
-
- for pool.is_running {
- sync.semaphore_wait_for(&pool.sem_available)
-
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
- }
-
- sync.semaphore_post(&pool.sem_available, 1)
- }
-
-
+// Once initialized, the pool's memory address is not allowed to change until
+// it is destroyed.
+//
+// The thread pool requires an allocator which it either owns, or which is thread safe.
+pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
context.allocator = allocator
pool.allocator = allocator
- pool.tasks = make([dynamic]Task)
- pool.threads = make([]^Thread, thread_count)
+ pool.tasks = make([dynamic]Task)
+ pool.tasks_done = make([dynamic]Task)
+ pool.threads = make([]^Thread, max(thread_count, 1))
- sync.mutex_init(&pool.mutex)
- sync.semaphore_init(&pool.sem_available)
pool.is_running = true
for _, i in pool.threads {
- t := create(worker_thread_internal)
+ t := create(proc(t: ^Thread) {
+ pool := (^Pool)(t.data)
+
+ for intrinsics.atomic_load(&pool.is_running) {
+ sync.wait(&pool.sem_available)
+
+ if task, ok := pool_pop_waiting(pool); ok {
+ pool_do_work(pool, task)
+ }
+ }
+
+ sync.post(&pool.sem_available, 1)
+ })
t.user_index = i
t.data = pool
pool.threads[i] = t
@@ -70,15 +79,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks)
+ delete(pool.tasks_done)
- for thread in &pool.threads {
- destroy(thread)
+ for t in &pool.threads {
+ destroy(t)
}
delete(pool.threads, pool.allocator)
-
- sync.mutex_destroy(&pool.mutex)
- sync.semaphore_destroy(&pool.sem_available)
}
pool_start :: proc(pool: ^Pool) {
@@ -87,65 +94,134 @@ pool_start :: proc(pool: ^Pool) {
}
}
+// Finish tasks that have already started processing, then shut down all pool
+// threads. Might leave over waiting tasks, any memory allocated for the
+// user data of those tasks will not be freed.
pool_join :: proc(pool: ^Pool) {
- pool.is_running = false
-
- sync.semaphore_post(&pool.sem_available, len(pool.threads))
+ intrinsics.atomic_store(&pool.is_running, false)
+ sync.post(&pool.sem_available, len(pool.threads))
yield()
- for t in pool.threads {
- join(t)
- }
-}
-
-pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
- sync.mutex_lock(&pool.mutex)
- defer sync.mutex_unlock(&pool.mutex)
-
- task: Task
- task.procedure = procedure
- task.data = data
- task.user_index = user_index
-
- append(&pool.tasks, task)
- sync.semaphore_post(&pool.sem_available, 1)
-}
-
-pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
- if sync.mutex_try_lock(&pool.mutex) {
- if len(pool.tasks) != 0 {
- intrinsics.atomic_add(&pool.processing_task_count, 1)
- task = pop_front(&pool.tasks)
- got_task = true
+started_count: int
+ for started_count < len(pool.threads) {
+ started_count = 0
+ for t in pool.threads {
+ if .Started in t.flags {
+ started_count += 1
+ if .Joined not_in t.flags {
+ join(t)
+ }
+ }
}
- sync.mutex_unlock(&pool.mutex)
}
+}
+
+// Add a task to the thread pool.
+//
+// Tasks can be added from any thread, not just the thread that created
+// the thread pool. You can even add tasks from inside other tasks.
+//
+// Each task also needs an allocator which it either owns, or which is thread
+// safe.
+pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
+ sync.guard(&pool.mutex)
+
+ append(&pool.tasks, Task{
+ procedure = procedure,
+ data = data,
+ user_index = user_index,
+ allocator = allocator,
+ })
+ intrinsics.atomic_add(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_outstanding, 1)
+ sync.post(&pool.sem_available, 1)
+}
+
+// Number of tasks waiting to be processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_waiting)
+}
+
+// Number of tasks currently being processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_in_processing)
+}
+
+// Outstanding tasks are all tasks that are not done, that is, tasks that are
+// waiting, as well as tasks that are currently being processed. Only
+// informational, mostly for debugging. Don't rely on this value being
+// consistent with other num_* values.
+pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_outstanding)
+}
+
+// Number of tasks which are done processing. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_done :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_done)
+}
+
+// If tasks are only being added from one thread, and this procedure is being
+// called from that same thread, it will reliably tell if the thread pool is
+// empty or not. Empty in this case means there are no tasks waiting, being
+// processed, or _done_.
+pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
+ return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0
+}
+
+// Mostly for internal use.
+pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
+
+ if len(pool.tasks) != 0 {
+ intrinsics.atomic_sub(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_in_processing, 1)
+ task = pop_front(&pool.tasks)
+ got_task = true
+ }
+
return
}
+// Use this to take out finished tasks.
+pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
-pool_do_work :: proc(pool: ^Pool, task: ^Task) {
- task.procedure(task)
- intrinsics.atomic_sub(&pool.processing_task_count, 1)
-}
-
-
-pool_wait_and_process :: proc(pool: ^Pool) {
- for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
-
- // Safety kick
- if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
- sync.mutex_lock(&pool.mutex)
- sync.semaphore_post(&pool.sem_available, len(pool.tasks))
- sync.mutex_unlock(&pool.mutex)
- }
-
- yield()
+ if len(pool.tasks_done) != 0 {
+ task = pop_front(&pool.tasks_done)
+ got_task = true
+ intrinsics.atomic_sub(&pool.num_done, 1)
}
+ return
+}
+
+// Mostly for internal use.
+pool_do_work :: proc(pool: ^Pool, task: Task) {
+ {
+ context.allocator = task.allocator
+ task.procedure(task)
+ }
+
+ sync.guard(&pool.mutex)
+
+ append(&pool.tasks_done, task)
+ intrinsics.atomic_add(&pool.num_done, 1)
+ intrinsics.atomic_sub(&pool.num_outstanding, 1)
+ intrinsics.atomic_sub(&pool.num_in_processing, 1)
+}
+
+// Process the rest of the tasks, also use this thread for processing, then join
+// all the pool threads.
+pool_finish :: proc(pool: ^Pool) {
+ for task in pool_pop_waiting(pool) {
+ pool_do_work(pool, task)
+ }
pool_join(pool)
}
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index cee278c7a..8c7058f17 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
// +private
package thread
@@ -7,30 +7,21 @@ import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
+CAS :: intrinsics.atomic_compare_exchange_strong
+
+Thread_State :: enum u8 {
+ Started,
+ Joined,
+ Done,
+}
+
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align 16 {
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
-
- // NOTE: pthread has a proc to query this, but it is marked
- // as non-portable ("np") so we do this instead.
- done: bool,
-
- // since libpthread doesn't seem to have a way to create a thread
- // in a suspended state, we have it wait on this gate, which we
- // signal to start it.
- // destroyed after thread is started.
- start_gate: sync.Condition,
- start_mutex: sync.Mutex,
-
- // if true, the thread has been started and the start_gate has been destroyed.
- started: bool,
-
- // NOTE: with pthreads, it is undefined behavior for multiple threads
- // to call join on the same thread at the same time.
- // this value is atomically updated to detect this.
- // See the comment in `join`.
- already_joined: bool,
+ cond: sync.Cond,
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
//
// Creates a thread which will run the given procedure.
@@ -38,26 +29,44 @@ Thread_Os_Specific :: struct #align 16 {
//
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
- context = runtime.default_context()
t := (^Thread)(t)
- sync.condition_wait_for(&t.start_gate)
- sync.condition_destroy(&t.start_gate)
- sync.mutex_destroy(&t.start_mutex)
- t.start_gate = {}
- t.start_mutex = {}
- context = t.init_context.? or_else runtime.default_context()
-
+ when ODIN_OS != .Darwin {
+ // We need to give the thread a moment to start up before we enable cancellation.
+ can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) == 0
+ }
+
+ context = runtime.default_context()
+
+ sync.lock(&t.mutex)
+
t.id = sync.current_thread_id()
- t.procedure(t)
- if t.init_context == nil {
- if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
- runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
+ for (.Started not_in t.flags) {
+ sync.wait(&t.cond, &t.mutex)
+ }
+
+ init_context := t.init_context
+ context = init_context.? or_else runtime.default_context()
+
+ when ODIN_OS != .Darwin {
+ // Enable thread's cancelability.
+ if can_set_thread_cancel_state {
+ unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
+ unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil)
}
}
- intrinsics.atomic_store(&t.done, true)
+ t.procedure(t)
+
+ intrinsics.atomic_store(&t.flags, t.flags + { .Done })
+
+ sync.unlock(&t.mutex)
+
+ if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
+ runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
+ }
+
return nil
}
@@ -76,9 +85,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return nil
}
thread.creation_allocator = context.allocator
-
- sync.mutex_init(&thread.start_mutex)
- sync.condition_init(&thread.start_gate, &thread.start_mutex)
// Set thread priority.
policy: i32
@@ -97,65 +103,42 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
res = unix.pthread_attr_setschedparam(&attrs, ¶ms)
assert(res == 0)
+ thread.procedure = procedure
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread, thread.creation_allocator)
-
- sync.condition_destroy(&thread.start_gate)
- sync.mutex_destroy(&thread.start_mutex)
return nil
}
- thread.procedure = procedure
-
return thread
}
_start :: proc(t: ^Thread) {
- if intrinsics.atomic_xchg(&t.started, true) {
- return
- }
- sync.condition_signal(&t.start_gate)
+ // sync.guard(&t.mutex)
+ t.flags += { .Started }
+ sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
- return intrinsics.atomic_load(&t.done)
+ return .Done in intrinsics.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
+ // sync.guard(&t.mutex)
+
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
- // if unix.pthread_self().x == t.unix_thread.x do return;
- // NOTE(tetra): It's apparently UB for multiple threads to join the same thread
- // at the same time.
- // If someone else already did, spin until the thread dies.
- // See note on `already_joined` field.
- // TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
- // sure it makes sense to need to join from multiple threads?
- if intrinsics.atomic_xchg(&t.already_joined, true) {
- for {
- if intrinsics.atomic_load(&t.done) {
- return
- }
- intrinsics.cpu_relax()
- }
- }
+ // Preserve other flags besides `.Joined`, like `.Started`.
+ unjoined := intrinsics.atomic_load(&t.flags) - {.Joined}
+ joined := unjoined + {.Joined}
- // NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
- // will just return 3 (ESRCH).
- // We do this instead because I don't know if there is a danger
- // that you may join a different thread from the one you called join on,
- // if the thread handle is reused.
- if intrinsics.atomic_load(&t.done) {
+ // Try to set `t.flags` from unjoined to joined. If it returns joined,
+ // it means the previous value had that flag set and we can return.
+ if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok {
return
}
-
- ret_val: rawptr
- _ = unix.pthread_join(t.unix_thread, &ret_val)
- if !intrinsics.atomic_load(&t.done) {
- panic("thread not done after join")
- }
+ unix.pthread_join(t.unix_thread, nil)
}
_join_multiple :: proc(threads: ..^Thread) {
@@ -164,18 +147,17 @@ _join_multiple :: proc(threads: ..^Thread) {
}
}
-
_destroy :: proc(t: ^Thread) {
_join(t)
- sync.condition_destroy(&t.start_gate)
- sync.mutex_destroy(&t.start_mutex)
t.unix_thread = {}
free(t, t.creation_allocator)
}
-
_terminate :: proc(t: ^Thread, exit_code: int) {
- // TODO(bill)
+ // `pthread_cancel` is unreliable on Darwin for unknown reasons.
+ when ODIN_OS != .Darwin {
+ unix.pthread_cancel(t.unix_thread)
+ }
}
_yield :: proc() {
diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin
index 428e241d0..68382444c 100644
--- a/core/thread/thread_windows.odin
+++ b/core/thread/thread_windows.odin
@@ -3,13 +3,21 @@
package thread
import "core:runtime"
-import sync "core:sync/sync2"
+import "core:intrinsics"
+import "core:sync"
import win32 "core:sys/windows"
+Thread_State :: enum u8 {
+ Started,
+ Joined,
+ Done,
+}
+
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
- done: bool, // see note in `is_done`
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
context = t.init_context.? or_else runtime.default_context()
t.id = sync.current_thread_id()
+
t.procedure(t)
+ intrinsics.atomic_store(&t.flags, t.flags + {.Done})
+
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
}
-
- sync.atomic_store(&t.done, true)
return 0
}
@@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return thread
}
-_start :: proc(thread: ^Thread) {
- win32.ResumeThread(thread.win32_thread)
+_start :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+ t.flags += {.Started}
+ win32.ResumeThread(t.win32_thread)
}
-_is_done :: proc(using thread: ^Thread) -> bool {
+_is_done :: proc(t: ^Thread) -> bool {
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
- return sync.atomic_load(&done)
+ return .Done in sync.atomic_load(&t.flags)
}
-_join :: proc(using thread: ^Thread) {
- if win32_thread != win32.INVALID_HANDLE {
- win32.WaitForSingleObject(win32_thread, win32.INFINITE)
- win32.CloseHandle(win32_thread)
- win32_thread = win32.INVALID_HANDLE
+_join :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+
+ if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE {
+ return
}
+
+ win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
+ win32.CloseHandle(t.win32_thread)
+ t.win32_thread = win32.INVALID_HANDLE
+
+ t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {
diff --git a/core/time/perf.odin b/core/time/perf.odin
index f49b57f5b..53406646f 100644
--- a/core/time/perf.odin
+++ b/core/time/perf.odin
@@ -1,6 +1,6 @@
package time
-import "core:mem"
+import "core:runtime"
Tick :: struct {
_nsec: i64, // relative amount
@@ -50,9 +50,9 @@ Benchmark_Error :: enum {
}
Benchmark_Options :: struct {
- setup: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error),
- bench: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error),
- teardown: #type proc(options: ^Benchmark_Options, allocator: mem.Allocator) -> (err: Benchmark_Error),
+ setup: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
+ bench: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
+ teardown: #type proc(options: ^Benchmark_Options, allocator: runtime.Allocator) -> (err: Benchmark_Error),
rounds: int,
bytes: int,
diff --git a/core/time/time.odin b/core/time/time.odin
index fddb09d85..6c6e47dc0 100644
--- a/core/time/time.odin
+++ b/core/time/time.odin
@@ -14,6 +14,8 @@ Hour :: 60 * Minute
MIN_DURATION :: Duration(-1 << 63)
MAX_DURATION :: Duration(1<<63 - 1)
+IS_SUPPORTED :: _IS_SUPPORTED
+
Time :: struct {
_nsec: i64, // zero is 1970-01-01 00:00:00
}
@@ -49,6 +51,14 @@ Stopwatch :: struct {
_accumulation: Duration,
}
+now :: proc "contextless" () -> Time {
+ return _now()
+}
+
+sleep :: proc "contextless" (d: Duration) {
+ _sleep(d)
+}
+
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
if !running {
_start_time = tick_now()
@@ -82,36 +92,36 @@ since :: proc(start: Time) -> Duration {
return diff(start, now())
}
-duration_nanoseconds :: proc(d: Duration) -> i64 {
+duration_nanoseconds :: proc "contextless" (d: Duration) -> i64 {
return i64(d)
}
-duration_microseconds :: proc(d: Duration) -> f64 {
+duration_microseconds :: proc "contextless" (d: Duration) -> f64 {
return duration_seconds(d) * 1e6
}
-duration_milliseconds :: proc(d: Duration) -> f64 {
+duration_milliseconds :: proc "contextless" (d: Duration) -> f64 {
return duration_seconds(d) * 1e3
}
-duration_seconds :: proc(d: Duration) -> f64 {
+duration_seconds :: proc "contextless" (d: Duration) -> f64 {
sec := d / Second
nsec := d % Second
return f64(sec) + f64(nsec)/1e9
}
-duration_minutes :: proc(d: Duration) -> f64 {
+duration_minutes :: proc "contextless" (d: Duration) -> f64 {
min := d / Minute
nsec := d % Minute
return f64(min) + f64(nsec)/(60*1e9)
}
-duration_hours :: proc(d: Duration) -> f64 {
+duration_hours :: proc "contextless" (d: Duration) -> f64 {
hour := d / Hour
nsec := d % Hour
return f64(hour) + f64(nsec)/(60*60*1e9)
}
-_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
- return u64(x)+u64(x) < u64(y)
-}
-
duration_round :: proc(d, m: Duration) -> Duration {
+ _less_than_half :: #force_inline proc(x, y: Duration) -> bool {
+ return u64(x)+u64(x) < u64(y)
+ }
+
if m <= 0 {
return d
}
@@ -201,10 +211,12 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
}
+to_unix_seconds :: time_to_unix
time_to_unix :: proc(t: Time) -> i64 {
return t._nsec/1e9
}
+to_unix_nanoseconds :: time_to_unix_nano
time_to_unix_nano :: proc(t: Time) -> i64 {
return t._nsec
}
@@ -213,6 +225,44 @@ time_add :: proc(t: Time, d: Duration) -> Time {
return Time{t._nsec + i64(d)}
}
+// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/
+//
+// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
+// On Windows it depends but is comparable with regular sleep in the worst case.
+// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
+// tell Windows to use a more accurate timer for your process.
+accurate_sleep :: proc(d: Duration) {
+ to_sleep, estimate, mean, m2, count: Duration
+
+ to_sleep = d
+ estimate = 5 * Millisecond
+ mean = 5 * Millisecond
+ count = 1
+
+ for to_sleep > estimate {
+ start := tick_now()
+ sleep(1 * Millisecond)
+
+ observed := tick_since(start)
+ to_sleep -= observed
+
+ count += 1
+
+ delta := observed - mean
+ mean += delta / count
+ m2 += delta * (observed - mean)
+ stddev := intrinsics.sqrt(f64(m2) / f64(count - 1))
+ estimate = mean + Duration(stddev)
+ }
+
+ start := tick_now()
+ for to_sleep > tick_since(start) {
+ // prevent the spinlock from taking the thread hostage, still accurate enough
+ _yield()
+ // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while
+ // TODO: needs actual testing done to check if that's the case
+ }
+}
ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations
ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY);
@@ -227,20 +277,24 @@ INTERNAL_TO_WALL :: -WALL_TO_INTERNAL
UNIX_TO_ABSOLUTE :: UNIX_TO_INTERNAL + INTERNAL_TO_ABSOLUTE
ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
-_is_leap_year :: proc(year: int) -> bool {
- return year%4 == 0 && (year%100 != 0 || year%400 == 0)
-}
+@(private)
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
year, month, day, yday = _abs_date(_time_abs(t), full)
return
}
+@(private)
_time_abs :: proc(t: Time) -> u64 {
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
}
+@(private)
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
+ _is_leap_year :: proc(year: int) -> bool {
+ return year%4 == 0 && (year%100 != 0 || year%400 == 0)
+ }
+
d := abs / SECONDS_PER_DAY
// 400 year cycles
diff --git a/core/time/time_essence.odin b/core/time/time_essence.odin
index 72efe9f8f..b7bc616d8 100644
--- a/core/time/time_essence.odin
+++ b/core/time/time_essence.odin
@@ -1,18 +1,19 @@
+//+private
package time
import "core:sys/es"
-IS_SUPPORTED :: true;
+_IS_SUPPORTED :: true
-now :: proc "contextless" () -> Time {
+_now :: proc "contextless" () -> Time {
// TODO Replace once there's a proper time API.
- return Time{_nsec = i64(es.TimeStampMs() * 1e6)};
+ return Time{_nsec = i64(es.TimeStampMs() * 1e6)}
}
-sleep :: proc "contextless" (d: Duration) {
- es.Sleep(u64(d/Millisecond));
+_sleep :: proc "contextless" (d: Duration) {
+ es.Sleep(u64(d/Millisecond))
}
_tick_now :: proc "contextless" () -> Tick {
- return Tick{_nsec = i64(es.TimeStampMs() * 1e6)};
+ return Tick{_nsec = i64(es.TimeStampMs() * 1e6)}
}
diff --git a/core/time/time_freestanding.odin b/core/time/time_freestanding.odin
index 17a21d79c..7c67cc5e8 100644
--- a/core/time/time_freestanding.odin
+++ b/core/time/time_freestanding.odin
@@ -1,16 +1,19 @@
+//+private
//+build freestanding
package time
-IS_SUPPORTED :: false
+_IS_SUPPORTED :: false
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
return {}
}
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
}
_tick_now :: proc "contextless" () -> Tick {
return {}
}
+_yield :: proc "contextless" () {
+}
diff --git a/core/time/time_js.odin b/core/time/time_js.odin
index 1b801cdad..226f921f9 100644
--- a/core/time/time_js.odin
+++ b/core/time/time_js.odin
@@ -1,21 +1,33 @@
+//+private
//+build js
package time
-IS_SUPPORTED :: false
+foreign import "odin_env"
-now :: proc() -> Time {
- return {}
+_IS_SUPPORTED :: true
+
+_now :: proc "contextless" () -> Time {
+ foreign odin_env {
+ time_now :: proc "contextless" () -> i64 ---
+ }
+ return Time{time_now()}
}
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
+ foreign odin_env {
+ time_sleep :: proc "contextless" (ms: u32) ---
+ }
+ if d > 0 {
+ time_sleep(u32(d/1e6))
+ }
}
_tick_now :: proc "contextless" () -> Tick {
- // mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
- // q := val / den
- // r := val % den
- // return q * num + r * num / den
- // }
- return {}
+ foreign odin_env {
+ tick_now :: proc "contextless" () -> i64 ---
+ }
+ return Tick{tick_now()}
}
+_yield :: proc "contextless" () {
+}
diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin
index 0d765b72d..ba0d91527 100644
--- a/core/time/time_unix.odin
+++ b/core/time/time_unix.odin
@@ -1,92 +1,34 @@
-//+build linux, darwin, freebsd
+//+private
+//+build linux, darwin, freebsd, openbsd
package time
-IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
+import "core:sys/unix"
-when ODIN_OS == "darwin" {
- foreign import libc "System.framework"
-} else {
- foreign import libc "system:c"
-}
+_IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
-
-@(default_calling_convention="c")
-foreign libc {
- @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 ---
- @(link_name="sleep") _unix_sleep :: proc(seconds: u32) -> i32 ---
- @(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---
-}
-
-TimeSpec :: struct {
- tv_sec : i64, /* seconds */
- tv_nsec : i64, /* nanoseconds */
-}
-
-CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
-CLOCK_PROCESS_CPUTIME_ID :: 2
-CLOCK_THREAD_CPUTIME_ID :: 3
-CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-CLOCK_MONOTONIC_COARSE :: 6
-CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-CLOCK_REALTIME_ALARM :: 8
-CLOCK_BOOTTIME_ALARM :: 9
-
-// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
-// I do not know if Darwin programmers are used to the existance of these constants or not, so
-// I'm leaving aliases to them for now.
-CLOCK_SYSTEM :: CLOCK_REALTIME
-CLOCK_CALENDAR :: CLOCK_MONOTONIC
-
-
-clock_gettime :: proc "contextless" (clock_id: u64) -> TimeSpec {
- ts : TimeSpec // NOTE(tetra): Do we need to initialize this?
- _unix_clock_gettime(clock_id, &ts)
- return ts
-}
-
-now :: proc() -> Time {
- time_spec_now := clock_gettime(CLOCK_REALTIME)
+_now :: proc "contextless" () -> Time {
+ time_spec_now: unix.timespec
+ unix.clock_gettime(unix.CLOCK_REALTIME, &time_spec_now)
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec
return Time{_nsec=ns}
}
-boot_time :: proc() -> Time {
- ts_now := clock_gettime(CLOCK_REALTIME)
- ts_boottime := clock_gettime(CLOCK_BOOTTIME)
-
- ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
- return Time{_nsec=ns}
-}
-
-seconds_since_boot :: proc() -> f64 {
- ts_boottime := clock_gettime(CLOCK_BOOTTIME)
- return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
-}
-
-
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
ds := duration_seconds(d)
seconds := u32(ds)
nanoseconds := i64((ds - f64(seconds)) * 1e9)
- if seconds > 0 { _unix_sleep(seconds) }
- if nanoseconds > 0 { nanosleep(nanoseconds) }
+ if seconds > 0 { unix.sleep(seconds) }
+ if nanoseconds > 0 { unix.inline_nanosleep(nanoseconds) }
}
-nanosleep :: proc(nanoseconds: i64) -> int {
- // NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all...
- assert(nanoseconds <= 999999999)
-
- requested := TimeSpec{tv_nsec = nanoseconds}
- remaining: TimeSpec // NOTE(tetra): Do we need to initialize this?
- return int(_unix_nanosleep(&requested, &remaining))
-}
-
-
_tick_now :: proc "contextless" () -> Tick {
- t := clock_gettime(CLOCK_MONOTONIC_RAW)
- _nsec := t.tv_sec*1e9 + t.tv_nsec
- return Tick{_nsec = _nsec}
+ t: unix.timespec
+ unix.clock_gettime(unix.CLOCK_MONOTONIC_RAW, &t)
+ return Tick{_nsec = t.tv_sec*1e9 + t.tv_nsec}
}
+
+_yield :: proc "contextless" () {
+ unix.sched_yield()
+}
+
diff --git a/core/time/time_wasi.odin b/core/time/time_wasi.odin
index 4a6c8afc0..9360e3591 100644
--- a/core/time/time_wasi.odin
+++ b/core/time/time_wasi.odin
@@ -1,15 +1,16 @@
+//+private
//+build wasi
package time
import wasi "core:sys/wasm/wasi"
-IS_SUPPORTED :: false
+_IS_SUPPORTED :: false
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
return {}
}
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
}
_tick_now :: proc "contextless" () -> Tick {
diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin
index 0fb9eaa0f..20863c323 100644
--- a/core/time/time_windows.odin
+++ b/core/time/time_windows.odin
@@ -1,22 +1,21 @@
+//+private
package time
import win32 "core:sys/windows"
-IS_SUPPORTED :: true
+_IS_SUPPORTED :: true
-now :: proc() -> Time {
+_now :: proc "contextless" () -> Time {
file_time: win32.FILETIME
win32.GetSystemTimeAsFileTime(&file_time)
ns := win32.FILETIME_as_unix_nanoseconds(file_time)
return Time{_nsec=ns}
}
-sleep :: proc(d: Duration) {
+_sleep :: proc "contextless" (d: Duration) {
win32.Sleep(win32.DWORD(d/Millisecond))
}
-
-
_tick_now :: proc "contextless" () -> Tick {
mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
q := val / den
@@ -35,3 +34,7 @@ _tick_now :: proc "contextless" () -> Tick {
_nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency))
return Tick{_nsec = _nsec}
}
+
+_yield :: proc "contextless" () {
+ win32.SwitchToThread()
+}
diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin
new file mode 100644
index 000000000..328ba9091
--- /dev/null
+++ b/core/unicode/tools/generate_entity_table.odin
@@ -0,0 +1,287 @@
+package xml_example
+
+import "core:encoding/xml"
+import "core:os"
+import "core:path"
+import "core:mem"
+import "core:strings"
+import "core:strconv"
+import "core:slice"
+import "core:fmt"
+
+/*
+ Silent error handler for the parser.
+*/
+Error_Handler :: proc(pos: xml.Pos, fmt: string, args: ..any) {}
+
+OPTIONS :: xml.Options{ flags = { .Ignore_Unsupported, }, expected_doctype = "unicode", }
+
+Entity :: struct {
+ name: string,
+ codepoint: rune,
+ description: string,
+}
+
+generate_encoding_entity_table :: proc() {
+ using fmt
+
+ filename := path.join(ODIN_ROOT, "tests", "core", "assets", "XML", "unicode.xml")
+ defer delete(filename)
+
+ generated_filename := path.join(ODIN_ROOT, "core", "encoding", "entity", "generated.odin")
+ defer delete(generated_filename)
+
+ doc, err := xml.parse(filename, OPTIONS, Error_Handler)
+ defer xml.destroy(doc)
+
+ if err != .None {
+ printf("Load/Parse error: %v\n", err)
+ if err == .File_Error {
+ printf("\"%v\" not found. Did you run \"tests\\download_assets.py\"?", filename)
+ }
+ os.exit(1)
+ }
+
+ printf("\"%v\" loaded and parsed.\n", filename)
+
+ generated_buf: strings.Builder
+ defer strings.builder_destroy(&generated_buf)
+ w := strings.to_writer(&generated_buf)
+
+ charlist, charlist_ok := xml.find_child_by_ident(doc.root, "charlist")
+ if !charlist_ok {
+ eprintln("Could not locate top-level `` tag.")
+ os.exit(1)
+ }
+
+ printf("Found `` with %v children.\n", len(charlist.children))
+
+ entity_map: map[string]Entity
+ names: [dynamic]string
+
+ min_name_length := max(int)
+ max_name_length := min(int)
+ shortest_name: string
+ longest_name: string
+
+ count := 0
+ for char in charlist.children {
+ if char.ident != "character" {
+ eprintf("Expected ``, got `<%v>`\n", char.ident)
+ os.exit(1)
+ }
+
+ if codepoint_string, ok := xml.find_attribute_val_by_key(char, "dec"); !ok {
+ eprintln("`` attribute not found.")
+ os.exit(1)
+ } else {
+ codepoint := strconv.atoi(codepoint_string)
+
+ desc, desc_ok := xml.find_child_by_ident(char, "description")
+ description := desc.value if desc_ok else ""
+
+ /*
+ For us to be interested in this codepoint, it has to have at least one entity.
+ */
+
+ nth := 0
+ for {
+ character_entity, entity_ok := xml.find_child_by_ident(char, "entity", nth)
+ if !entity_ok { break }
+
+ nth += 1
+ if name, name_ok := xml.find_attribute_val_by_key(character_entity, "id"); name_ok {
+
+ if len(name) == 0 {
+ /*
+ Invalid name. Skip.
+ */
+ continue
+ }
+
+ if name == "\"\"" {
+ printf("%#v\n", char)
+ printf("%#v\n", character_entity)
+ }
+
+ if len(name) > max_name_length { longest_name = name }
+ if len(name) < min_name_length { shortest_name = name }
+
+ min_name_length = min(min_name_length, len(name))
+ max_name_length = max(max_name_length, len(name))
+
+ e := Entity{
+ name = name,
+ codepoint = rune(codepoint),
+ description = description,
+ }
+
+ if _, seen := entity_map[name]; seen {
+ continue
+ }
+
+ entity_map[name] = e
+ append(&names, name)
+ count += 1
+ }
+ }
+ }
+ }
+
+ /*
+ Sort by name.
+ */
+ slice.sort(names[:])
+
+ printf("Found %v unique `&name;` -> rune mappings.\n", count)
+ printf("Shortest name: %v (%v)\n", shortest_name, min_name_length)
+ printf("Longest name: %v (%v)\n", longest_name, max_name_length)
+
+ // println(rune_to_string(1234))
+
+ /*
+ Generate table.
+ */
+ wprintln(w, "package unicode_entity")
+ wprintln(w, "")
+ wprintln(w, GENERATED)
+ wprintln(w, "")
+ wprintf (w, TABLE_FILE_PROLOG)
+ wprintln(w, "")
+
+ wprintf (w, "// `&%v;`\n", shortest_name)
+ wprintf (w, "XML_NAME_TO_RUNE_MIN_LENGTH :: %v\n", min_name_length)
+ wprintf (w, "// `&%v;`\n", longest_name)
+ wprintf (w, "XML_NAME_TO_RUNE_MAX_LENGTH :: %v\n", max_name_length)
+ wprintln(w, "")
+
+ wprintln(w,
+`
+/*
+ Input:
+ entity_name - a string, like "copy" that describes a user-encoded Unicode entity as used in XML.
+
+ Output:
+ "decoded" - The decoded rune if found by name, or -1 otherwise.
+ "ok" - true if found, false if not.
+
+ IMPORTANT: XML processors (including browsers) treat these names as case-sensitive. So do we.
+*/
+named_xml_entity_to_rune :: proc(name: string) -> (decoded: rune, ok: bool) {
+ /*
+ Early out if the name is too short or too long.
+ min as a precaution in case the generated table has a bogus value.
+ */
+ if len(name) < min(1, XML_NAME_TO_RUNE_MIN_LENGTH) || len(name) > XML_NAME_TO_RUNE_MAX_LENGTH {
+ return -1, false
+ }
+
+ switch rune(name[0]) {
+`)
+
+ prefix := '?'
+ should_close := false
+
+ for v in names {
+ if rune(v[0]) != prefix {
+ if should_close {
+ wprintln(w, "\t\t}\n")
+ }
+
+ prefix = rune(v[0])
+ wprintf (w, "\tcase '%v':\n", prefix)
+ wprintln(w, "\t\tswitch name {")
+ }
+
+ e := entity_map[v]
+
+ wprintf(w, "\t\t\tcase \"%v\": \n", e.name)
+ wprintf(w, "\t\t\t\t// %v\n", e.description)
+ wprintf(w, "\t\t\t\treturn %v, true\n", rune_to_string(e.codepoint))
+
+ should_close = true
+ }
+ wprintln(w, "\t\t}")
+ wprintln(w, "\t}")
+ wprintln(w, "\treturn -1, false")
+ wprintln(w, "}\n")
+ wprintln(w, GENERATED)
+
+ println()
+ println(strings.to_string(generated_buf))
+ println()
+
+ written := os.write_entire_file(generated_filename, transmute([]byte)strings.to_string(generated_buf))
+
+ if written {
+ fmt.printf("Successfully written generated \"%v\".", generated_filename)
+ } else {
+ fmt.printf("Failed to write generated \"%v\".", generated_filename)
+ }
+
+ delete(entity_map)
+ delete(names)
+ for name in &names {
+ free(&name)
+ }
+}
+
+GENERATED :: `/*
+ ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
+*/`
+
+TABLE_FILE_PROLOG :: `/*
+ This file is generated from "https://www.w3.org/2003/entities/2007xml/unicode.xml".
+
+ UPDATE:
+ - Ensure the XML file was downloaded using "tests\core\download_assets.py".
+ - Run "core/unicode/tools/generate_entity_table.odin"
+
+ Odin unicode generated tables: https://github.com/odin-lang/Odin/tree/master/core/encoding/entity
+
+ Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology,
+ European Research Consortium for Informatics and Mathematics, Keio University, Beihang).
+
+ All Rights Reserved.
+
+ This work is distributed under the W3C® Software License [1] in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ [1] http://www.w3.org/Consortium/Legal/copyright-software
+
+ See also: LICENSE_table.md
+*/
+`
+
+rune_to_string :: proc(r: rune) -> (res: string) {
+ res = fmt.tprintf("%08x", int(r))
+ for len(res) > 2 && res[:2] == "00" {
+ res = res[2:]
+ }
+ return fmt.tprintf("rune(0x%v)", res)
+}
+
+is_dotted_name :: proc(name: string) -> (dotted: bool) {
+ for r in name {
+ if r == '.' { return true}
+ }
+ return false
+}
+
+main :: proc() {
+ using fmt
+
+ track: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&track, context.allocator)
+ context.allocator = mem.tracking_allocator(&track)
+
+ generate_encoding_entity_table()
+
+ if len(track.allocation_map) > 0 {
+ println()
+ for _, v in track.allocation_map {
+ printf("%v Leaked %v bytes.\n", v.location, v.size)
+ }
+ }
+ println("Done and cleaned up!")
+}
\ No newline at end of file
diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin
index 380381f9a..6bdd6558a 100644
--- a/core/unicode/utf16/utf16.odin
+++ b/core/unicode/utf16/utf16.odin
@@ -1,5 +1,7 @@
package utf16
+import "core:unicode/utf8"
+
REPLACEMENT_CHAR :: '\ufffd'
MAX_RUNE :: '\U0010ffff'
@@ -80,3 +82,49 @@ encode_string :: proc(d: []u16, s: string) -> int {
}
return n
}
+
+decode :: proc(d: []rune, s: []u16) -> (n: int) {
+ for i := 0; i < len(s); i += 1 {
+ if n >= len(d) {
+ return
+ }
+
+ r := rune(REPLACEMENT_CHAR)
+
+ switch c := s[i]; {
+ case c < _surr1, _surr3 <= c:
+ r = rune(c)
+ case _surr1 <= c && c < _surr2 && i+1 < len(s) &&
+ _surr2 <= s[i+1] && s[i+1] < _surr3:
+ r = decode_surrogate_pair(rune(c), rune(s[i+1]))
+ i += 1
+ }
+ d[n] = r
+
+ n += 1
+ }
+ return
+}
+
+
+decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) {
+ for i := 0; i < len(s); i += 1 {
+ if n >= len(d) {
+ return
+ }
+ r := rune(REPLACEMENT_CHAR)
+
+ switch c := s[i]; {
+ case c < _surr1, _surr3 <= c:
+ r = rune(c)
+ case _surr1 <= c && c < _surr2 && i+1 < len(s) &&
+ _surr2 <= s[i+1] && s[i+1] < _surr3:
+ r = decode_surrogate_pair(rune(c), rune(s[i+1]))
+ i += 1
+ }
+
+ b, w := utf8.encode_rune(rune(r))
+ n += copy(d[n:], b[:w])
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin
index 6a04b0fe9..a0da5c5d1 100644
--- a/core/unicode/utf8/utf8.odin
+++ b/core/unicode/utf8/utf8.odin
@@ -90,10 +90,15 @@ encode_rune :: proc(c: rune) -> ([4]u8, int) {
return buf, 4
}
-decode_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
- return decode_rune(transmute([]u8)s)
+
+decode_rune :: proc{
+ decode_rune_in_string,
+ decode_rune_in_bytes,
}
-decode_rune :: proc(s: []u8) -> (rune, int) {
+decode_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
+ return decode_rune_in_bytes(transmute([]u8)s)
+}
+decode_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
n := len(s)
if n < 1 {
return RUNE_ERROR, 0
@@ -161,10 +166,15 @@ runes_to_string :: proc(runes: []rune, allocator := context.allocator) -> string
}
-decode_last_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
- return decode_last_rune(transmute([]u8)s)
+decode_last_rune :: proc{
+ decode_last_rune_in_string,
+ decode_last_rune_in_bytes,
}
-decode_last_rune :: proc(s: []u8) -> (rune, int) {
+
+decode_last_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
+ return decode_last_rune_in_bytes(transmute([]u8)s)
+}
+decode_last_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
r: rune
size: int
start, end, limit: int
@@ -297,10 +307,15 @@ rune_start :: #force_inline proc(b: u8) -> bool {
return b&0xc0 != 0x80
}
-rune_count_in_string :: #force_inline proc(s: string) -> int {
- return rune_count(transmute([]u8)s)
+rune_count :: proc{
+ rune_count_in_string,
+ rune_count_in_bytes,
}
-rune_count :: proc(s: []u8) -> int {
+
+rune_count_in_string :: #force_inline proc(s: string) -> int {
+ return rune_count_in_bytes(transmute([]u8)s)
+}
+rune_count_in_bytes :: proc(s: []u8) -> int {
count := 0
n := len(s)
@@ -353,7 +368,14 @@ rune_size :: proc(r: rune) -> int {
// full_rune reports if the bytes in b begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
-full_rune :: proc(b: []byte) -> bool {
+full_rune :: proc{
+ full_rune_in_bytes,
+ full_rune_in_string,
+}
+
+// full_rune_in_bytes reports if the bytes in b begin with a full utf-8 encoding of a rune or not
+// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
+full_rune_in_bytes :: proc(b: []byte) -> bool {
n := len(b)
if n == 0 {
return false
@@ -374,7 +396,7 @@ full_rune :: proc(b: []byte) -> bool {
// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
full_rune_in_string :: proc(s: string) -> bool {
- return full_rune(transmute([]byte)s)
+ return full_rune_in_bytes(transmute([]byte)s)
}
diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin
index 23e2eefc6..86267defb 100644
--- a/core/unicode/utf8/utf8string/string.odin
+++ b/core/unicode/utf8/utf8string/string.odin
@@ -16,140 +16,140 @@ String :: struct {
}
@(private)
-_len :: builtin.len; // helper procedure
+_len :: builtin.len // helper procedure
init :: proc(s: ^String, contents: string) -> ^String {
- s.contents = contents;
- s.byte_pos = 0;
- s.rune_pos = 0;
+ s.contents = contents
+ s.byte_pos = 0
+ s.rune_pos = 0
for i in 0..<_len(contents) {
if contents[i] >= utf8.RUNE_SELF {
- s.rune_count = utf8.rune_count_in_string(contents);
- _, s.width = utf8.decode_rune_in_string(contents);
- s.non_ascii = i;
- return s;
+ s.rune_count = utf8.rune_count_in_string(contents)
+ _, s.width = utf8.decode_rune_in_string(contents)
+ s.non_ascii = i
+ return s
}
}
- s.rune_count = _len(contents);
- s.width = 0;
- s.non_ascii = _len(contents);
- return s;
+ s.rune_count = _len(contents)
+ s.width = 0
+ s.non_ascii = _len(contents)
+ return s
}
to_string :: proc(s: ^String) -> string {
- return s.contents;
+ return s.contents
}
len :: proc(s: ^String) -> int {
- return s.rune_count;
+ return s.rune_count
}
is_ascii :: proc(s: ^String) -> bool {
- return s.width == 0;
+ return s.width == 0
}
at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
- runtime.bounds_check_error_loc(loc, i, s.rune_count);
+ runtime.bounds_check_error_loc(loc, i, s.rune_count)
if i < s.non_ascii {
- return rune(s.contents[i]);
+ return rune(s.contents[i])
}
switch i {
case 0:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = 0;
- s.byte_pos = 0;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = 0
+ s.byte_pos = 0
+ return
case s.rune_count-1:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = i;
- s.byte_pos = _len(s.contents) - s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = i
+ s.byte_pos = _len(s.contents) - s.width
+ return
case s.rune_pos-1:
- r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
- s.rune_pos = i;
- s.byte_pos -= s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos])
+ s.rune_pos = i
+ s.byte_pos -= s.width
+ return
case s.rune_pos+1:
- s.rune_pos = i;
- s.byte_pos += s.width;
- fallthrough;
+ s.rune_pos = i
+ s.byte_pos += s.width
+ fallthrough
case s.rune_pos:
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
+ return
}
// Linear scan
- scan_forward := true;
+ scan_forward := true
if i < s.rune_pos {
if i < (s.rune_pos-s.non_ascii)/2 {
- s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
+ s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii
} else {
- scan_forward = false;
+ scan_forward = false
}
} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
- // scan_forward = true;
+ // scan_forward = true
} else {
- s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
- scan_forward = false;
+ s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count
+ scan_forward = false
}
if scan_forward {
for {
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
if s.rune_pos == i {
- return;
+ return
}
- s.rune_pos += 1;
- s.byte_pos += s.width;
+ s.rune_pos += 1
+ s.byte_pos += s.width
}
} else {
for {
- r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
- s.rune_pos -= 1;
- s.byte_pos -= s.width;
+ r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos])
+ s.rune_pos -= 1
+ s.byte_pos -= s.width
if s.rune_pos == i {
- return;
+ return
}
}
}
}
slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
- runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
+ runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count)
if j < s.non_ascii {
- return s.contents[i:j];
+ return s.contents[i:j]
}
if i == j {
- return "";
+ return ""
}
- lo, hi: int;
+ lo, hi: int
if i < s.non_ascii {
- lo = i;
+ lo = i
} else if i == s.rune_count {
- lo = _len(s.contents);
+ lo = _len(s.contents)
} else {
- at(s, i, loc);
- lo = s.byte_pos;
+ at(s, i, loc)
+ lo = s.byte_pos
}
if j == s.rune_count {
- hi = _len(s.contents);
+ hi = _len(s.contents)
} else {
- at(s, j, loc);
- hi = s.byte_pos;
+ at(s, j, loc)
+ hi = s.byte_pos
}
- return s.contents[lo:hi];
+ return s.contents[lo:hi]
}
diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin
index a88cc273e..9f347fad3 100644
--- a/examples/all/all_main.odin
+++ b/examples/all/all_main.odin
@@ -5,25 +5,71 @@ package all
import bufio "core:bufio"
import bytes "core:bytes"
+
import c "core:c"
import libc "core:c/libc"
+
import compress "core:compress"
+import shoco "core:compress/shoco"
import gzip "core:compress/gzip"
import zlib "core:compress/zlib"
-import container "core:container"
+
+import bit_array "core:container/bit_array"
+import priority_queue "core:container/priority_queue"
+import queue "core:container/queue"
+import small_array "core:container/small_array"
+import lru "core:container/lru"
+
+import crypto "core:crypto"
+import blake "core:crypto/blake"
+import blake2b "core:crypto/blake2b"
+import blake2s "core:crypto/blake2s"
+import chacha20 "core:crypto/chacha20"
+import chacha20poly1305 "core:crypto/chacha20poly1305"
+import gost "core:crypto/gost"
+import groestl "core:crypto/groestl"
+import haval "core:crypto/haval"
+import jh "core:crypto/jh"
+import keccak "core:crypto/keccak"
+import md2 "core:crypto/md2"
+import md4 "core:crypto/md4"
+import md5 "core:crypto/md5"
+import poly1305 "core:crypto/poly1305"
+import ripemd "core:crypto/ripemd"
+import sha1 "core:crypto/sha1"
+import sha2 "core:crypto/sha2"
+import sha3 "core:crypto/sha3"
+import shake "core:crypto/shake"
+import sm3 "core:crypto/sm3"
+import streebog "core:crypto/streebog"
+import tiger "core:crypto/tiger"
+import tiger2 "core:crypto/tiger2"
+import crypto_util "core:crypto/util"
+import whirlpool "core:crypto/whirlpool"
+import x25519 "core:crypto/x25519"
+
import dynlib "core:dynlib"
-import encoding "core:encoding"
+
import base32 "core:encoding/base32"
import base64 "core:encoding/base64"
import csv "core:encoding/csv"
import hxa "core:encoding/hxa"
import json "core:encoding/json"
+import varint "core:encoding/varint"
+import xml "core:encoding/xml"
+
import fmt "core:fmt"
import hash "core:hash"
+
import image "core:image"
+import netpbm "core:image/netpbm"
import png "core:image/png"
+import qoi "core:image/qoi"
+import tga "core:image/tga"
+
import io "core:io"
import log "core:log"
+
import math "core:math"
import big "core:math/big"
import bits "core:math/bits"
@@ -32,29 +78,40 @@ import linalg "core:math/linalg"
import glm "core:math/linalg/glsl"
import hlm "core:math/linalg/hlsl"
import rand "core:math/rand"
+
import mem "core:mem"
+// import virtual "core:mem/virtual"
+
import ast "core:odin/ast"
import doc_format "core:odin/doc-format"
import odin_format "core:odin/format"
import odin_parser "core:odin/parser"
import odin_printer "core:odin/printer"
import odin_tokenizer "core:odin/tokenizer"
+
import os "core:os"
-import path "core:path"
+
+import slashpath "core:path/slashpath"
import filepath "core:path/filepath"
+
import reflect "core:reflect"
import runtime "core:runtime"
+import simd "core:simd"
import slice "core:slice"
+import slice_heap "core:slice/heap"
import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
-import sync2 "core:sync/sync2"
+import testing "core:testing"
import scanner "core:text/scanner"
+import i18n "core:text/i18n"
import thread "core:thread"
import time "core:time"
+
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
+import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
main :: proc(){}
@@ -65,20 +122,56 @@ _ :: bytes
_ :: c
_ :: libc
_ :: compress
+_ :: shoco
_ :: gzip
_ :: zlib
-_ :: container
+_ :: bit_array
+_ :: priority_queue
+_ :: queue
+_ :: small_array
+_ :: lru
+_ :: crypto
+_ :: blake
+_ :: blake2b
+_ :: blake2s
+_ :: chacha20
+_ :: chacha20poly1305
+_ :: gost
+_ :: groestl
+_ :: haval
+_ :: jh
+_ :: keccak
+_ :: md2
+_ :: md4
+_ :: md5
+_ :: poly1305
+_ :: ripemd
+_ :: sha1
+_ :: sha2
+_ :: sha3
+_ :: shake
+_ :: sm3
+_ :: streebog
+_ :: tiger
+_ :: tiger2
+_ :: crypto_util
+_ :: whirlpool
+_ :: x25519
_ :: dynlib
-_ :: encoding
_ :: base32
_ :: base64
_ :: csv
_ :: hxa
_ :: json
+_ :: varint
+_ :: xml
_ :: fmt
_ :: hash
_ :: image
+_ :: netpbm
_ :: png
+_ :: qoi
+_ :: tga
_ :: io
_ :: log
_ :: math
@@ -97,19 +190,23 @@ _ :: odin_parser
_ :: odin_printer
_ :: odin_tokenizer
_ :: os
-_ :: path
+_ :: slashpath
_ :: filepath
_ :: reflect
_ :: runtime
+_ :: simd
_ :: slice
+_ :: slice_heap
_ :: sort
_ :: strconv
_ :: strings
_ :: sync
-_ :: sync2
+_ :: testing
_ :: scanner
+_ :: i18n
_ :: thread
_ :: time
_ :: unicode
_ :: utf8
+_ :: utf8string
_ :: utf16
\ No newline at end of file
diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin
index 777c184f9..7da2e501b 100644
--- a/examples/all/all_vendor.odin
+++ b/examples/all/all_vendor.odin
@@ -1,26 +1,44 @@
-//+build windows
package all
-import glfw "vendor:glfw"
-import gl "vendor:OpenGL"
-import rl "vendor:raylib"
-import PM "vendor:portmidi"
+import botan "vendor:botan"
+import ENet "vendor:ENet"
+import ggpo "vendor:ggpo"
+import gl "vendor:OpenGL"
+import glfw "vendor:glfw"
+import microui "vendor:microui"
+import miniaudio "vendor:miniaudio"
+import PM "vendor:portmidi"
+import rl "vendor:raylib"
+import exr "vendor:OpenEXRCore"
+
import SDL "vendor:sdl2"
-import IMG "vendor:sdl2/image"
import SDLNet "vendor:sdl2/net"
+import IMG "vendor:sdl2/image"
import MIX "vendor:sdl2/mixer"
import TTF "vendor:sdl2/ttf"
-import vk "vendor:vulkan"
-import ENet "vendor:ENet"
-_ :: glfw
+import vk "vendor:vulkan"
+
+import NS "vendor:darwin/Foundation"
+import MTL "vendor:darwin/Metal"
+import CA "vendor:darwin/QuartzCore"
+
+_ :: botan
+_ :: ENet
+_ :: ggpo
_ :: gl
-_ :: rl
+_ :: glfw
+_ :: microui
+_ :: miniaudio
_ :: PM
+_ :: rl
+_ :: exr
_ :: SDL
-_ :: IMG
_ :: SDLNet
+_ :: IMG
_ :: MIX
_ :: TTF
_ :: vk
-_ :: ENet
\ No newline at end of file
+_ :: NS
+_ :: MTL
+_ :: CA
diff --git a/examples/all/all_vendor_directx.odin b/examples/all/all_vendor_directx.odin
new file mode 100644
index 000000000..2f10d92f8
--- /dev/null
+++ b/examples/all/all_vendor_directx.odin
@@ -0,0 +1,10 @@
+//+build windows
+package all
+
+import D3D11 "vendor:directx/d3d11"
+import D3D12 "vendor:directx/d3d12"
+import DXGI "vendor:directx/dxgi"
+
+_ :: D3D11
+_ :: D3D12
+_ :: DXGI
diff --git a/examples/all/all_vendor_stl.odin b/examples/all/all_vendor_stl.odin
new file mode 100644
index 000000000..9faf53c63
--- /dev/null
+++ b/examples/all/all_vendor_stl.odin
@@ -0,0 +1,15 @@
+//+build windows, linux
+package all
+
+import stb_easy_font "vendor:stb/easy_font"
+import stbi "vendor:stb/image"
+import stbrp "vendor:stb/rect_pack"
+import stbtt "vendor:stb/truetype"
+import stb_vorbis "vendor:stb/vorbis"
+
+_ :: stb_easy_font
+_ :: stbi
+_ :: stbrp
+_ :: stbtt
+_ :: stb_vorbis
+
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 3e34e3d49..457aa786a 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -11,22 +11,32 @@ import "core:intrinsics"
import "core:math/big"
/*
- The Odin programming language is fast, concise, readable, pragmatic and open sourced.
- It is designed with the intent of replacing C with the following goals:
- * simplicity
- * high performance
- * built for modern systems
- * joy of programming
+ Odin is a general-purpose programming language with distinct typing built
+ for high performance, modern systems and data-oriented programming.
+
+ Odin is the C alternative for the Joy of Programming.
# Installing Odin
Getting Started - https://odin-lang.org/docs/install/
Instructions for downloading and install the Odin compiler and libraries.
# Learning Odin
+ Getting Started - https://odin-lang.org/docs/install/
+ Getting Started with Odin. Downloading, installing, and getting your
+ first program to compile and run.
Overview of Odin - https://odin-lang.org/docs/overview/
- An overview of the Odin programming language.
+ An overview of the Odin programming language and its features.
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
Answers to common questions about Odin.
+ Packages - https://pkg.odin-lang.org/
+ Documentation for all the official packages part of the
+ core and vendor library collections.
+ Nightly Builds - https://odin-lang.org/docs/nightly/
+ Get the latest nightly builds of Odin.
+ More Odin Examples - https://github.com/odin-lang/examples
+ This repository contains examples of how certain things can be accomplished
+ in idiomatic Odin, allowing you learn its semantics, as well as how to use
+ parts of the core and vendor package collections.
*/
the_basics :: proc() {
@@ -88,6 +98,7 @@ the_basics :: proc() {
z: f64 // `z` is typed of type `f64` (64-bit floating point number)
z = 1 // `1` is an untyped integer literal which can be implicitly converted to `f64`
// No need for any suffixes or decimal places like in other languages
+ // (with the exception of negative zero, which must be given as `-0.0`)
// CONSTANTS JUST WORK!!!
@@ -244,10 +255,10 @@ control_flow :: proc() {
// A switch statement is another way to write a sequence of if-else statements.
// In Odin, the default case is denoted as a case without any expression.
- switch arch := ODIN_ARCH; arch {
- case "386":
+ #partial switch arch := ODIN_ARCH; arch {
+ case .i386:
fmt.println("32-bit")
- case "amd64":
+ case .amd64:
fmt.println("64-bit")
case: // default
fmt.println("Unsupported architecture")
@@ -363,12 +374,12 @@ control_flow :: proc() {
*/
// Example
- when ODIN_ARCH == "386" {
+ when ODIN_ARCH == .i386 {
fmt.println("32 bit")
- } else when ODIN_ARCH == "amd64" {
+ } else when ODIN_ARCH == .amd64 {
fmt.println("64 bit")
} else {
- fmt.println("Unsupported architecture")
+ fmt.println("Unknown architecture")
}
// The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in C’s preprocessor however, in Odin,
@@ -1099,14 +1110,16 @@ prefix_table := [?]string{
"Black",
}
-threading_example :: proc() {
- if ODIN_OS == "darwin" {
- // TODO: Fix threads on darwin/macOS
- return
- }
+print_mutex := b64(false)
+threading_example :: proc() {
fmt.println("\n# threading_example")
+ did_acquire :: proc(m: ^b64) -> (acquired: bool) {
+ res, ok := intrinsics.atomic_compare_exchange_strong(m, false, true)
+ return ok && res == false
+ }
+
{ // Basic Threads
fmt.println("\n## Basic Threads")
worker_proc :: proc(t: ^thread.Thread) {
@@ -1145,26 +1158,47 @@ threading_example :: proc() {
{ // Thread Pool
fmt.println("\n## Thread Pool")
- task_proc :: proc(t: ^thread.Task) {
+ task_proc :: proc(t: thread.Task) {
index := t.user_index % len(prefix_table)
for iteration in 1..=5 {
+ for !did_acquire(&print_mutex) { thread.yield() } // Allow one thread to print at a time.
+
fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration)
fmt.printf("`%s`: iteration %d\n", prefix_table[index], iteration)
+
+ print_mutex = false
+
time.sleep(1 * time.Millisecond)
}
}
+ N :: 3
+
pool: thread.Pool
- thread.pool_init(pool=&pool, thread_count=3)
+ thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator)
defer thread.pool_destroy(&pool)
for i in 0..<30 {
- thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i)
+ // be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use
+ thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator)
}
thread.pool_start(&pool)
- thread.pool_wait_and_process(&pool)
+
+ {
+ // Wait a moment before we cancel a thread
+ time.sleep(5 * time.Millisecond)
+
+ // Allow one thread to print at a time.
+ for !did_acquire(&print_mutex) { thread.yield() }
+
+ thread.terminate(pool.threads[N - 1], 0)
+ fmt.println("Canceled last thread")
+ print_mutex = false
+ }
+
+ thread.pool_finish(&pool)
}
}
@@ -1606,13 +1640,13 @@ where_clauses :: proc() {
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import kernel32 "system:kernel32.lib"
}
foreign_system :: proc() {
fmt.println("\n#foreign system")
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
// It is sometimes necessarily to interface with foreign code,
// such as a C library. In Odin, this is achieved through the
// foreign system. You can “import” a library into the code
@@ -1708,7 +1742,6 @@ deprecated_attribute :: proc() {
}
range_statements_with_multiple_return_values :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
fmt.println("\n#range statements with multiple return values")
My_Iterator :: struct {
index: int,
@@ -1921,14 +1954,14 @@ constant_literal_expressions :: proc() {
fmt.println("-------")
- Partial_Baz :: enum{A=5, B, C, D=16}
- #assert(len(Partial_Baz) < len(#partial [Partial_Baz]int))
- PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A ..= .C = 1, .D = 16}
+ Sparse_Baz :: enum{A=5, B, C, D=16}
+ #assert(len(Sparse_Baz) < len(#sparse[Sparse_Baz]int))
+ SPARSE_ENUM_ARRAY_CONST :: #sparse[Sparse_Baz]int{.A ..= .C = 1, .D = 16}
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.A])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.B])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.C])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.D])
fmt.println("-------")
@@ -1944,15 +1977,17 @@ constant_literal_expressions :: proc() {
}
union_maybe :: proc() {
- fmt.println("\n#union #maybe")
+ fmt.println("\n#union based maybe")
// NOTE: This is already built-in, and this is just a reimplementation to explain the behaviour
- Maybe :: union($T: typeid) #maybe {T}
+ Maybe :: union($T: typeid) {T}
i: Maybe(u8)
p: Maybe(^u8) // No tag is stored for pointers, nil is the sentinel value
+ // Tag size will be as small as needed for the number of variants
#assert(size_of(i) == size_of(u8) + size_of(u8))
+ // No need to store a tag here, the `nil` state is shared with the variant's `nil`
#assert(size_of(p) == size_of(^u8))
i = 123
@@ -1998,7 +2033,6 @@ relative_data_types :: proc() {
or_else_operator :: proc() {
fmt.println("\n#'or_else'")
- // IMPORTANT NOTE: 'or_else' is an experimental feature and subject to change/removal
{
m: map[string]int
i: int
@@ -2029,8 +2063,6 @@ or_else_operator :: proc() {
or_return_operator :: proc() {
fmt.println("\n#'or_return'")
- // IMPORTANT NOTE: 'or_return' is an experimental feature and subject to change/removal
- //
// The concept of 'or_return' will work by popping off the end value in a multiple
// valued expression and checking whether it was not 'nil' or 'false', and if so,
// set the end return value to value if possible. If the procedure only has one
@@ -2421,6 +2453,13 @@ matrix_type :: proc() {
}
main :: proc() {
+ /*
+ For More Odin Examples - https://github.com/odin-lang/examples
+ This repository contains examples of how certain things can be accomplished
+ in idiomatic Odin, allowing you learn its semantics, as well as how to use
+ parts of the core and vendor package collections.
+ */
+
when true {
the_basics()
control_flow()
diff --git a/examples/hms2019/basic.odin b/examples/hms2019/basic.odin
deleted file mode 100644
index 4c4a4eff0..000000000
--- a/examples/hms2019/basic.odin
+++ /dev/null
@@ -1,7 +0,0 @@
-package basic
-
-import "core:fmt"
-
-main :: proc() {
- fmt.println("Hellope!");
-}
\ No newline at end of file
diff --git a/examples/hms2019/eca.odin b/examples/hms2019/eca.odin
deleted file mode 100644
index eaefeeb9b..000000000
--- a/examples/hms2019/eca.odin
+++ /dev/null
@@ -1,67 +0,0 @@
-package eca
-
-import "core:fmt"
-import "core:math/rand"
-import "core:time"
-import "intrinsics"
-
-elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0)
- where intrinsics.type_is_integer(T),
- intrinsics.type_is_unsigned(T) {
- N :: 8*size_of(state);
-
- output :: proc(state: T) {
- buf: [N]byte;
- for i in 0.. T {
- return (x >> i) & 0x1;
- }
- set :: proc(x: ^T, cell, k: T, rule: u8) {
- x^ &~= 1<>k&1 != 0 {
- x^ |= 1<| 0 do time.sleep(pause);
-
-
- k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2;
- set(&a1, 0, k, rule);
- a1 |= (1<<0) * T(rule>>k&1);
- for c in 1..>1 | bit(a, c+1)<<2;
- set(&a1, c, k, rule);
- }
- set(&a1, last, k>>1|bit(a, 0)<<2, rule);
- a, a1 = a1, a;
- output(a);
- if a == a1 {
- return;
- }
- }
-}
-
-main :: proc() {
- elementary_cellular_automata(
- state=rand.uint128(),
- rule=30,
- generations=5000,
- pause=100*time.Millisecond,
- );
-}
\ No newline at end of file
diff --git a/examples/hms2019/hms2019.odin b/examples/hms2019/hms2019.odin
deleted file mode 100644
index 5c2e836f8..000000000
--- a/examples/hms2019/hms2019.odin
+++ /dev/null
@@ -1,1754 +0,0 @@
-package hms2019
-
-import "core:fmt"
-import "core:mem"
-import "core:os"
-import "core:reflect"
-import "intrinsics"
-
-/*
- Welcome to Handmade Seattle 2019!
-
- The Odin programming language is fast, concise, readable, pragmatic and open sourced.
- It is designed with the intent of replacing C with the following goals:
- * simplicity
- * high performance
- * built for modern systems
- * joy of programming
-
- # Installing Odin
- Getting Started - https://odin-lang.org/docs/install/
- Instructions for downloading and install the Odin compiler and libraries.
-
- # Learning Odin
- Overview of Odin - https://odin-lang.org/docs/overview/
- An overview of the Odin programming language.
- Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
- Answers to common questions about Odin.
-*/
-
-the_basics :: proc() {
- fmt.println("\n# the basics");
-
- { // The Basics
- fmt.println("Hellope");
-
- // Lexical elements and literals
- // A comment
-
- my_integer_variable: int; // A comment for documentaton
-
- // Multi-line comments begin with /* and end with */. Multi-line comments can
- // also be nested (unlike in C):
- /*
- You can have any text or code here and
- have it be commented.
- /*
- NOTE: comments can be nested!
- */
- */
-
- // String literals are enclosed in double quotes and character literals in single quotes.
- // Special characters are escaped with a backslash \
-
- some_string := "This is a string";
- _ = 'A'; // unicode codepoint literal
- _ = '\n';
- _ = "C:\\Windows\\notepad.exe";
- // Raw string literals are enclosed with single back ticks
- _ = `C:\Windows\notepad.exe`;
-
- // The length of a string in bytes can be found using the built-in `len` procedure:
- _ = len("Foo");
- _ = len(some_string);
-
-
- // Numbers
-
- // Numerical literals are written similar to most other programming languages.
- // A useful feature in Odin is that underscores are allowed for better
- // readability: 1_000_000_000 (one billion). A number that contains a dot is a
- // floating point literal: 1.0e9 (one billion). If a number literal is suffixed
- // with i, is an imaginary number literal: 2i (2 multiply the square root of -1).
-
- // Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
- // literals 0x. A leading zero does not produce an octal constant (unlike C).
-
- // In Odin, if a number constant is possible to be represented by a type without
- // precision loss, it will automatically convert to that type.
-
- x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
- // Constant literals are “untyped” which means that they can implicitly convert to a type.
-
- y: int; // `y` is typed of type `int`
- y = 1; // `1` is an untyped integer literal which can implicitly convert to `int`
-
- z: f64; // `z` is typed of type `f64` (64-bit floating point number)
- z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64`
- // No need for any suffixes or decimal places like in other languages
- // CONSTANTS JUST WORK!!!
-
-
- // Assignment statements
- h: int = 123; // declares a new variable `h` with type `int` and assigns a value to it
- h = 637; // assigns a new value to `h`
-
- // `=` is the assignment operator
-
- // You can assign multiple variables with it:
- a, b := 1, "hello"; // declares `a` and `b` and infers the types from the assignments
- b, a = "byte", 0;
-
- // Note: `:=` is two tokens, `:` and `=`. The follow are equivalent
- /*
- i: int = 123;
- i: = 123;
- i := 123
- */
-
- // Constant declarations
- // Constants are entities (symbols) which have an assigned value.
- // The constant’s value cannot be changed.
- // The constant’s value must be able to be evaluated at compile time:
- X :: "what"; // constant `X` has the untyped string value "what"
-
- // Constants can be explicitly typed like a variable declaration:
- Y : int : 123;
- Z :: Y + 7; // constant computations are possible
- }
-}
-
-control_flow :: proc() {
- fmt.println("\n# control flow");
- { // Control flow
- // For loop
- // Odin has only one loop statement, the `for` loop
-
- // Basic for loop
- for i := 0; i < 10; i += 1 {
- fmt.println(i);
- }
-
- // NOTE: Unlike other languages like C, there are no parentheses `( )` surrounding the three components.
- // Braces `{ }` or a `do` are always required>
- for i := 0; i < 10; i += 1 { }
- for i := 0; i < 10; i += 1 do fmt.print();
-
- // The initial and post statements are optional
- i := 0;
- for ; i < 10; {
- i += 1;
- }
-
- // These semicolons can be dropped. This `for` loop is equivalent to C's `while` loop
- i = 0;
- for i < 10 {
- i += 1;
- }
-
- // If the condition is omitted, this produces an infinite loop:
- for {
- break;
- }
-
- // Range-based for loop
- // The basic for loop
- for i := 0; i < 10; i += 1 {
- fmt.println(i);
- }
- // can also be written
- for i in 0..<10 {
- fmt.println(i);
- }
- for i in 0..9 {
- fmt.println(i);
- }
-
- // Certain built-in types can be iterated over
- some_string := "Hello, 世界";
- for character in some_string { // Strings are assumed to be UTF-8
- fmt.println(character);
- }
-
- some_array := [3]int{1, 4, 9};
- for value in some_array {
- fmt.println(value);
- }
-
- some_slice := []int{1, 4, 9};
- for value in some_slice {
- fmt.println(value);
- }
-
- some_dynamic_array := [dynamic]int{1, 4, 9};
- defer delete(some_dynamic_array);
- for value in some_dynamic_array {
- fmt.println(value);
- }
-
-
- some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4};
- defer delete(some_map);
- for key in some_map {
- fmt.println(key);
- }
-
- // Alternatively a second index value can be added
- for character, index in some_string {
- fmt.println(index, character);
- }
- for value, index in some_array {
- fmt.println(index, value);
- }
- for value, index in some_slice {
- fmt.println(index, value);
- }
- for value, index in some_dynamic_array {
- fmt.println(index, value);
- }
- for key, value in some_map {
- fmt.println(key, value);
- }
-
- // The iterated values are copies and cannot be written to.
- // The following idiom is useful for iterating over a container in a by-reference manner:
- for _, i in some_slice {
- some_slice[i] = (i+1)*(i+1);
- }
-
-
- // If statements
- x := 123;
- if x >= 0 {
- fmt.println("x is positive");
- }
-
- if y := -34; y < 0 {
- fmt.println("y is negative");
- }
-
- if y := 123; y < 0 {
- fmt.println("y is negative");
- } else if y == 0 {
- fmt.println("y is zero");
- } else {
- fmt.println("y is positive");
- }
-
- // Switch statement
- // A switch statement is another way to write a sequence of if-else statements.
- // In Odin, the default case is denoted as a case without any expression.
-
- switch arch := ODIN_ARCH; arch {
- case "386":
- fmt.println("32-bit");
- case "amd64":
- fmt.println("64-bit");
- case: // default
- fmt.println("Unsupported architecture");
- }
-
- // Odin’s `switch` is like one in C or C++, except that Odin only runs the selected case.
- // This means that a `break` statement is not needed at the end of each case.
- // Another important difference is that the case values need not be integers nor constants.
-
- // To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used.
- one_angry_dwarf :: proc() -> int {
- fmt.println("one_angry_dwarf was called");
- return 1;
- }
-
- switch i := 0; i {
- case 0:
- case one_angry_dwarf():
- }
-
- // A switch statement without a condition is the same as `switch true`.
- // This can be used to write a clean and long if-else chain and have the
- // ability to break if needed
-
- switch {
- case x < 0:
- fmt.println("x is negative");
- case x == 0:
- fmt.println("x is zero");
- case:
- fmt.println("x is positive");
- }
-
- // A `switch` statement can also use ranges like a range-based loop:
- switch c := 'j'; c {
- case 'A'..'Z', 'a'..'z', '0'..'9':
- fmt.println("c is alphanumeric");
- }
-
- switch x {
- case 0..<10:
- fmt.println("units");
- case 10..<13:
- fmt.println("pre-teens");
- case 13..<20:
- fmt.println("teens");
- case 20..<30:
- fmt.println("twenties");
- }
- }
-
- { // Defer statement
- // A defer statement defers the execution of a statement until the end of
- // the scope it is in.
-
- // The following will print 4 then 234:
- {
- x := 123;
- defer fmt.println(x);
- {
- defer x = 4;
- x = 2;
- }
- fmt.println(x);
-
- x = 234;
- }
-
- // You can defer an entire block too:
- {
- bar :: proc() {}
-
- defer {
- fmt.println("1");
- fmt.println("2");
- }
-
- cond := false;
- defer if cond {
- bar();
- }
- }
-
- // Defer statements are executed in the reverse order that they were declared:
- {
- defer fmt.println("1");
- defer fmt.println("2");
- defer fmt.println("3");
- }
- // Will print 3, 2, and then 1.
-
- if false {
- f, err := os.open("my_file.txt");
- if err != 0 {
- // handle error
- }
- defer os.close(f);
- // rest of code
- }
- }
-
- { // When statement
- /*
- The when statement is almost identical to the if statement but with some differences:
-
- * Each condition must be a constant expression as a when
- statement is evaluated at compile time.
- * The statements within a branch do not create a new scope
- * The compiler checks the semantics and code only for statements
- that belong to the first condition that is true
- * An initial statement is not allowed in a when statement
- * when statements are allowed at file scope
- */
-
- // Example
- when ODIN_ARCH == "386" {
- fmt.println("32 bit");
- } else when ODIN_ARCH == "amd64" {
- fmt.println("64 bit");
- } else {
- fmt.println("Unsupported architecture");
- }
- // The when statement is very useful for writing platform specific code.
- // This is akin to the #if construct in C’s preprocessor however, in Odin,
- // it is type checked.
- }
-
- { // Branch statements
- cond, cond1, cond2 := false, false, false;
- one_step :: proc() { fmt.println("one_step"); }
- beyond :: proc() { fmt.println("beyond"); }
-
- // Break statement
- for cond {
- switch {
- case:
- if cond {
- break; // break out of the `switch` statement
- }
- }
-
- break; // break out of the `for` statement
- }
-
- loop: for cond1 {
- for cond2 {
- break loop; // leaves both loops
- }
- }
-
- // Continue statement
- for cond {
- if cond2 {
- continue;
- }
- fmt.println("Hellope");
- }
-
- // Fallthrough statement
-
- // Odin’s switch is like one in C or C++, except that Odin only runs the selected
- // case. This means that a break statement is not needed at the end of each case.
- // Another important difference is that the case values need not be integers nor
- // constants.
-
- // fallthrough can be used to explicitly fall through into the next case block:
-
- switch i := 0; i {
- case 0:
- one_step();
- fallthrough;
- case 1:
- beyond();
- }
- }
-}
-
-
-named_proc_return_parameters :: proc() {
- fmt.println("\n# 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
-}
-
-
-explicit_procedure_overloading :: proc() {
- fmt.println("\n# 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);
-}
-
-struct_type :: proc() {
- fmt.println("\n# struct type");
- // A struct is a record type in Odin. It is a collection of fields.
- // Struct fields are accessed by using a dot:
- {
- Vector2 :: struct {
- x: f32,
- y: f32,
- };
- v := Vector2{1, 2};
- v.x = 4;
- fmt.println(v.x);
-
- // Struct fields can be accessed through a struct pointer:
-
- v = Vector2{1, 2};
- p := &v;
- p.x = 1335;
- fmt.println(v);
-
- // We could write p^.x, however, it is to nice abstract the ability
- // to not explicitly dereference the pointer. This is very useful when
- // refactoring code to use a pointer rather than a value, and vice versa.
- }
- {
- // A struct literal can be denoted by providing the struct’s type
- // followed by {}. A struct literal must either provide all the
- // arguments or none:
- Vector3 :: struct {
- x, y, z: f32,
- };
- v: Vector3;
- v = Vector3{}; // Zero value
- v = Vector3{1, 4, 9};
-
- // You can list just a subset of the fields if you specify the
- // field by name (the order of the named fields does not matter):
- v = Vector3{z=1, y=2};
- assert(v.x == 0);
- assert(v.y == 2);
- assert(v.z == 1);
- }
- {
- // Structs can tagged with different memory layout and alignment requirements:
-
- a :: struct #align 4 {}; // align to 4 bytes
- b :: struct #packed {}; // remove padding between fields
- c :: struct #raw_union {}; // all fields share the same offset (0). This is the same as C's union
- }
-
-}
-
-
-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 :: distinct [3]f32;
- Quaternion :: distinct quaternion128;
-
- // 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: typeid) -> ^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!");
- fmt.println("I'm a monster");
- }
- }
-
- {
- // 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: typeid) -> ^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;
- }
- */
- }
-}
-
-using_statement :: proc() {
- fmt.println("\n# using statement");
- // using can used to bring entities declared in a scope/namespace
- // into the current scope. This can be applied to import declarations,
- // import names, struct fields, procedure fields, and struct values.
-
- Vector3 :: struct{x, y, z: f32};
- {
- Entity :: struct {
- position: Vector3,
- orientation: quaternion128,
- };
-
- // It can used like this:
- foo0 :: proc(entity: ^Entity) {
- fmt.println(entity.position.x, entity.position.y, entity.position.z);
- }
-
- // The entity members can be brought into the procedure scope by using it:
- foo1 :: proc(entity: ^Entity) {
- using entity;
- fmt.println(position.x, position.y, position.z);
- }
-
- // The using can be applied to the parameter directly:
- foo2 :: proc(using entity: ^Entity) {
- fmt.println(position.x, position.y, position.z);
- }
-
- // It can also be applied to sub-fields:
- foo3 :: proc(entity: ^Entity) {
- using entity.position;
- fmt.println(x, y, z);
- }
- }
- {
- // We can also apply the using statement to the struct fields directly,
- // making all the fields of position appear as if they on Entity itself:
- Entity :: struct {
- using position: Vector3,
- orientation: quaternion128,
- };
- foo :: proc(entity: ^Entity) {
- fmt.println(entity.x, entity.y, entity.z);
- }
-
-
- // Subtype polymorphism
- // It is possible to get subtype polymorphism, similar to inheritance-like
- // functionality in C++, but without the requirement of vtables or unknown
- // struct layout:
-
- Colour :: struct {r, g, b, a: u8};
- Frog :: struct {
- ribbit_volume: f32,
- using entity: Entity,
- colour: Colour,
- };
-
- frog: Frog;
- // Both work
- foo(&frog.entity);
- foo(&frog);
- frog.x = 123;
-
- // Note: using can be applied to arbitrarily many things, which allows
- // the ability to have multiple subtype polymorphism (but also its issues).
-
- // Note: using’d fields can still be referred by name.
- }
- { // using on an enum declaration
-
- using Foo :: enum {A, B, C};
-
- f0 := A;
- f1 := B;
- f2 := C;
- fmt.println(f0, f1, f2);
- fmt.println(len(Foo));
- }
-}
-
-
-implicit_context_system :: proc() {
- fmt.println("\n# implicit context system");
- // In each scope, there is an implicit value named context. This
- // context variable is local to each scope and is implicitly passed
- // by pointer to any procedure call in that scope (if the procedure
- // has the Odin calling convention).
-
- // The main purpose of the implicit context system is for the ability
- // to intercept third-party code and libraries and modify their
- // functionality. One such case is modifying how a library allocates
- // something or logs something. In C, this was usually achieved with
- // the library defining macros which could be overridden so that the
- // user could define what he wanted. However, not many libraries
- // supported this in many languages by default which meant intercepting
- // third-party code to see what it does and to change how it does it is
- // not possible.
-
- c := context; // copy the current scope's context
-
- context.user_index = 456;
- {
- context.allocator = my_custom_allocator();
- context.user_index = 123;
- what_a_fool_believes(); // the `context` for this scope is implicitly passed to `what_a_fool_believes`
- }
-
- // `context` value is local to the scope it is in
- assert(context.user_index == 456);
-
- what_a_fool_believes :: proc() {
- c := context; // this `context` is the same as the parent procedure that it was called from
- // From this example, context.user_index == 123
- // An context.allocator is assigned to the return value of `my_custom_allocator()`
- assert(context.user_index == 123);
-
- // The memory management procedure use the `context.allocator` by
- // default unless explicitly specified otherwise
- china_grove := new(int);
- free(china_grove);
- }
-
- my_custom_allocator :: mem.nil_allocator;
-
- // By default, the context value has default values for its parameters which is
- // decided in the package runtime. What the defaults are are compiler specific.
-
- // To see what the implicit context value contains, please see the following
- // definition in package runtime.
-}
-
-parametric_polymorphism :: proc() {
- fmt.println("\n# 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: typeid) -> ^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: typeid) {
- occupied: bool,
- hash: u32,
- key: Key,
- value: Value,
- };
- TABLE_SIZE_MIN :: 32;
- Table :: struct(Key, Value: typeid) {
- count: int,
- allocator: mem.Allocator,
- slots: []Table_Slot(Key, Value),
- };
-
- // Only allow types that are specializations of a (polymorphic) slice
- make_slice :: proc($T: typeid/[]$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;
- defer delete(old_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);
- }
- }
-
- // 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));
-
- 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.. (res: [N]T) {
- // `N` is the constant value passed
- // `I` is the type of N
- // `T` is the type passed
- fmt.printf("Generating an array of type %v from the value %v of type %v\n",
- typeid_of(type_of(res)), N, typeid_of(I));
- for i in 0.. (c: [M][P]T) {
- for i in 0.. 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));
- }
-}
-
-map_type :: proc() {
- fmt.println("\n# map type");
-
- m := make(map[string]int);
- defer delete(m);
-
- m["Bob"] = 2;
- m["Ted"] = 5;
- fmt.println(m["Bob"]);
-
- delete_key(&m, "Ted");
-
- // If an element of a key does not exist, the zero value of the
- // element will be returned. To check to see if an element exists
- // can be done in two ways:
- elem, ok := m["Bob"];
- exists := "Bob" in m;
-
-}
-
-implicit_selector_expression :: proc() {
- fmt.println("\n# implicit selector expression");
-
- Foo :: enum {A, B, C};
-
- f: Foo;
- f = Foo.A;
- f = .A;
-
- BAR :: bit_set[Foo]{.B, .C};
-
- switch f {
- case .A:
- fmt.println("HERE");
- case .B:
- fmt.println("NEVER");
- case .C:
- fmt.println("FOREVER");
- }
-
- my_map := make(map[Foo]int);
- defer delete(my_map);
-
- my_map[.A] = 123;
- my_map[Foo.B] = 345;
-
- fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]);
-}
-
-
-complete_switch :: proc() {
- fmt.println("\n# complete_switch");
- { // enum
- Foo :: enum {
- A,
- B,
- C,
- D,
- };
-
- 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:
- }
- }
-}
-
-cstring_example :: proc() {
- fmt.println("\n# cstring_example");
-
- W :: "Hellope";
- X :: cstring(W);
- Y :: string(X);
-
- w := W;
- _ = w;
- x: cstring = X;
- y: string = Y;
- z := string(x);
- fmt.println(x, y, z);
- fmt.println(len(x), len(y), len(z));
- fmt.println(len(W), len(X), len(Y));
- // IMPORTANT NOTE for cstring variables
- // len(cstring) is O(N)
- // cast(string)cstring is O(N)
-}
-
-bit_set_type :: proc() {
- fmt.println("\n# bit_set type");
-
- {
- using Day :: enum {
- Sunday,
- Monday,
- Tuesday,
- Wednesday,
- Thursday,
- Friday,
- Saturday,
- };
-
- Days :: distinct bit_set[Day];
- WEEKEND :: Days{Sunday, Saturday};
-
- d: Days;
- d = {Sunday, Monday};
- e := d | WEEKEND;
- e |= {Monday};
- fmt.println(d, e);
-
- ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
- fmt.println(ok);
- if Saturday in e {
- fmt.println("Saturday in", e);
- }
- X :: Saturday in WEEKEND; // Constant evaluation
- fmt.println(X);
- fmt.println("Cardinality:", card(e));
- }
- {
- x: bit_set['A'..'Z'];
- #assert(size_of(x) == size_of(u32));
- y: bit_set[0..8; u16];
- fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
- fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
-
- incl(&x, 'F');
- assert('F' in x);
- excl(&x, 'F');
- assert('F' not_in x);
-
- y |= {1, 4, 2};
- assert(2 in y);
- }
- {
- Letters :: bit_set['A'..'Z'];
- a := Letters{'A', 'B'};
- b := Letters{'A', 'B', 'C', 'D', 'F'};
- c := Letters{'A', 'B'};
-
- assert(a <= b); // 'a' is a subset of 'b'
- assert(b >= a); // 'b' is a superset of 'a'
- assert(a < b); // 'a' is a strict subset of 'b'
- assert(b > a); // 'b' is a strict superset of 'a'
-
- assert(!(a < c)); // 'a' is a not strict subset of 'c'
- assert(!(c > a)); // 'c' is a not strict superset of 'a'
- }
-}
-
-deferred_procedure_associations :: proc() {
- fmt.println("\n# deferred procedure associations");
-
- @(deferred_out=closure)
- open :: proc(s: string) -> bool {
- fmt.println(s);
- return true;
- }
-
- closure :: proc(ok: bool) {
- fmt.println("Goodbye?", ok);
- }
-
- if open("Welcome") {
- fmt.println("Something in the middle, mate.");
- }
-}
-
-reflection :: proc() {
- fmt.println("\n# reflection");
-
- Foo :: struct {
- x: int `tag1`,
- y: string `json:"y_field"`,
- z: bool, // no tag
- };
-
- id := typeid_of(Foo);
- names := reflect.struct_field_names(id);
- types := reflect.struct_field_types(id);
- tags := reflect.struct_field_tags(id);
-
- assert(len(names) == len(types) && len(names) == len(tags));
-
- fmt.println("Foo :: struct {");
- for tag, i in tags {
- name, type := names[i], types[i];
- if tag != "" {
- fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
- } else {
- fmt.printf("\t%s: %T,\n", name, type);
- }
- }
- fmt.println("}");
-
-
- for tag, i in tags {
- if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
- fmt.printf("json: %s -> %s\n", names[i], val);
- }
- }
-}
-
-quaternions :: proc() {
- // Not just an April Fool's Joke any more, but a fully working thing!
- fmt.println("\n# quaternions");
-
- { // Quaternion operations
- q := 1 + 2i + 3j + 4k;
- r := quaternion(5, 6, 7, 8);
- t := q * r;
- fmt.printf("(%v) * (%v) = %v\n", q, r, t);
- v := q / r;
- fmt.printf("(%v) / (%v) = %v\n", q, r, v);
- u := q + r;
- fmt.printf("(%v) + (%v) = %v\n", q, r, u);
- s := q - r;
- fmt.printf("(%v) - (%v) = %v\n", q, r, s);
- }
- { // The quaternion types
- q128: quaternion128; // 4xf32
- q256: quaternion256; // 4xf64
- q128 = quaternion(1, 0, 0, 0);
- q256 = 1; // quaternion(1, 0, 0, 0);
- }
- { // Built-in procedures
- q := 1 + 2i + 3j + 4k;
- fmt.println("q =", q);
- fmt.println("real(q) =", real(q));
- fmt.println("imag(q) =", imag(q));
- fmt.println("jmag(q) =", jmag(q));
- fmt.println("kmag(q) =", kmag(q));
- fmt.println("conj(q) =", conj(q));
- fmt.println("abs(q) =", abs(q));
- }
- { // Conversion of a complex type to a quaternion type
- c := 1 + 2i;
- q := quaternion256(c);
- fmt.println(c);
- fmt.println(q);
- }
- { // Memory layout of Quaternions
- q := 1 + 2i + 3j + 4k;
- a := transmute([4]f64)q;
- fmt.println("Quaternion memory layout: xyzw/(ijkr)");
- fmt.println(q); // 1.000+2.000i+3.000j+4.000k
- fmt.println(a); // [2.000, 3.000, 4.000, 1.000]
- }
-}
-
-inline_for_statement :: proc() {
- fmt.println("\n#inline for statements");
-
- // 'inline for' works the same as if the 'inline' prefix did not
- // exist but these ranged loops are explicitly unrolled which can
- // be very very useful for certain optimizations
-
- fmt.println("Ranges");
- inline for x, i in 1..<4 {
- fmt.println(x, i);
- }
-
- fmt.println("Strings");
- inline for r, i in "Hello, 世界" {
- fmt.println(r, i);
- }
-
- fmt.println("Arrays");
- inline for elem, idx in ([4]int{1, 4, 9, 16}) {
- fmt.println(elem, idx);
- }
-
-
- Foo_Enum :: enum {
- A = 1,
- B,
- C = 6,
- D,
- };
- fmt.println("Enum types");
- inline for elem, idx in Foo_Enum {
- fmt.println(elem, idx);
- }
-}
-
-where_clauses :: proc() {
- fmt.println("\n#procedure 'where' clauses");
-
- { // Sanity checks
- simple_sanity_check :: proc(x: [2]int)
- where len(x) > 1,
- type_of(x) == [2]int {
- fmt.println(x);
- }
- }
- { // Parametric polymorphism checks
- cross_2d :: proc(a, b: $T/[2]$E) -> E
- where intrinsics.type_is_numeric(E) {
- return a.x*b.y - a.y*b.x;
- }
- cross_3d :: proc(a, b: $T/[3]$E) -> T
- where intrinsics.type_is_numeric(E) {
- x := a.y*b.z - a.z*b.y;
- y := a.z*b.x - a.x*b.z;
- z := a.x*b.y - a.y*b.z;
- return T{x, y, z};
- }
-
- a := [2]int{1, 2};
- b := [2]int{5, -3};
- fmt.println(cross_2d(a, b));
-
- x := [3]f32{1, 4, 9};
- y := [3]f32{-5, 0, 3};
- fmt.println(cross_3d(x, y));
-
- // Failure case
- // i := [2]bool{true, false};
- // j := [2]bool{false, true};
- // fmt.println(cross_2d(i, j));
-
- }
-
- { // Procedure groups usage
- foo :: proc(x: [$N]int) -> bool
- where N > 2 {
- fmt.println(#procedure, "was called with the parameter", x);
- return true;
- }
-
- bar :: proc(x: [$N]int) -> bool
- where 0 < N,
- N <= 2 {
- fmt.println(#procedure, "was called with the parameter", x);
- return false;
- }
-
- baz :: proc{foo, bar};
-
- x := [3]int{1, 2, 3};
- y := [2]int{4, 9};
- ok_x := baz(x);
- ok_y := baz(y);
- assert(ok_x == true);
- assert(ok_y == false);
- }
-
- { // Record types
- Foo :: struct(T: typeid, N: int)
- where intrinsics.type_is_integer(T),
- N > 2 {
- x: [N]T,
- y: [N-2]T,
- };
-
- T :: i32;
- N :: 5;
- f: Foo(T, N);
- #assert(size_of(f) == (N+N-2)*size_of(T));
- }
-}
-
-
-when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib"
-
-foreign_system :: proc() {
- fmt.println("\n#foreign system");
- when ODIN_OS == "windows" {
- // It is sometimes necessarily to interface with foreign code,
- // such as a C library. In Odin, this is achieved through the
- // foreign system. You can “import” a library into the code
- // using the same semantics as a normal import declaration.
-
- // This foreign import declaration will create a
- // “foreign import name” which can then be used to associate
- // entities within a foreign block.
-
- foreign kernel32 {
- ExitProcess :: proc "stdcall" (exit_code: u32) ---
- }
-
- // Foreign procedure declarations have the cdecl/c calling
- // convention by default unless specified otherwise. Due to
- // foreign procedures do not have a body declared within this
- // code, you need append the --- symbol to the end to distinguish
- // it as a procedure literal without a body and not a procedure type.
-
- // The attributes system can be used to change specific properties
- // of entities declared within a block:
-
- @(default_calling_convention = "std")
- foreign kernel32 {
- @(link_name="GetLastError") get_last_error :: proc() -> i32 ---
- }
-
- // Example using the link_prefix attribute
- @(default_calling_convention = "std")
- @(link_prefix = "Get")
- foreign kernel32 {
- LastError :: proc() -> i32 ---
- }
- }
-}
-
-ranged_fields_for_array_compound_literals :: proc() {
- fmt.println("\n#ranged fields for array compound literals");
- { // Normal Array Literal
- foo := [?]int{1, 4, 9, 16};
- fmt.println(foo);
- }
- { // Indexed
- foo := [?]int{
- 3 = 16,
- 1 = 4,
- 2 = 9,
- 0 = 1,
- };
- fmt.println(foo);
- }
- { // Ranges
- i := 2;
- foo := [?]int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- #assert(len(foo) == 16);
- fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
- }
- { // Slice and Dynamic Array support
- i := 2;
- foo_slice := []int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- assert(len(foo_slice) == 16);
- fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
-
- foo_dynamic_array := [dynamic]int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- assert(len(foo_dynamic_array) == 16);
- fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
- }
-}
-
-deprecated_attribute :: proc() {
- @(deprecated="Use foo_v2 instead")
- foo_v1 :: proc(x: int) {
- fmt.println("foo_v1");
- }
- foo_v2 :: proc(x: int) {
- fmt.println("foo_v2");
- }
-
- // NOTE: Uncomment to see the warning messages
- // foo_v1(1);
-}
-
-range_statements_with_multiple_return_values :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
- fmt.println("\n#range statements with multiple return values");
- My_Iterator :: struct {
- index: int,
- data: []i32,
- };
- make_my_iterator :: proc(data: []i32) -> My_Iterator {
- return My_Iterator{data = data};
- }
- my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) {
- if cond = it.index < len(it.data); cond {
- val = it.data[it.index];
- idx = it.index;
- it.index += 1;
- }
- return;
- }
-
- data := make([]i32, 6);
- for _, i in data {
- data[i] = i32(i*i);
- }
-
- {
- it := make_my_iterator(data);
- for val in my_iterator(&it) {
- fmt.println(val);
- }
- }
- {
- it := make_my_iterator(data);
- for val, idx in my_iterator(&it) {
- fmt.println(val, idx);
- }
- }
- {
- it := make_my_iterator(data);
- for {
- val, _, cond := my_iterator(&it);
- if !cond do break;
- fmt.println(val);
- }
- }
-}
-
-soa_struct_layout :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
- // NOTE(bill): Most likely #soa [N]T
- fmt.println("\n#SOA Struct Layout");
-
- {
- Vector3 :: struct {x, y, z: f32};
-
- N :: 2;
- v_aos: [N]Vector3;
- v_aos[0].x = 1;
- v_aos[0].y = 4;
- v_aos[0].z = 9;
-
- fmt.println(len(v_aos));
- fmt.println(v_aos[0]);
- fmt.println(v_aos[0].x);
- fmt.println(&v_aos[0].x);
-
- v_aos[1] = {0, 3, 4};
- v_aos[1].x = 2;
- fmt.println(v_aos[1]);
- fmt.println(v_aos);
-
- v_soa: #soa[N]Vector3;
-
- v_soa[0].x = 1;
- v_soa[0].y = 4;
- v_soa[0].z = 9;
-
-
- // Same syntax as AOS and treat as if it was an array
- fmt.println(len(v_soa));
- fmt.println(v_soa[0]);
- fmt.println(v_soa[0].x);
- v_soa[1] = {0, 3, 4};
- v_soa[1].x = 2;
- fmt.println(v_soa[1]);
-
- // Can use SOA syntax if necessary
- v_soa.x[0] = 1;
- v_soa.y[0] = 4;
- v_soa.z[0] = 9;
- fmt.println(v_soa.x[0]);
-
- // Same pointer addresses with both syntaxes
- assert(&v_soa[0].x == &v_soa.x[0]);
-
-
- // Same fmt printing
- fmt.println(v_aos);
- fmt.println(v_soa);
- }
- {
- // Works with arrays of length <= 4 which have the implicit fields xyzw/rgba
- Vector3 :: distinct [3]f32;
-
- N :: 2;
- v_aos: [N]Vector3;
- v_aos[0].x = 1;
- v_aos[0].y = 4;
- v_aos[0].z = 9;
-
- v_soa: #soa[N]Vector3;
-
- v_soa[0].x = 1;
- v_soa[0].y = 4;
- v_soa[0].z = 9;
- }
-}
-
-
-main :: proc() {
- when true {
- the_basics();
- control_flow();
- named_proc_return_parameters();
- explicit_procedure_overloading();
- struct_type();
- union_type();
- using_statement();
- implicit_context_system();
- parametric_polymorphism();
- array_programming();
- map_type();
- implicit_selector_expression();
- complete_switch();
- cstring_example();
- bit_set_type();
- deferred_procedure_associations();
- reflection();
- quaternions();
- inline_for_statement();
- where_clauses();
- foreign_system();
- ranged_fields_for_array_compound_literals();
- deprecated_attribute();
- range_statements_with_multiple_return_values();
- soa_struct_layout();
- }
-}
-
diff --git a/src/array.cpp b/src/array.cpp
index c41125c6d..d08bd647f 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -77,15 +77,21 @@ template Slice slice_from_array(Array const &a);
template
Slice slice_make(gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
Slice s = {};
s.data = gb_alloc_array(allocator, T, count);
+ GB_ASSERT(s.data != nullptr);
s.count = count;
return s;
}
template
void slice_init(Slice *s, gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
s->data = gb_alloc_array(allocator, T, count);
+ if (count > 0) {
+ GB_ASSERT(s->data != nullptr);
+ }
s->count = count;
}
diff --git a/src/big_int.cpp b/src/big_int.cpp
index 20f940e8e..5509545ca 100644
--- a/src/big_int.cpp
+++ b/src/big_int.cpp
@@ -40,7 +40,7 @@ typedef mp_int BigInt;
void big_int_from_u64(BigInt *dst, u64 x);
void big_int_from_i64(BigInt *dst, i64 x);
void big_int_init (BigInt *dst, BigInt const *src);
-void big_int_from_string(BigInt *dst, String const &s);
+void big_int_from_string(BigInt *dst, String const &s, bool *success);
void big_int_dealloc(BigInt *dst) {
mp_clear(dst);
@@ -84,7 +84,7 @@ void big_int_quo_eq(BigInt *dst, BigInt const *x);
void big_int_rem_eq(BigInt *dst, BigInt const *x);
bool big_int_is_neg(BigInt const *x);
-
+void big_int_neg(BigInt *dst, BigInt const *x);
void big_int_add_eq(BigInt *dst, BigInt const *x) {
BigInt res = {};
@@ -169,7 +169,11 @@ BigInt big_int_make_i64(i64 x) {
}
-void big_int_from_string(BigInt *dst, String const &s) {
+void big_int_from_string(BigInt *dst, String const &s, bool *success) {
+ *success = true;
+
+ bool is_negative = false;
+
u64 base = 10;
bool has_prefix = false;
if (s.len > 2 && s[0] == '0') {
@@ -197,11 +201,26 @@ void big_int_from_string(BigInt *dst, String const &s) {
isize i = 0;
for (; i < len; i++) {
Rune r = cast(Rune)text[i];
+
+ if (r == '-') {
+ if (is_negative) {
+ // NOTE(Jeroen): Can't have a doubly negative number.
+ *success = false;
+ return;
+ }
+ is_negative = true;
+ continue;
+ }
+
if (r == '_') {
continue;
}
u64 v = u64_digit_value(r);
if (v >= base) {
+ // NOTE(Jeroen): Can still be a valid integer if the next character is an `e` or `E`.
+ if (r != 'e' && r != 'E') {
+ *success = false;
+ }
break;
}
BigInt val = big_int_make_u64(v);
@@ -225,6 +244,7 @@ void big_int_from_string(BigInt *dst, String const &s) {
if (gb_char_is_digit(r)) {
v = u64_digit_value(r);
} else {
+ *success = false;
break;
}
exp *= 10;
@@ -234,6 +254,10 @@ void big_int_from_string(BigInt *dst, String const &s) {
big_int_mul_eq(dst, &b);
}
}
+
+ if (is_negative) {
+ big_int_neg(dst, dst);
+ }
}
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index 27e7fcf9a..02a2b1ba2 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -17,6 +17,11 @@
#include
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include
+ #include
+#endif
+
/*
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
*/
@@ -140,7 +145,7 @@ void report_windows_product_type(DWORD ProductType) {
break;
default:
- gb_printf("Unknown Edition (%08x)", ProductType);
+ gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
}
}
#endif
@@ -242,6 +247,14 @@ void report_ram_info() {
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
}
+ #elif defined(GB_SYSTEM_OPENBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
#else
gb_printf("Unknown.\n");
#endif
@@ -316,14 +329,14 @@ void print_bug_report_help() {
}
if (false) {
- gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion);
- gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion);
- gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber);
- gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId);
- gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor);
- gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor);
- gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask);
- gb_printf("wProductType: %d\n", osvi.wProductType);
+ gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
+ gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
+ gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
+ gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
+ gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
+ gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
+ gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
+ gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
}
gb_printf("Windows ");
@@ -441,18 +454,18 @@ void print_bug_report_help() {
TEXT("DisplayVersion"),
RRF_RT_REG_SZ,
ValueType,
- &DisplayVersion,
+ DisplayVersion,
&ValueSize
);
if (status == 0x0) {
- gb_printf(" (version: %s)", &DisplayVersion);
+ gb_printf(" (version: %s)", DisplayVersion);
}
/*
Now print build number.
*/
- gb_printf(", build %d", osvi.dwBuildNumber);
+ gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
ValueSize = sizeof(UBR);
status = RegGetValue(
@@ -466,18 +479,18 @@ void print_bug_report_help() {
);
if (status == 0x0) {
- gb_printf(".%d", UBR);
+ gb_printf(".%u", cast(unsigned)UBR);
}
gb_printf("\n");
}
#elif defined(GB_SYSTEM_LINUX)
/*
- Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+ Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
*/
gbAllocator a = heap_allocator();
- gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
+ gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
defer (gb_file_free_contents(&release));
b32 found = 0;
@@ -643,6 +656,14 @@ void print_bug_report_help() {
} else {
gb_printf("macOS: Unknown\n");
}
+ #elif defined(GB_SYSTEM_OPENBSD)
+ struct utsname un;
+
+ if (uname(&un) != -1) {
+ gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+ } else {
+ gb_printf("OpenBSD: Unknown\n");
+ }
#else
gb_printf("Unknown\n");
@@ -657,4 +678,4 @@ void print_bug_report_help() {
And RAM info.
*/
report_ram_info();
-}
\ No newline at end of file
+}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 4fa07c808..65da09df0 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -1,14 +1,13 @@
-#if defined(GB_SYSTEM_FREEBSD)
+#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
#include
#include
#endif
-
// #if defined(GB_SYSTEM_WINDOWS)
-#define DEFAULT_TO_THREADED_CHECKER
+// #define DEFAULT_TO_THREADED_CHECKER
// #endif
-enum TargetOsKind {
+enum TargetOsKind : u16 {
TargetOs_Invalid,
TargetOs_windows,
@@ -16,6 +15,7 @@ enum TargetOsKind {
TargetOs_linux,
TargetOs_essence,
TargetOs_freebsd,
+ TargetOs_openbsd,
TargetOs_wasi,
TargetOs_js,
@@ -25,11 +25,12 @@ enum TargetOsKind {
TargetOs_COUNT,
};
-enum TargetArchKind {
+enum TargetArchKind : u16 {
TargetArch_Invalid,
TargetArch_amd64,
- TargetArch_386,
+ TargetArch_i386,
+ TargetArch_arm32,
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64,
@@ -37,7 +38,7 @@ enum TargetArchKind {
TargetArch_COUNT,
};
-enum TargetEndianKind {
+enum TargetEndianKind : u8 {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -46,6 +47,16 @@ enum TargetEndianKind {
TargetEndian_COUNT,
};
+enum TargetABIKind : u16 {
+ TargetABI_Default,
+
+ TargetABI_Win64,
+ TargetABI_SysV,
+
+ TargetABI_COUNT,
+};
+
+
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
@@ -53,6 +64,7 @@ String target_os_names[TargetOs_COUNT] = {
str_lit("linux"),
str_lit("essence"),
str_lit("freebsd"),
+ str_lit("openbsd"),
str_lit("wasi"),
str_lit("js"),
@@ -63,7 +75,8 @@ String target_os_names[TargetOs_COUNT] = {
String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
- str_lit("386"),
+ str_lit("i386"),
+ str_lit("arm32"),
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64"),
@@ -75,12 +88,19 @@ String target_endian_names[TargetEndian_COUNT] = {
str_lit("big"),
};
+String target_abi_names[TargetABI_COUNT] = {
+ str_lit(""),
+ str_lit("win64"),
+ str_lit("sysv"),
+};
+
TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Invalid,
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
+ TargetEndian_Little,
};
#ifndef ODIN_VERSION_RAW
@@ -98,6 +118,7 @@ struct TargetMetrics {
isize max_align;
String target_triplet;
String target_data_layout;
+ TargetABIKind abi;
};
@@ -119,6 +140,8 @@ enum BuildModeKind {
BuildMode_Object,
BuildMode_Assembly,
BuildMode_LLVM_IR,
+
+ BuildMode_COUNT,
};
enum CommandKind : u32 {
@@ -163,19 +186,50 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
+enum ErrorPosStyle {
+ ErrorPosStyle_Default, // path(line:column) msg
+ ErrorPosStyle_Unix, // path:line:column: msg
+
+ ErrorPosStyle_COUNT
+};
+
+enum RelocMode : u8 {
+ RelocMode_Default,
+ RelocMode_Static,
+ RelocMode_PIC,
+ RelocMode_DynamicNoPIC,
+};
+
+enum BuildPath : u8 {
+ BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
+ BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
+ BuildPath_RES, // Output Path for .res file, generated from previous.
+ BuildPath_Win_SDK_Root, // windows_sdk_root
+ BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
+ BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
+ BuildPath_VS_EXE, // vs_exe_path
+ BuildPath_VS_LIB, // vs_library_path
+
+ BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
+ BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
+
+ BuildPathCOUNT,
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
- String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
- String ODIN_BUILD_MODE;
bool ODIN_DEBUG; // Odin in debug mode
bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_FOREIGN_ERROR_PROCEDURES;
+
+ ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
@@ -190,12 +244,17 @@ struct BuildContext {
bool show_help;
+ Array build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
+ // BuildPath enum contains the indices of paths we know *before* the work starts.
+
String out_filepath;
String resource_filepath;
String pdb_filepath;
+
bool has_resource;
String link_flags;
String extra_linker_flags;
+ String extra_assembler_flags;
String microarch;
BuildModeKind build_mode;
bool generate_docs;
@@ -242,6 +301,12 @@ struct BuildContext {
bool copy_file_contents;
+ bool disallow_rtti;
+
+ RelocMode reloc_mode;
+ bool disable_red_zone;
+
+
u32 cmd_doc_flags;
Array extra_packages;
@@ -253,10 +318,13 @@ struct BuildContext {
isize thread_count;
PtrMap defined_values;
+
+ BlockingMutex target_features_mutex;
+ StringSet target_features_set;
+ String target_features_string;
+
};
-
-
gb_global BuildContext build_context = {0};
bool global_warnings_as_errors(void) {
@@ -267,9 +335,9 @@ bool global_ignore_warnings(void) {
}
-gb_global TargetMetrics target_windows_386 = {
+gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-windows-msvc"),
@@ -283,9 +351,9 @@ gb_global TargetMetrics target_windows_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
-gb_global TargetMetrics target_linux_386 = {
+gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-linux-gnu"),
@@ -299,6 +367,23 @@ gb_global TargetMetrics target_linux_amd64 = {
str_lit("x86_64-pc-linux-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_linux_arm64 = {
+ TargetOs_linux,
+ TargetArch_arm64,
+ 8,
+ 16,
+ str_lit("aarch64-linux-elf"),
+ str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
+};
+
+gb_global TargetMetrics target_linux_arm32 = {
+ TargetOs_linux,
+ TargetArch_arm32,
+ 4,
+ 8,
+ str_lit("arm-linux-gnu"),
+ str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
+};
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
@@ -318,9 +403,9 @@ gb_global TargetMetrics target_darwin_arm64 = {
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
};
-gb_global TargetMetrics target_freebsd_386 = {
+gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-unknown-freebsd-elf"),
@@ -335,6 +420,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_openbsd_amd64 = {
+ TargetOs_openbsd,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-unknown-openbsd-elf"),
+ str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -361,6 +455,15 @@ gb_global TargetMetrics target_js_wasm32 = {
str_lit(""),
};
+gb_global TargetMetrics target_js_wasm64 = {
+ TargetOs_js,
+ TargetArch_wasm64,
+ 8,
+ 16,
+ str_lit("wasm64-js-js"),
+ str_lit(""),
+};
+
gb_global TargetMetrics target_wasi_wasm32 = {
TargetOs_wasi,
TargetArch_wasm32,
@@ -380,6 +483,16 @@ gb_global TargetMetrics target_wasi_wasm32 = {
// str_lit(""),
// };
+gb_global TargetMetrics target_freestanding_amd64_sysv = {
+ TargetOs_freestanding,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-pc-none-gnu"),
+ str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
+ TargetABI_SysV,
+};
+
struct NamedTargetMetrics {
@@ -391,16 +504,21 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
{ str_lit("essence_amd64"), &target_essence_amd64 },
- { str_lit("linux_386"), &target_linux_386 },
+ { str_lit("linux_i386"), &target_linux_i386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
- { str_lit("windows_386"), &target_windows_386 },
+ { str_lit("linux_arm64"), &target_linux_arm64 },
+ { str_lit("linux_arm32"), &target_linux_arm32 },
+ { str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
- { str_lit("freebsd_386"), &target_freebsd_386 },
+ { str_lit("freebsd_i386"), &target_freebsd_i386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
+ { str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
- // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
+ { str_lit("js_wasm64"), &target_js_wasm64 },
+
+ { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
};
NamedTargetMetrics *selected_target_metrics;
@@ -514,6 +632,15 @@ bool is_arch_wasm(void) {
return false;
}
+bool is_arch_x86(void) {
+ switch (build_context.metrics.arch) {
+ case TargetArch_i386:
+ case TargetArch_amd64:
+ return true;
+ }
+ return false;
+}
+
bool allow_check_foreign_filepath(void) {
switch (build_context.metrics.arch) {
case TargetArch_wasm32:
@@ -523,7 +650,6 @@ bool allow_check_foreign_filepath(void) {
return true;
}
-
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -702,10 +828,38 @@ String internal_odin_root_dir(void) {
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
-#else
+#elif defined(GB_SYSTEM_LINUX)
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
+#elif defined(GB_SYSTEM_OPENBSD)
+ int error;
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC_ARGS,
+ getpid(),
+ KERN_PROC_ARGV,
+ };
+ // get argv size
+ error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ return make_string(nullptr, 0);
+ }
+ // get argv
+ char **argv = (char **)gb_malloc(len);
+ error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ gb_mfree(argv);
+ return make_string(nullptr, 0);
+ }
+ // copy argv[0] to path_buf
+ len = gb_strlen(argv[0]);
+ if(len < path_buf.count) {
+ gb_memmove(&path_buf[0], argv[0], len);
+ }
+ gb_mfree(argv);
#endif
- if(len == 0) {
+ if(len == 0 || len == -1) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
@@ -821,6 +975,33 @@ bool show_error_line(void) {
return build_context.show_error_line;
}
+bool has_asm_extension(String const &path) {
+ String ext = path_extension(path);
+ if (ext == ".asm") {
+ return true;
+ } else if (ext == ".s") {
+ return true;
+ } else if (ext == ".S") {
+ return true;
+ }
+ return false;
+}
+
+// temporary
+char *token_pos_to_string(TokenPos const &pos) {
+ gbString s = gb_string_make_reserve(temporary_allocator(), 128);
+ String file = get_file_path_string(pos.file_id);
+ switch (build_context.ODIN_ERROR_POS_STYLE) {
+ default: /*fallthrough*/
+ case ErrorPosStyle_Default:
+ s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
+ break;
+ case ErrorPosStyle_Unix:
+ s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
+ break;
+ }
+ return s;
+}
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -833,25 +1014,31 @@ void init_build_context(TargetMetrics *cross_target) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
- switch (bc->build_mode) {
- default:
- case BuildMode_Executable:
- bc->ODIN_BUILD_MODE = str_lit("executable");
- break;
- case BuildMode_DynamicLibrary:
- bc->ODIN_BUILD_MODE = str_lit("dynamic");
- break;
- case BuildMode_Object:
- bc->ODIN_BUILD_MODE = str_lit("object");
- break;
- case BuildMode_Assembly:
- bc->ODIN_BUILD_MODE = str_lit("assembly");
- break;
- case BuildMode_LLVM_IR:
- bc->ODIN_BUILD_MODE = str_lit("llvm-ir");
- break;
+
+ {
+ char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
+ if (found) {
+ ErrorPosStyle kind = ErrorPosStyle_Default;
+ String style = make_string_c(found);
+ style = string_trim_whitespace(style);
+ if (style == "" || style == "default" || style == "odin") {
+ kind = ErrorPosStyle_Default;
+ } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
+ kind = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
+ gb_printf_err("Valid formats:\n");
+ gb_printf_err("\t\"default\" or \"odin\"\n");
+ gb_printf_err("\t\tpath(line:column) message\n");
+ gb_printf_err("\t\"unix\"\n");
+ gb_printf_err("\t\tpath:line:column: message\n");
+ gb_exit(1);
+ }
+
+ build_context.ODIN_ERROR_POS_STYLE = kind;
+ }
}
-
+
bc->copy_file_contents = true;
TargetMetrics *metrics = nullptr;
@@ -867,18 +1054,22 @@ void init_build_context(TargetMetrics *cross_target) {
#endif
#elif defined(GB_SYSTEM_FREEBSD)
metrics = &target_freebsd_amd64;
+ #elif defined(GB_SYSTEM_OPENBSD)
+ metrics = &target_openbsd_amd64;
+ #elif defined(GB_CPU_ARM)
+ metrics = &target_linux_arm64;
#else
metrics = &target_linux_amd64;
#endif
#else
#if defined(GB_SYSTEM_WINDOWS)
- metrics = &target_windows_386;
+ metrics = &target_windows_i386;
#elif defined(GB_SYSTEM_OSX)
#error "Build Error: Unsupported architecture"
#elif defined(GB_SYSTEM_FREEBSD)
- metrics = &target_freebsd_386;
+ metrics = &target_freebsd_i386;
#else
- metrics = &target_linux_386;
+ metrics = &target_linux_i386;
#endif
#endif
@@ -897,7 +1088,6 @@ void init_build_context(TargetMetrics *cross_target) {
bc->metrics = *metrics;
bc->ODIN_OS = target_os_names[metrics->os];
bc->ODIN_ARCH = target_arch_names[metrics->arch];
- bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics->arch]];
bc->endian_kind = target_endians[metrics->arch];
bc->word_size = metrics->word_size;
bc->max_align = metrics->max_align;
@@ -907,6 +1097,21 @@ void init_build_context(TargetMetrics *cross_target) {
bc->threaded_checker = true;
#endif
+ if (bc->disable_red_zone) {
+ if (is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) {
+ gb_printf_err("-disable-red-zone is not support for this target");
+ gb_exit(1);
+ }
+ }
+
+ if (bc->metrics.os == TargetOs_freestanding) {
+ bc->no_entry_point = true;
+ } else {
+ if (bc->disallow_rtti) {
+ gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n");
+ gb_exit(1);
+ }
+ }
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
@@ -924,8 +1129,11 @@ void init_build_context(TargetMetrics *cross_target) {
case TargetOs_freebsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_openbsd:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
- } else if (bc->metrics.arch == TargetArch_386) {
+ } else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
@@ -941,11 +1149,23 @@ void init_build_context(TargetMetrics *cross_target) {
bc->link_flags = str_lit("-arch x86 ");
break;
}
+ } else if (bc->metrics.arch == TargetArch_arm32) {
+ switch (bc->metrics.os) {
+ case TargetOs_linux:
+ bc->link_flags = str_lit("-arch arm ");
+ break;
+ default:
+ gb_printf_err("Compiler Error: Unsupported architecture\n");
+ gb_exit(1);
+ }
} else if (bc->metrics.arch == TargetArch_arm64) {
switch (bc->metrics.os) {
case TargetOs_darwin:
bc->link_flags = str_lit("-arch arm64 ");
break;
+ case TargetOs_linux:
+ bc->link_flags = str_lit("-arch aarch64 ");
+ break;
}
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
@@ -953,16 +1173,16 @@ void init_build_context(TargetMetrics *cross_target) {
// link_flags = gb_string_appendc(link_flags, "--export-table ");
link_flags = gb_string_appendc(link_flags, "--allow-undefined ");
if (bc->metrics.arch == TargetArch_wasm64) {
- link_flags = gb_string_appendc(link_flags, "-mwas64 ");
+ link_flags = gb_string_appendc(link_flags, "-mwasm64 ");
}
- if (bc->metrics.os == TargetOs_freestanding) {
+ if (bc->no_entry_point) {
link_flags = gb_string_appendc(link_flags, "--no-entry ");
}
bc->link_flags = make_string_c(link_flags);
// Disallow on wasm
- build_context.use_separate_modules = false;
+ bc->use_separate_modules = false;
} else {
gb_printf_err("Compiler Error: Unsupported architecture\n");
gb_exit(1);
@@ -973,3 +1193,305 @@ void init_build_context(TargetMetrics *cross_target) {
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
+
+#if defined(GB_SYSTEM_WINDOWS)
+// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
+// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
+#include "microsoft_craziness.h"
+#endif
+
+
+Array split_by_comma(String const &list) {
+ isize n = 1;
+ for (isize i = 0; i < list.len; i++) {
+ if (list.text[i] == ',') {
+ n++;
+ }
+ }
+ auto res = array_make(heap_allocator(), n);
+
+ String s = list;
+ for (isize i = 0; i < n; i++) {
+ isize m = string_index_byte(s, ',');
+ if (m < 0) {
+ res[i] = s;
+ break;
+ }
+ res[i] = substring(s, 0, m);
+ s = substring(s, m+1, s.len);
+ }
+ return res;
+}
+
+bool check_target_feature_is_valid(TokenPos pos, String const &feature) {
+ // TODO(bill): check_target_feature_is_valid
+ return true;
+}
+
+bool check_target_feature_is_enabled(TokenPos pos, String const &target_feature_list) {
+ BuildContext *bc = &build_context;
+ mutex_lock(&bc->target_features_mutex);
+ defer (mutex_unlock(&bc->target_features_mutex));
+
+ auto items = split_by_comma(target_feature_list);
+ array_free(&items);
+ for_array(i, items) {
+ String const &item = items.data[i];
+ if (!check_target_feature_is_valid(pos, item)) {
+ error(pos, "Target feature '%.*s' is not valid", LIT(item));
+ return false;
+ }
+ if (!string_set_exists(&bc->target_features_set, item)) {
+ error(pos, "Target feature '%.*s' is not enabled", LIT(item));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void enable_target_feature(TokenPos pos, String const &target_feature_list) {
+ BuildContext *bc = &build_context;
+ mutex_lock(&bc->target_features_mutex);
+ defer (mutex_unlock(&bc->target_features_mutex));
+
+ auto items = split_by_comma(target_feature_list);
+ array_free(&items);
+ for_array(i, items) {
+ String const &item = items.data[i];
+ if (!check_target_feature_is_valid(pos, item)) {
+ error(pos, "Target feature '%.*s' is not valid", LIT(item));
+ }
+ }
+}
+
+
+char const *target_features_set_to_cstring(gbAllocator allocator, bool with_quotes) {
+ isize len = 0;
+ for_array(i, build_context.target_features_set.entries) {
+ if (i != 0) {
+ len += 1;
+ }
+ String feature = build_context.target_features_set.entries[i].value;
+ len += feature.len;
+ if (with_quotes) len += 2;
+ }
+ char *features = gb_alloc_array(allocator, char, len+1);
+ len = 0;
+ for_array(i, build_context.target_features_set.entries) {
+ if (i != 0) {
+ features[len++] = ',';
+ }
+
+ if (with_quotes) features[len++] = '"';
+ String feature = build_context.target_features_set.entries[i].value;
+ gb_memmove(features, feature.text, feature.len);
+ len += feature.len;
+ if (with_quotes) features[len++] = '"';
+ }
+ features[len++] = 0;
+
+ return features;
+}
+
+// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
+// We've previously called `parse_build_flags`, so `out_filepath` should be set.
+bool init_build_paths(String init_filename) {
+ gbAllocator ha = heap_allocator();
+ BuildContext *bc = &build_context;
+
+ // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
+ array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
+
+ string_set_init(&bc->target_features_set, heap_allocator(), 1024);
+ mutex_init(&bc->target_features_mutex);
+
+ // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
+ bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
+
+ bool produces_output_file = false;
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ produces_output_file = true;
+ } else if (bc->command_kind & Command__does_build) {
+ produces_output_file = true;
+ }
+
+ if (!produces_output_file) {
+ // Command doesn't produce output files. We're done.
+ return true;
+ }
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->metrics.os == TargetOs_windows) {
+ if (bc->resource_filepath.len > 0) {
+ bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
+ bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ }
+
+ if (bc->pdb_filepath.len > 0) {
+ bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
+ }
+
+ if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
+ // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+ Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+ defer (mc_free_all());
+
+ if (find_result.windows_sdk_version == 0) {
+ gb_printf_err("Windows SDK not found.\n");
+ return false;
+ }
+
+ if (!build_context.use_lld && find_result.vs_exe_path.len == 0) {
+ gb_printf_err("link.exe not found.\n");
+ return false;
+ }
+
+ if (find_result.vs_library_path.len == 0) {
+ gb_printf_err("VS library path not found.\n");
+ return false;
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+
+ if (find_result.windows_sdk_root.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
+ }
+
+ if (find_result.windows_sdk_ucrt_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
+ }
+
+ if (find_result.vs_exe_path.len > 0) {
+ bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
+ }
+
+ if (find_result.vs_library_path.len > 0) {
+ bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
+ }
+ }
+ }
+ }
+ #endif
+
+ // All the build targets and OSes.
+ String output_extension;
+
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ output_extension = STR_LIT("odin-doc");
+ } else if (is_arch_wasm()) {
+ output_extension = STR_LIT("wasm");
+ } else if (build_context.build_mode == BuildMode_Executable) {
+ // By default use a .bin executable extension.
+ output_extension = STR_LIT("bin");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("exe");
+ } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+ output_extension = make_string(nullptr, 0);
+ }
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // By default use a .so shared library extension.
+ output_extension = STR_LIT("so");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("dll");
+ } else if (build_context.metrics.os == TargetOs_darwin) {
+ output_extension = STR_LIT("dylib");
+ }
+ } else if (build_context.build_mode == BuildMode_Object) {
+ // By default use a .o object extension.
+ output_extension = STR_LIT("o");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("obj");
+ }
+ } else if (build_context.build_mode == BuildMode_Assembly) {
+ // By default use a .S asm extension.
+ output_extension = STR_LIT("S");
+ } else if (build_context.build_mode == BuildMode_LLVM_IR) {
+ output_extension = STR_LIT("ll");
+ } else {
+ GB_PANIC("Unhandled build mode/target combination.\n");
+ }
+
+ if (bc->out_filepath.len > 0) {
+ bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
+ if (build_context.metrics.os == TargetOs_windows) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ } else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
+ return false;
+ }
+ }
+ } else {
+ Path output_path;
+
+ if (str_eq(init_filename, str_lit("."))) {
+ // We must name the output file after the current directory.
+ debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
+
+ if (last_element.len == 0) {
+ gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ return false;
+ }
+ output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
+ output_path.name = copy_string(ha, last_element);
+
+ } else {
+ // Init filename was not 'current path'.
+ // Contruct the output name from the path elements as usual.
+ String output_name = init_filename;
+ // If it ends with a trailing (back)slash, strip it before continuing.
+ while (output_name.len > 0 && (output_name[output_name.len-1] == '/' || output_name[output_name.len-1] == '\\')) {
+ output_name.len -= 1;
+ }
+ output_name = remove_directory_from_path(output_name);
+ output_name = remove_extension_from_path(output_name);
+ output_name = copy_string(ha, string_trim_whitespace(output_name));
+ output_path = path_from_string(ha, output_name);
+
+ // Replace extension.
+ if (output_path.ext.len > 0) {
+ gb_free(ha, output_path.ext.text);
+ }
+ }
+ output_path.ext = copy_string(ha, output_extension);
+
+ bc->build_paths[BuildPath_Output] = output_path;
+ }
+
+ // Do we have an extension? We might not if the output filename was supplied.
+ if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
+ bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
+ }
+ }
+
+ // Check if output path is a directory.
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ }
+
+ if (bc->target_features_string.len != 0) {
+ enable_target_feature({}, bc->target_features_string);
+ }
+
+ return true;
+}
+
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index d910314fb..8108604ba 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -29,6 +29,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
is_type_named,
is_type_pointer,
+ is_type_multi_pointer,
is_type_array,
is_type_enumerated_array,
is_type_slice,
@@ -143,6 +144,936 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
}
+bool does_require_msgSend_stret(Type *return_type) {
+ if (return_type == nullptr) {
+ return false;
+ }
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ i64 struct_limit = type_size_of(t_uintptr) << 1;
+ return type_size_of(return_type) > struct_limit;
+ }
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ return false;
+ }
+
+ // if (build_context.metrics.arch == TargetArch_arm32) {
+ // i64 struct_limit = type_size_of(t_uintptr);
+ // // NOTE(bill): This is technically wrong
+ // return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit;
+ // }
+ GB_PANIC("unsupported architecture");
+ return false;
+}
+
+ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+ if (return_type == nullptr) {
+ return ObjcMsg_normal;
+ }
+
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_float(return_type)) {
+ return ObjcMsg_fpret;
+ }
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_complex(return_type)) {
+ // URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159
+ return ObjcMsg_fpret;
+ }
+ }
+ }
+ if (build_context.metrics.arch != TargetArch_arm64) {
+ if (does_require_msgSend_stret(return_type)) {
+ return ObjcMsg_stret;
+ }
+ }
+ return ObjcMsg_normal;
+}
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) {
+ ObjcMsgKind kind = get_objc_proc_kind(return_type);
+
+ Scope *scope = create_scope(c->info, nullptr);
+
+ // NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope
+ Type *params = alloc_type_tuple();
+ {
+ auto variables = array_make(permanent_allocator(), 0, param_types.count);
+
+ for_array(i, param_types) {
+ Type *type = param_types[i];
+ Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
+ array_add(&variables, param);
+ }
+ params->Tuple.variables = slice_from_array(variables);
+ }
+
+ Type *results = alloc_type_tuple();
+ if (return_type) {
+ auto variables = array_make(permanent_allocator(), 1);
+ results->Tuple.variables = slice_from_array(variables);
+ Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true);
+ results->Tuple.variables[0] = param;
+ }
+
+
+ ObjcMsgData data = {};
+ data.kind = kind;
+ data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+ mutex_lock(&c->info->objc_types_mutex);
+ map_set(&c->info->objc_msgSend_types, call, data);
+ mutex_unlock(&c->info->objc_types_mutex);
+
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+}
+
+bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
+ Operand op = {};
+ check_expr(c, &op, expr);
+ if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
+ if (name_) *name_ = op.value.value_string;
+ return true;
+ }
+ gbString e = expr_to_string(op.expr);
+ gbString t = type_to_string(op.type);
+ error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+}
+
+bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+ String const &builtin_name = builtin_procs[id].name;
+
+ if (build_context.metrics.os != TargetOs_darwin) {
+ // allow on doc generation (e.g. Metal stuff)
+ if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
+ error(call, "'%.*s' only works on darwin", LIT(builtin_name));
+ }
+ }
+
+
+ ast_node(ce, CallExpr, call);
+ switch (id) {
+ default:
+ GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name));
+ return false;
+
+ case BuiltinProc_objc_send: {
+ Type *return_type = nullptr;
+
+ Operand rt = {};
+ check_expr_or_type(c, &rt, ce->args[0]);
+ if (rt.mode == Addressing_Type) {
+ return_type = rt.type;
+ } else if (is_operand_nil(rt)) {
+ return_type = nullptr;
+ } else {
+ gbString e = expr_to_string(rt.expr);
+ error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->type = return_type;
+ operand->mode = return_type ? Addressing_Value : Addressing_NoValue;
+
+ String class_name = {};
+ String sel_name = {};
+
+ Type *sel_type = t_objc_SEL;
+ Operand self = {};
+ check_expr_or_type(c, &self, ce->args[1]);
+ if (self.mode == Addressing_Type) {
+ if (!is_type_objc_object(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (!has_type_got_objc_class_attribute(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ sel_type = t_objc_Class;
+ } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else if (!is_type_pointer(self.type)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else {
+ Type *type = type_deref(self.type);
+ if (!(type->kind == Type_Named &&
+ type->Named.type_name != nullptr &&
+ type->Named.type_name->TypeName.objc_class_name != "")) {
+ gbString t = type_to_string(type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ }
+
+
+ if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) {
+ return false;
+ }
+
+ isize const arg_offset = 1;
+ auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset);
+ param_types[0] = t_objc_id;
+ param_types[1] = sel_type;
+
+ for (isize i = 2+arg_offset; i < ce->args.count; i++) {
+ Operand x = {};
+ check_expr(c, &x, ce->args[i]);
+ param_types[i-arg_offset] = x.type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+
+ return true;
+ } break;
+
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ {
+ String sel_name = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) {
+ return false;
+ }
+
+ switch (id) {
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_register_selector:
+ operand->type = t_objc_SEL;
+ break;
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_class:
+ operand->type = t_objc_Class;
+ break;
+
+ }
+ operand->mode = Addressing_Value;
+
+ try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
+ try_to_add_package_dependency(c, "runtime", "sel_registerName");
+ try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
+ return true;
+ } break;
+ }
+}
+
+bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) {
+ gbString str = type_to_string(x.type);
+ if (extra_message) {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str);
+ } else {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str);
+ }
+ gb_string_free(str);
+ return false;
+ }
+ i64 value = exact_value_to_i64(x.value);
+ if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) {
+ error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value);
+ return false;
+ }
+ if (memory_order_) {
+ *memory_order_ = cast(OdinAtomicMemoryOrder)value;
+ }
+
+ return true;
+
+}
+
+
+bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+ ast_node(ce, CallExpr, call);
+
+ String const &builtin_name = builtin_procs[id].name;
+ switch (id) {
+ // Any numeric
+ case BuiltinProc_simd_add:
+ case BuiltinProc_simd_sub:
+ case BuiltinProc_simd_mul:
+ case BuiltinProc_simd_div:
+ case BuiltinProc_simd_min:
+ case BuiltinProc_simd_max:
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString ys = type_to_string(y.type);
+ error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
+ gb_string_free(ys);
+ gb_string_free(xs);
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer(elem) && !is_type_float(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ if (id == BuiltinProc_simd_div && is_type_integer(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' is not supported for integer elements, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ // don't return
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ // Integer only
+ case BuiltinProc_simd_add_sat:
+ case BuiltinProc_simd_sub_sat:
+ case BuiltinProc_simd_and:
+ case BuiltinProc_simd_or:
+ case BuiltinProc_simd_xor:
+ case BuiltinProc_simd_and_not:
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString ys = type_to_string(y.type);
+ error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
+ gb_string_free(ys);
+ gb_string_free(xs);
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+
+ switch (id) {
+ case BuiltinProc_simd_add_sat:
+ case BuiltinProc_simd_sub_sat:
+ if (!is_type_integer(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ break;
+ default:
+ if (!is_type_integer(elem) && !is_type_boolean(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ break;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ case BuiltinProc_simd_shl: // Odin-like
+ case BuiltinProc_simd_shr: // Odin-like
+ case BuiltinProc_simd_shl_masked: // C-like
+ case BuiltinProc_simd_shr_masked: // C-like
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ GB_ASSERT(x.type->kind == Type_SimdVector);
+ GB_ASSERT(y.type->kind == Type_SimdVector);
+ Type *xt = x.type;
+ Type *yt = y.type;
+
+ if (xt->SimdVector.count != yt->SimdVector.count) {
+ error(x.expr, "'%.*s' mismatched simd vector lengths, got '%lld' vs '%lld'",
+ LIT(builtin_name),
+ cast(long long)xt->SimdVector.count,
+ cast(long long)yt->SimdVector.count);
+ return false;
+ }
+ if (!is_type_integer(base_array_type(x.type))) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ if (!is_type_unsigned(base_array_type(y.type))) {
+ gbString ys = type_to_string(y.type);
+ error(y.expr, "'%.*s' expected a #simd type with an unsigned integer element as the shifting operand, got '%s'", LIT(builtin_name), ys);
+ gb_string_free(ys);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ // Unary
+ case BuiltinProc_simd_neg:
+ case BuiltinProc_simd_abs:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer(elem) && !is_type_float(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ // Return integer masks
+ case BuiltinProc_simd_lanes_eq:
+ case BuiltinProc_simd_lanes_ne:
+ case BuiltinProc_simd_lanes_lt:
+ case BuiltinProc_simd_lanes_le:
+ case BuiltinProc_simd_lanes_gt:
+ case BuiltinProc_simd_lanes_ge:
+ {
+ // op(#simd[N]T, #simd[N]T) -> #simd[N]V
+ // where `V` is an integer, `size_of(T) == size_of(V)`
+ // `V` will all 0s if false and all 1s if true (e.g. 0x00 and 0xff for false and true, respectively)
+
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ switch (id) {
+ case BuiltinProc_simd_lanes_eq:
+ case BuiltinProc_simd_lanes_ne:
+ if (!is_type_integer(elem) && !is_type_float(elem) && !is_type_boolean(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer, floating point, or boolean element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ break;
+ default:
+ if (!is_type_integer(elem) && !is_type_float(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ break;
+ }
+
+
+ Type *vt = base_type(x.type);
+ GB_ASSERT(vt->kind == Type_SimdVector);
+ i64 count = vt->SimdVector.count;
+
+ i64 sz = type_size_of(elem);
+ Type *new_elem = nullptr;
+
+ switch (sz) {
+ case 1: new_elem = t_u8; break;
+ case 2: new_elem = t_u16; break;
+ case 4: new_elem = t_u32; break;
+ case 8: new_elem = t_u64; break;
+ case 16:
+ error(x.expr, "'%.*s' not supported 128-bit integer backed simd vector types", LIT(builtin_name));
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = alloc_type_simd_vector(count, new_elem);
+ return true;
+ }
+
+ case BuiltinProc_simd_extract:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ i64 max_count = x.type->SimdVector.count;
+ i64 value = -1;
+ if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) {
+ return false;
+ }
+ if (max_count < 0) {
+ error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = elem;
+ return true;
+ }
+ break;
+ case BuiltinProc_simd_replace:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ i64 max_count = x.type->SimdVector.count;
+ i64 value = -1;
+ if (!check_index_value(c, x.type, false, ce->args[1], max_count, &value)) {
+ return false;
+ }
+ if (max_count < 0) {
+ error(ce->args[1], "'%.*s' expected a constant integer index, got '%lld'", LIT(builtin_name), cast(long long)value);
+ return false;
+ }
+
+ Operand y = {};
+ check_expr_with_type_hint(c, &y, ce->args[2], elem); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, elem); if (y.mode == Addressing_Invalid) return false;
+ if (!are_types_identical(y.type, elem)) {
+ gbString et = type_to_string(elem);
+ gbString yt = type_to_string(y.type);
+ error(y.expr, "'%.*s' expected a type of '%s' to insert, got '%s'", LIT(builtin_name), et, yt);
+ gb_string_free(yt);
+ gb_string_free(et);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+ break;
+
+ case BuiltinProc_simd_reduce_add_ordered:
+ case BuiltinProc_simd_reduce_mul_ordered:
+ case BuiltinProc_simd_reduce_min:
+ case BuiltinProc_simd_reduce_max:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer(elem) && !is_type_float(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = base_array_type(x.type);
+ return true;
+ }
+
+ case BuiltinProc_simd_reduce_and:
+ case BuiltinProc_simd_reduce_or:
+ case BuiltinProc_simd_reduce_xor:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer(elem) && !is_type_boolean(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = base_array_type(x.type);
+ return true;
+ }
+
+
+ case BuiltinProc_simd_shuffle:
+ {
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString ys = type_to_string(y.type);
+ error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
+ gb_string_free(ys);
+ gb_string_free(xs);
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+
+ i64 max_count = x.type->SimdVector.count + y.type->SimdVector.count;
+
+ i64 arg_count = 0;
+ for_array(i, ce->args) {
+ if (i < 2) {
+ continue;
+ }
+ Ast *arg = ce->args[i];
+ Operand op = {};
+ check_expr(c, &op, arg);
+ if (op.mode == Addressing_Invalid) {
+ return false;
+ }
+ Type *arg_type = base_type(op.type);
+ if (!is_type_integer(arg_type) || op.mode != Addressing_Constant) {
+ error(op.expr, "Indices to '%.*s' must be constant integers", LIT(builtin_name));
+ return false;
+ }
+
+ if (big_int_is_neg(&op.value.value_integer)) {
+ error(op.expr, "Negative '%.*s' index", LIT(builtin_name));
+ return false;
+ }
+
+ BigInt mc = {};
+ big_int_from_i64(&mc, max_count);
+ if (big_int_cmp(&mc, &op.value.value_integer) <= 0) {
+ error(op.expr, "'%.*s' index exceeds length", LIT(builtin_name));
+ return false;
+ }
+
+ arg_count++;
+ }
+
+ if (arg_count > max_count) {
+ error(call, "Too many '%.*s' indices, %td > %td", LIT(builtin_name), arg_count, max_count);
+ return false;
+ }
+
+
+ if (!is_power_of_two(arg_count)) {
+ error(call, "'%.*s' must have a power of two index arguments, got %lld", LIT(builtin_name), cast(long long)arg_count);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = alloc_type_simd_vector(arg_count, elem);
+ return true;
+ }
+
+ case BuiltinProc_simd_select:
+ {
+ Operand cond = {};
+ check_expr(c, &cond, ce->args[0]); if (cond.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(cond.type)) {
+ error(cond.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name));
+ return false;
+ }
+ Type *cond_elem = base_array_type(cond.type);
+ if (!is_type_boolean(cond_elem) && !is_type_integer(cond_elem)) {
+ gbString cond_str = type_to_string(cond.type);
+ error(cond.expr, "'%.*s' expected a simd vector boolean or integer type, got '%s'", LIT(builtin_name), cond_str);
+ gb_string_free(cond_str);
+ return false;
+ }
+
+ Operand x = {};
+ Operand y = {};
+ check_expr(c, &x, ce->args[1]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[2], x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString ys = type_to_string(y.type);
+ error(x.expr, "'%.*s' expected 2 results of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
+ gb_string_free(ys);
+ gb_string_free(xs);
+ return false;
+ }
+
+ if (cond.type->SimdVector.count != x.type->SimdVector.count) {
+ error(x.expr, "'%.*s' expected condition vector to match the length of the result lengths, got '%lld' vs '%lld'",
+ LIT(builtin_name),
+ cast(long long)cond.type->SimdVector.count,
+ cast(long long)x.type->SimdVector.count);
+ return false;
+ }
+
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ case BuiltinProc_simd_ceil:
+ case BuiltinProc_simd_floor:
+ case BuiltinProc_simd_trunc:
+ case BuiltinProc_simd_nearest:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector boolean type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_float(elem)) {
+ gbString x_str = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a simd vector floating point type, got '%s'", LIT(builtin_name), x_str);
+ gb_string_free(x_str);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ case BuiltinProc_simd_lanes_reverse:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ operand->type = x.type;
+ operand->mode = Addressing_Value;
+ return true;
+ }
+
+ case BuiltinProc_simd_lanes_rotate_left:
+ case BuiltinProc_simd_lanes_rotate_right:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Operand offset = {};
+ check_expr(c, &offset, ce->args[1]); if (offset.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &offset, t_i64);
+ if (!is_type_integer(offset.type) || offset.mode != Addressing_Constant) {
+ error(offset.expr, "'%.*s' expected a constant integer offset");
+ return false;
+ }
+ check_assignment(c, &offset, t_i64, builtin_name);
+
+ operand->type = x.type;
+ operand->mode = Addressing_Value;
+ return true;
+ }
+
+ case BuiltinProc_simd_clamp:
+ {
+ Operand x = {};
+ Operand y = {};
+ Operand z = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &y, ce->args[1], x.type); if (y.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &z, ce->args[2], x.type); if (z.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &z, x.type);
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(y.type)) {
+ error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!is_type_simd_vector(z.type)) {
+ error(z.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString ys = type_to_string(y.type);
+ error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, ys);
+ gb_string_free(ys);
+ gb_string_free(xs);
+ return false;
+ }
+ if (!are_types_identical(x.type, z.type)) {
+ gbString xs = type_to_string(x.type);
+ gbString zs = type_to_string(z.type);
+ error(x.expr, "'%.*s' expected 2 arguments of the same type, got '%s' vs '%s'", LIT(builtin_name), xs, zs);
+ gb_string_free(zs);
+ gb_string_free(xs);
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer(elem) && !is_type_float(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with an integer or floating point element, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = x.type;
+ return true;
+ }
+
+ case BuiltinProc_simd_to_bits:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name));
+ return false;
+ }
+ Type *elem = base_array_type(x.type);
+ i64 count = get_array_type_count(x.type);
+ i64 sz = type_size_of(elem);
+ Type *bit_elem = nullptr;
+ switch (sz) {
+ case 1: bit_elem = t_u8; break;
+ case 2: bit_elem = t_u16; break;
+ case 4: bit_elem = t_u32; break;
+ case 8: bit_elem = t_u64; break;
+ }
+ GB_ASSERT(bit_elem != nullptr);
+
+ operand->type = alloc_type_simd_vector(count, bit_elem);
+ operand->mode = Addressing_Value;
+ return true;
+ }
+
+ case BuiltinProc_simd_x86__MM_SHUFFLE:
+ {
+ Operand x[4] = {};
+ for (unsigned i = 0; i < 4; i++) {
+ check_expr(c, x+i, ce->args[i]); if (x[i].mode == Addressing_Invalid) return false;
+ }
+
+ u32 offsets[4] = {6, 4, 2, 0};
+ u32 result = 0;
+ for (unsigned i = 0; i < 4; i++) {
+ if (!is_type_integer(x[i].type) || x[i].mode != Addressing_Constant) {
+ gbString xs = type_to_string(x[i].type);
+ error(x[i].expr, "'%.*s' expected a constant integer", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+ i64 val = exact_value_to_i64(x[i].value);
+ if (val < 0 || val > 3) {
+ error(x[i].expr, "'%.*s' expected a constant integer in the range 0..<4, got %lld", LIT(builtin_name), cast(long long)val);
+ return false;
+ }
+ result |= cast(u32)(val) << offsets[i];
+ }
+
+ operand->type = t_untyped_integer;
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_i64(result);
+ return true;
+ }
+ default:
+ GB_PANIC("Unhandled simd intrinsic: %.*s", LIT(builtin_name));
+ }
+
+ return false;
+}
+
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -179,9 +1110,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_len:
case BuiltinProc_min:
case BuiltinProc_max:
+ case BuiltinProc_type_is_subtype_of:
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ case BuiltinProc_atomic_type_is_lock_free:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ // NOTE(bill): first type will require a type hint
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -202,7 +1145,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- String builtin_name = builtin_procs[id].name;;
+ String const &builtin_name = builtin_procs[id].name;
if (ce->args.count > 0) {
@@ -214,11 +1157,35 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
+ if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
+ bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint);
+ if (!ok) {
+ operand->type = t_invalid;
+ }
+ operand->mode = Addressing_Value;
+ operand->value = {};
+ operand->expr = call;
+ return ok;
+ }
+
switch (id) {
default:
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
break;
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ return check_builtin_objc_procedure(c, operand, call, id, type_hint);
+
+ case BuiltinProc___entry_point:
+ operand->mode = Addressing_NoValue;
+ operand->type = nullptr;
+ mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -446,9 +1413,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
} else if (hash_kind == "fnv64") {
hash_value = gb_fnv64(data, file_size);
} else if (hash_kind == "fnv32a") {
- hash_value = gb_fnv32a(data, file_size);
+ hash_value = fnv32a(data, file_size);
} else if (hash_kind == "fnv64a") {
- hash_value = gb_fnv64a(data, file_size);
+ hash_value = fnv64a(data, file_size);
} else if (hash_kind == "murmur32") {
hash_value = gb_murmur32(data, file_size);
} else if (hash_kind == "murmur64") {
@@ -542,8 +1509,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
} else if (name == "assert") {
- if (ce->args.count != 1) {
- error(call, "'#assert' expects 1 argument, got %td", ce->args.count);
+ if (ce->args.count != 1 && ce->args.count != 2) {
+ error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
return false;
}
if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
@@ -552,15 +1519,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(str);
return false;
}
+ if (ce->args.count == 2) {
+ Ast *arg = unparen_expr(ce->args[1]);
+ if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
+ gbString str = expr_to_string(arg);
+ error(call, "'%s' is not a constant string", str);
+ gb_string_free(str);
+ return false;
+ }
+ }
+
if (!operand->value.value_bool) {
- gbString arg = expr_to_string(ce->args[0]);
- error(call, "Compile time assertion: %s", arg);
+ gbString arg1 = expr_to_string(ce->args[0]);
+ gbString arg2 = {};
+
+ if (ce->args.count == 1) {
+ error(call, "Compile time assertion: %s", arg1);
+ } else {
+ arg2 = expr_to_string(ce->args[1]);
+ error(call, "Compile time assertion: %s (%s)", arg1, arg2);
+ }
+
if (c->proc_name != "") {
gbString str = type_to_string(c->curr_proc_sig);
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
gb_string_free(str);
}
- gb_string_free(arg);
+
+ gb_string_free(arg1);
+ if (ce->args.count == 2) {
+ gb_string_free(arg2);
+ }
}
operand->type = t_untyped_bool;
@@ -698,7 +1687,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
mode = Addressing_Constant;
value = exact_value_i64(at->EnumeratedArray.count);
type = t_untyped_integer;
- } else if (is_type_slice(op_type) && id == BuiltinProc_len) {
+ } else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
mode = Addressing_Value;
} else if (is_type_dynamic_array(op_type)) {
mode = Addressing_Value;
@@ -719,6 +1708,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
bt->Struct.soa_kind == StructSoa_Dynamic) {
mode = Addressing_Value;
}
+ } else if (is_type_simd_vector(op_type)) {
+ Type *bt = base_type(op_type);
+ mode = Addressing_Constant;
+ value = exact_value_i64(bt->SimdVector.count);
+ type = t_untyped_integer;
}
if (operand->mode == Addressing_Type && mode != Addressing_Constant) {
mode = Addressing_Invalid;
@@ -852,7 +1846,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -864,7 +1858,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -925,7 +1919,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -937,7 +1931,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -987,6 +1981,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -999,9 +1997,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) {
if (is_type_polymorphic(t)) {
- error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type");
+ error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name));
} else {
- error(ce->args[0], "Invalid argument for 'type_info_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
}
return false;
}
@@ -1012,7 +2010,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (is_operand_value(o) && is_type_typeid(t)) {
add_package_dependency(c, "runtime", "__type_info_of");
} else if (o.mode != Addressing_Type) {
- error(expr, "Expected a type or typeid for 'type_info_of'");
+ error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1026,6 +2024,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -1037,7 +2039,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) {
- error(ce->args[0], "Invalid argument for 'typeid_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
return false;
}
t = default_type(t);
@@ -1045,7 +2047,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
add_type_info_type(c, t);
if (o.mode != Addressing_Type) {
- error(expr, "Expected a type for 'typeid_of'");
+ error(expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1125,6 +2127,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_Value;
}
+ if (is_type_simd_vector(type) && !is_power_of_two(arg_count)) {
+ error(call, "'swizzle' with a #simd vector must have a power of two arguments, got %lld", cast(long long)arg_count);
+ return false;
+ }
+
operand->type = determine_swizzle_array_type(original_type, type_hint, arg_count);
break;
}
@@ -1858,7 +2865,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
f64 r = operand->value.value_complex->real;
f64 i = operand->value.value_complex->imag;
operand->value = exact_value_float(gb_sqrt(r*r + i*i));
-
+ break;
+ }
+ case ExactValue_Quaternion: {
+ f64 r = operand->value.value_quaternion->real;
+ f64 i = operand->value.value_quaternion->imag;
+ f64 j = operand->value.value_quaternion->jmag;
+ f64 k = operand->value.value_quaternion->kmag;
+ operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k));
break;
}
default:
@@ -1877,10 +2891,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
- if (is_type_complex(operand->type)) {
+ if (is_type_complex_or_quaternion(operand->type)) {
operand->type = base_complex_elem_type(operand->type);
}
- GB_ASSERT(!is_type_complex(operand->type));
+ GB_ASSERT(!is_type_complex_or_quaternion(operand->type));
break;
}
@@ -1952,7 +2966,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (i == j) continue;
Operand *b = ops[j];
convert_to_typed(c, a, b->type);
- if (a->mode == Addressing_Invalid) { return false; }
+ if (a->mode == Addressing_Invalid) return false;
}
}
@@ -2170,9 +3184,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
operand->mode = Addressing_Value;
- if (is_type_array(t)) {
+ if (t->kind == Type_Array) {
+ i32 rank = type_math_rank(t);
// Do nothing
- operand->type = x.type;
+ operand->type = x.type;
+ if (rank > 2) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank);
+ gb_string_free(s);
+ return false;
+ } else if (rank == 2) {
+ Type *inner = base_type(t->Array.elem);
+ GB_ASSERT(inner->kind == Type_Array);
+ Type *elem = inner->Array.elem;
+ Type *array_inner = alloc_type_array(elem, t->Array.count);
+ Type *array_outer = alloc_type_array(array_inner, inner->Array.count);
+ operand->type = array_outer;
+
+ i64 elements = t->Array.count*inner->Array.count;
+ i64 size = type_size_of(operand->type);
+ if (!is_type_valid_for_matrix_elems(elem)) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s);
+ gb_string_free(s);
+ } else if (size > MATRIX_ELEMENT_MAX_SIZE) {
+ gbString s = type_to_string(x.type);
+ error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE);
+ gb_string_free(s);
+ }
+ }
} else {
GB_ASSERT(t->kind == Type_Matrix);
operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
@@ -2324,46 +3372,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- case BuiltinProc_simd_vector: {
- Operand x = {};
- Operand y = {};
- x = *operand;
- if (!is_type_integer(x.type) || x.mode != Addressing_Constant) {
- error(call, "Expected a constant integer for 'intrinsics.simd_vector'");
- operand->mode = Addressing_Type;
- operand->type = t_invalid;
- return false;
- }
- if (big_int_is_neg(&x.value.value_integer)) {
- error(call, "Negative vector element length");
- operand->mode = Addressing_Type;
- operand->type = t_invalid;
- return false;
- }
- i64 count = big_int_to_i64(&x.value.value_integer);
-
- check_expr_or_type(c, &y, ce->args[1]);
- if (y.mode != Addressing_Type) {
- error(call, "Expected a type 'intrinsics.simd_vector'");
- operand->mode = Addressing_Type;
- operand->type = t_invalid;
- return false;
- }
- Type *elem = y.type;
- if (!is_type_valid_vector_elem(elem)) {
- gbString str = type_to_string(elem);
- error(call, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str);
- gb_string_free(str);
- operand->mode = Addressing_Type;
- operand->type = t_invalid;
- return false;
- }
-
- operand->mode = Addressing_Type;
- operand->type = alloc_type_simd_vector(count, elem);
- break;
- }
-
case BuiltinProc_is_package_imported: {
bool value = false;
@@ -2583,7 +3591,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
- if (!is_type_integer_like(x.type)) {
+ if (is_type_simd_vector(x.type)) {
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer_like(elem)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "#simd values passed to '%.*s' must have an element of an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ }
+ } else if (!is_type_integer_like(x.type)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Values passed to '%.*s' must be an integer-like type (integer, boolean, enum, bit_set), got %s", LIT(builtin_name), xts);
gb_string_free(xts);
@@ -2641,7 +3656,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (y.mode == Addressing_Invalid) {
return false;
}
- convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, y.type);
if (is_type_untyped(x.type)) {
gbString xts = type_to_string(x.type);
@@ -2678,14 +3693,23 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (x.mode == Addressing_Invalid) {
return false;
}
- if (!is_type_float(x.type)) {
+
+ Type *elem = core_array_type(x.type);
+ if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) {
gbString xts = type_to_string(x.type);
- error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_name), xts);
+ error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts);
gb_string_free(xts);
return false;
+ } else if (is_type_different_to_arch_endianness(elem)) {
+ GB_ASSERT(elem->kind == Type_Basic);
+ if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
}
-
- if (x.mode == Addressing_Constant) {
+ if (is_type_float(x.type) && x.mode == Addressing_Constant) {
f64 v = exact_value_to_f64(x.value);
operand->mode = Addressing_Constant;
@@ -2698,6 +3722,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
break;
+ case BuiltinProc_fused_mul_add:
+ {
+ Operand x = {};
+ Operand y = {};
+ Operand z = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+ check_expr(c, &y, ce->args[1]); if (y.mode == Addressing_Invalid) return false;
+ check_expr(c, &z, ce->args[2]); if (z.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &x, z.type); if (x.mode == Addressing_Invalid) return false;
+ if (is_type_untyped(x.type)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a typed floating point value or #simd vector for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+
+ Type *elem = core_array_type(x.type);
+ if (!is_type_float(x.type) && !(is_type_simd_vector(x.type) && is_type_float(elem))) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a floating point or #simd vector value for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ if (is_type_different_to_arch_endianness(elem)) {
+ GB_ASSERT(elem->kind == Type_Basic);
+ if (elem->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
+ gbString xts = type_to_string(x.type);
+ error(x.expr, "Expected a float which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
+ gb_string_free(xts);
+ return false;
+ }
+ }
+
+ if (!are_types_identical(x.type, y.type) || !are_types_identical(y.type, z.type)) {
+ gbString xts = type_to_string(x.type);
+ gbString yts = type_to_string(y.type);
+ gbString zts = type_to_string(z.type);
+ error(x.expr, "Mismatched types for '%.*s', got %s vs %s vs %s", LIT(builtin_name), xts, yts, zts);
+ gb_string_free(zts);
+ gb_string_free(yts);
+ gb_string_free(xts);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = default_type(x.type);
+ }
+ break;
+
case BuiltinProc_mem_copy:
case BuiltinProc_mem_copy_non_overlapping:
{
@@ -2721,13 +3798,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
- if (!is_type_pointer(dst.type)) {
+ if (!is_type_pointer(dst.type) && !is_type_multi_pointer(dst.type)) {
gbString str = type_to_string(dst.type);
error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
return false;
}
- if (!is_type_pointer(src.type)) {
+ if (!is_type_pointer(src.type) && !is_type_multi_pointer(src.type)) {
gbString str = type_to_string(src.type);
error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
@@ -2769,7 +3846,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
- if (!is_type_pointer(ptr.type)) {
+ if (!is_type_pointer(ptr.type) && !is_type_multi_pointer(ptr.type)) {
gbString str = type_to_string(ptr.type);
error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
@@ -2813,7 +3890,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_Value;
operand->type = ptr.type;
- if (!is_type_pointer(ptr.type)) {
+ if (!is_type_pointer(ptr.type) && !is_type_multi_pointer(ptr.type)) {
gbString str = type_to_string(ptr.type);
error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
@@ -2856,7 +3933,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_Value;
operand->type = t_int;
- if (!is_type_pointer(ptr0.type)) {
+ if (!is_type_pointer(ptr0.type) && !is_type_multi_pointer(ptr0.type)) {
gbString str = type_to_string(ptr0.type);
error(ptr0.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
@@ -2869,7 +3946,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
- if (!is_type_pointer(ptr1.type)) {
+ if (!is_type_pointer(ptr1.type) && !is_type_multi_pointer(ptr1.type)) {
gbString str = type_to_string(ptr1.type);
error(ptr1.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
@@ -2895,21 +3972,62 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
- case BuiltinProc_atomic_fence:
- case BuiltinProc_atomic_fence_acq:
- case BuiltinProc_atomic_fence_rel:
- case BuiltinProc_atomic_fence_acqrel:
- operand->mode = Addressing_NoValue;
+ case BuiltinProc_atomic_type_is_lock_free:
+ {
+ Ast *expr = ce->args[0];
+ Operand o = {};
+ check_expr_or_type(c, &o, expr);
+
+ if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) {
+ return false;
+ }
+ if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) {
+ error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_polymorphic(o.type)) {
+ error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_untyped(o.type)) {
+ error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name));
+ return false;
+ }
+ Type *t = o.type;
+ bool is_lock_free = is_type_lock_free(t);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ operand->value = exact_value_bool(is_lock_free);
+ break;
+ }
+
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ {
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]);
+ break;
+ }
+
+ operand->mode = Addressing_NoValue;
+ }
break;
case BuiltinProc_volatile_store:
- /*fallthrough*/
case BuiltinProc_unaligned_store:
- /*fallthrough*/
+ case BuiltinProc_non_temporal_store:
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2925,60 +4043,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- case BuiltinProc_volatile_load:
- /*fallthrough*/
- case BuiltinProc_unaligned_load:
- /*fallthrough*/
- case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered:
- {
- Type *elem = nullptr;
- if (!is_type_normal_pointer(operand->type, &elem)) {
- error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
- return false;
- }
- operand->type = elem;
- operand->mode = Addressing_Value;
- break;
- }
-
- case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
- case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
- case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
- case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
- case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
- case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed:
+ case BuiltinProc_atomic_store_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2989,30 +4054,146 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_assignment(c, &x, elem, builtin_name);
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = nullptr;
+ operand->mode = Addressing_NoValue;
+ break;
+ }
+
+
+ case BuiltinProc_volatile_load:
+ case BuiltinProc_unaligned_load:
+ case BuiltinProc_non_temporal_load:
+ case BuiltinProc_atomic_load:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
operand->type = elem;
operand->mode = Addressing_Value;
break;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
+ case BuiltinProc_atomic_load_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
+ return false;
+ }
+
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_add:
+ case BuiltinProc_atomic_sub:
+ case BuiltinProc_atomic_and:
+ case BuiltinProc_atomic_nand:
+ case BuiltinProc_atomic_or:
+ case BuiltinProc_atomic_xor:
+ case BuiltinProc_atomic_exchange:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add:
+ case BuiltinProc_atomic_sub:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3026,11 +4207,110 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_assignment(c, &x, elem, builtin_name);
check_assignment(c, &y, elem, builtin_name);
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ operand->mode = Addressing_OptionalOk;
+ operand->type = elem;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ Operand y = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_expr_with_type_hint(c, &y, ce->args[2], elem);
+ check_assignment(c, &x, elem, builtin_name);
+ check_assignment(c, &y, elem, builtin_name);
+
+ OdinAtomicMemoryOrder success_memory_order = {};
+ OdinAtomicMemoryOrder failure_memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) {
+ return false;
+ }
+ if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ bool invalid_combination = false;
+
+ switch (success_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_release:
+ if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) {
+ invalid_combination = true;
+ }
+ break;
+ case OdinAtomicMemoryOrder_consume:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_seq_cst:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+
+
+ if (invalid_combination) {
+ error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s",
+ LIT(builtin_name),
+ OdinAtomicMemoryOrder_strings[success_memory_order],
+ OdinAtomicMemoryOrder_strings[failure_memory_order]
+ );
+ }
+
operand->mode = Addressing_OptionalOk;
operand->type = elem;
break;
}
- break;
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
@@ -3052,7 +4332,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (x.mode == Addressing_Invalid) {
return false;
}
- convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
if (x.mode == Addressing_Invalid) {
return false;
}
@@ -3109,7 +4389,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (y.mode == Addressing_Invalid) {
return false;
}
- convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
convert_to_typed(c, &x, y.type);
if (!are_types_identical(x.type, y.type)) {
gbString xts = type_to_string(x.type);
@@ -3211,8 +4491,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case TargetOs_linux:
case TargetOs_essence:
case TargetOs_freebsd:
+ case TargetOs_openbsd:
switch (build_context.metrics.arch) {
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_amd64:
case TargetArch_arm64:
max_arg_count = 7;
@@ -3297,9 +4578,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
+ case BuiltinProc_type_is_valid_matrix_elements:
case BuiltinProc_type_is_named:
case BuiltinProc_type_is_pointer:
+ case BuiltinProc_type_is_multi_pointer:
case BuiltinProc_type_is_array:
+ case BuiltinProc_type_is_enumerated_array:
case BuiltinProc_type_is_slice:
case BuiltinProc_type_is_dynamic_array:
case BuiltinProc_type_is_map:
@@ -3307,10 +4591,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_union:
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
- case BuiltinProc_type_is_bit_field:
- case BuiltinProc_type_is_bit_field_value:
case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_simd_vector:
+ case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:
case BuiltinProc_type_is_unspecialized_polymorphic_record:
case BuiltinProc_type_has_nil:
@@ -3359,6 +4642,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
break;
+ case BuiltinProc_type_field_type:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr(c, &x, ce->args[1]);
+
+ if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
+ error(ce->args[1], "Expected a const string for field argument");
+ return false;
+ }
+
+ String field_name = x.value.value_string;
+
+ Selection sel = lookup_field(type, field_name, false);
+ if (sel.index.count == 0) {
+ gbString t = type_to_string(type);
+ error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ operand->mode = Addressing_Type;
+ operand->type = sel.entity->type;
+ break;
+ }
+ break;
case BuiltinProc_type_is_specialization_of:
{
@@ -3678,6 +4992,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
+ case BuiltinProc_type_is_subtype_of:
+ {
+ Operand op_src = {};
+ Operand op_dst = {};
+
+ check_expr_or_type(c, &op_src, ce->args[0]);
+ if (op_src.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_src.expr);
+ error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+ check_expr_or_type(c, &op_dst, ce->args[1]);
+ if (op_dst.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_dst.expr);
+ error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ } break;
+
case BuiltinProc_type_field_index_of:
{
Operand op = {};
@@ -3767,6 +5106,238 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_hasher_proc;
break;
}
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ String value = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
+ return false;
+ }
+ operand->mode = Addressing_Value;
+ operand->type = alloc_type_multi_pointer(t_u16);
+ operand->value = {};
+ break;
+ }
+
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ Operand delta = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+ check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) {
+ gbString e = expr_to_string(delta.expr);
+ gbString t = type_to_string(delta.type);
+ error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+ case BuiltinProc_wasm_memory_size:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+
+ case BuiltinProc_wasm_memory_atomic_wait32:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand ptr = {};
+ Operand expected = {};
+ Operand timeout = {};
+ check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
+ check_expr(c, &expected, ce->args[1]); if (expected.mode == Addressing_Invalid) return false;
+ check_expr(c, &timeout, ce->args[2]); if (timeout.mode == Addressing_Invalid) return false;
+
+ Type *t_u32_ptr = alloc_type_pointer(t_u32);
+ convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &expected, t_u32); if (expected.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &timeout, t_i64); if (timeout.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) {
+ gbString e = expr_to_string(ptr.expr);
+ gbString t = type_to_string(ptr.type);
+ error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(expected) || !check_is_assignable_to(c, &expected, t_u32)) {
+ gbString e = expr_to_string(expected.expr);
+ gbString t = type_to_string(expected.type);
+ error(expected.expr, "'%.*s' expected u32 for the 'expected' value, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(timeout) || !check_is_assignable_to(c, &timeout, t_i64)) {
+ gbString e = expr_to_string(timeout.expr);
+ gbString t = type_to_string(timeout.type);
+ error(timeout.expr, "'%.*s' expected i64 for the timeout, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_u32;
+ operand->value = {};
+ break;
+ }
+ break;
+ case BuiltinProc_wasm_memory_atomic_notify32:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand ptr = {};
+ Operand waiters = {};
+ check_expr(c, &ptr, ce->args[0]); if (ptr.mode == Addressing_Invalid) return false;
+ check_expr(c, &waiters, ce->args[1]); if (waiters.mode == Addressing_Invalid) return false;
+
+ Type *t_u32_ptr = alloc_type_pointer(t_u32);
+ convert_to_typed(c, &ptr, t_u32_ptr); if (ptr.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &waiters, t_u32); if (waiters.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(ptr) || !check_is_assignable_to(c, &ptr, t_u32_ptr)) {
+ gbString e = expr_to_string(ptr.expr);
+ gbString t = type_to_string(ptr.type);
+ error(ptr.expr, "'%.*s' expected ^u32 for the memory pointer, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(waiters) || !check_is_assignable_to(c, &waiters, t_u32)) {
+ gbString e = expr_to_string(waiters.expr);
+ gbString t = type_to_string(waiters.type);
+ error(waiters.expr, "'%.*s' expected u32 for the 'waiters' value, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_u32;
+ operand->value = {};
+ break;
+ }
+ break;
+
+ case BuiltinProc_x86_cpuid:
+ {
+ if (!is_arch_x86()) {
+ error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
+ return false;
+ }
+
+ Operand ax = {};
+ Operand cx = {};
+
+ check_expr_with_type_hint(c, &ax, ce->args[0], t_u32); if (ax.mode == Addressing_Invalid) return false;
+ check_expr_with_type_hint(c, &cx, ce->args[1], t_u32); if (cx.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &ax, t_u32); if (ax.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
+ if (!are_types_identical(ax.type, t_u32)) {
+ gbString str = type_to_string(ax.type);
+ error(ax.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ return false;
+ }
+ if (!are_types_identical(cx.type, t_u32)) {
+ gbString str = type_to_string(cx.type);
+ error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ return false;
+ }
+ Type *types[4] = {t_u32, t_u32, t_u32, t_u32}; // eax ebc ecx edx
+ operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
+ operand->mode = Addressing_Value;
+ operand->value = {};
+ return true;
+ }
+ break;
+ case BuiltinProc_x86_xgetbv:
+ {
+ if (!is_arch_x86()) {
+ error(call, "'%.*s' is only allowed on x86 targets (i386, amd64)", LIT(builtin_name));
+ return false;
+ }
+
+ Operand cx = {};
+ check_expr_with_type_hint(c, &cx, ce->args[0], t_u32); if (cx.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &cx, t_u32); if (cx.mode == Addressing_Invalid) return false;
+ if (!are_types_identical(cx.type, t_u32)) {
+ gbString str = type_to_string(cx.type);
+ error(cx.expr, "'%.*s' expected a u32, got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ return false;
+ }
+
+ Type *types[2] = {t_u32, t_u32};
+ operand->type = alloc_type_tuple_from_field_types(types, gb_count_of(types), false, false);
+ operand->mode = Addressing_Value;
+ operand->value = {};
+ return true;
+ }
+ break;
+
}
return true;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 134dbb35b..86280b6cb 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
return;
}
+ if (is_type_proc(e->type)) {
+ error(e->token, "Illegal declaration of a constant procedure value");
+ }
+
e->parent_proc_decl = ctx->curr_proc_decl;
e->Constant.value = operand->value;
@@ -238,6 +242,51 @@ isize total_attribute_count(DeclInfo *decl) {
return attribute_count;
}
+Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+ // NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
+ //
+ // X :: enum {A, B, C}
+ // Y :: distinct X
+ //
+ // To make Y be just like X, it will need to copy the elements of X and change their type
+ // so that they match Y rather than X.
+ GB_ASSERT(original_enum_type != nullptr);
+ GB_ASSERT(named_type != nullptr);
+ GB_ASSERT(original_enum_type->kind == Type_Enum);
+ GB_ASSERT(named_type->kind == Type_Named);
+
+ Scope *parent = original_enum_type->Enum.scope->parent;
+ Scope *scope = create_scope(nullptr, parent);
+
+
+ Type *et = alloc_type_enum();
+ et->Enum.base_type = original_enum_type->Enum.base_type;
+ et->Enum.min_value = original_enum_type->Enum.min_value;
+ et->Enum.max_value = original_enum_type->Enum.max_value;
+ et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
+ et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
+ et->Enum.scope = scope;
+
+ auto fields = array_make(permanent_allocator(), original_enum_type->Enum.fields.count);
+ for_array(i, fields) {
+ Entity *old = original_enum_type->Enum.fields[i];
+
+ Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
+ e->file = old->file;
+ e->identifier = clone_ast(old->identifier);
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ e->Constant.flags = old->Constant.flags;
+ e->Constant.docs = old->Constant.docs;
+ e->Constant.comment = old->Constant.comment;
+
+ fields[i] = e;
+ add_entity(ctx, scope, nullptr, e);
+ add_entity_use(ctx, e->identifier, e);
+ }
+ et->Enum.fields = fields;
+ return et;
+}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
@@ -258,15 +307,25 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
- named->Named.base = base_type(bt);
-
- if (is_distinct && is_type_typeid(e->type)) {
- error(init_expr, "'distinct' cannot be applied to 'typeid'");
- is_distinct = false;
+ Type *base = base_type(bt);
+ if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
+ base = clone_enum_type(ctx, base, named);
}
- if (is_distinct && is_type_any(e->type)) {
- error(init_expr, "'distinct' cannot be applied to 'any'");
- is_distinct = false;
+ named->Named.base = base;
+
+ if (is_distinct) {
+ if (is_type_typeid(e->type)) {
+ error(init_expr, "'distinct' cannot be applied to 'typeid'");
+ is_distinct = false;
+ } else if (is_type_any(e->type)) {
+ error(init_expr, "'distinct' cannot be applied to 'any'");
+ is_distinct = false;
+ } else if (is_type_simd_vector(e->type)) {
+ gbString str = type_to_string(e->type);
+ error(init_expr, "'distinct' cannot be applied to '%s'", str);
+ gb_string_free(str);
+ is_distinct = false;
+ }
}
if (!is_distinct) {
e->type = bt;
@@ -289,6 +348,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+ if (e->kind == Entity_TypeName && ac.objc_class != "") {
+ e->TypeName.objc_class_name = ac.objc_class;
+
+ if (type_size_of(e->type) > 0) {
+ error(e->token, "@(objc_class) marked type must be of zero size");
+ }
+ }
}
@@ -380,12 +446,56 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
if (type_expr) {
e->type = check_type(ctx, type_expr);
+ if (are_types_identical(e->type, t_typeid)) {
+ e->type = nullptr;
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
}
Operand operand = {};
if (init != nullptr) {
- Entity *entity = nullptr;
+ Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
+ if (entity != nullptr && entity->kind == Entity_TypeName) {
+ // @TypeAliasingProblem
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+ // being "confused" as constants
+ //
+ // A :: B
+ // C :: proc "c" (^A)
+ // B :: struct {x: C}
+ //
+ // A gets evaluated first, and then checks B.
+ // B then checks C.
+ // C then tries to check A which is unresolved but thought to be a constant.
+ // Therefore within C's check, A errs as "not a type".
+ //
+ // This is because a const declaration may or may not be a type and this cannot
+ // be determined from a syntactical standpoint.
+ // This check allows the compiler to override the entity to be checked as a type.
+ //
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
+ // both a syntax and semantic level that B must be a type.
+ //
+ // A :: #type B
+ //
+ // This approach is not fool proof and can fail in case such as:
+ //
+ // X :: type_of(x)
+ // X :: Foo(int).Type
+ //
+ // Since even these kind of declarations may cause weird checking cycles.
+ // For the time being, these are going to be treated as an unfortunate error
+ // until there is a proper delaying system to try declaration again if they
+ // have failed.
+
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
+ entity = nullptr;
if (init->kind == Ast_Ident) {
entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
} else if (init->kind == Ast_SelectorExpr) {
@@ -732,6 +842,75 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
+ if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
+ if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+ error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+ } else if (ac.objc_type == nullptr) {
+ error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+ } else if (ac.objc_name.len == 0 && ac.objc_type) {
+ error(e->token, "@(objc_name) is required with @(objc_type)");
+ } else {
+ Type *t = ac.objc_type;
+ if (t->kind == Type_Named) {
+ Entity *tn = t->Named.type_name;
+
+ GB_ASSERT(tn->kind == Entity_TypeName);
+
+ if (tn->scope != e->scope) {
+ error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+ } else {
+ mutex_lock(&global_type_name_objc_metadata_mutex);
+ defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+ if (!tn->TypeName.objc_metadata) {
+ tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+ }
+ auto *md = tn->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+
+ if (!ac.objc_is_class_method) {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ } else {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
+ error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
+ } else if (ac.require_target_feature.len != 0) {
+ if (check_target_feature_is_enabled(e->token.pos, ac.require_target_feature)) {
+ e->Procedure.target_feature = ac.require_target_feature;
+ } else {
+ e->Procedure.target_feature_disabled = true;
+ }
+ } else if (ac.enable_target_feature.len != 0) {
+ enable_target_feature(e->token.pos, ac.enable_target_feature);
+ e->Procedure.target_feature = ac.enable_target_feature;
+ }
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
@@ -777,21 +956,23 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (e->pkg != nullptr && e->token.string == "main") {
- if (pt->param_count != 0 ||
- pt->result_count != 0) {
- gbString str = type_to_string(proc_type);
- error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
- gb_string_free(str);
- }
- if (pt->calling_convention != default_calling_convention()) {
- error(e->token, "Procedure 'main' cannot have a custom calling convention");
- }
- pt->calling_convention = default_calling_convention();
- if (e->pkg->kind == Package_Init) {
- if (ctx->info->entry_point != nullptr) {
- error(e->token, "Redeclaration of the entry pointer procedure 'main'");
- } else {
- ctx->info->entry_point = e;
+ if (e->pkg->kind != Package_Runtime) {
+ if (pt->param_count != 0 ||
+ pt->result_count != 0) {
+ gbString str = type_to_string(proc_type);
+ error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
+ gb_string_free(str);
+ }
+ if (pt->calling_convention != default_calling_convention()) {
+ error(e->token, "Procedure 'main' cannot have a custom calling convention");
+ }
+ pt->calling_convention = default_calling_convention();
+ if (e->pkg->kind == Package_Init) {
+ if (ctx->info->entry_point != nullptr) {
+ error(e->token, "Redeclaration of the entry pointer procedure 'main'");
+ } else {
+ ctx->info->entry_point = e;
+ }
}
}
}
@@ -833,10 +1014,12 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
- if (pt->result_count == 0 && ac.require_results) {
- error(pl->type, "'require_results' is not needed on a procedure with no results");
- } else {
- pt->require_results = ac.require_results;
+ if (ac.require_results) {
+ if (pt->result_count == 0) {
+ error(pl->type, "'require_results' is not needed on a procedure with no results");
+ } else {
+ pt->require_results = true;
+ }
}
if (ac.link_name.len > 0) {
@@ -855,18 +1038,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
Entity *foreign_library = init_entity_foreign_library(ctx, e);
- if (is_arch_wasm()) {
+ if (is_arch_wasm() && foreign_library != nullptr) {
String module_name = str_lit("env");
- if (foreign_library != nullptr) {
- GB_ASSERT (foreign_library->kind == Entity_LibraryName);
- if (foreign_library->LibraryName.paths.count != 1) {
- error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
- LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
- }
-
- if (foreign_library->LibraryName.paths.count >= 1) {
- module_name = foreign_library->LibraryName.paths[0];
- }
+ GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+ if (foreign_library->LibraryName.paths.count != 1) {
+ error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+ LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+ }
+
+ if (foreign_library->LibraryName.paths.count >= 1) {
+ module_name = foreign_library->LibraryName.paths[0];
}
name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
}
@@ -924,7 +1105,9 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s",
LIT(name), token_pos_to_string(pos));
} else if (name == "main") {
- error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ if (d->entity->pkg->kind != Package_Runtime) {
+ error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ }
} else {
string_map_set(fp, key, e);
}
@@ -971,6 +1154,12 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ e->Variable.thread_local_model.len = 0;
+ // NOTE(bill): ignore this message for the time begin
+ // error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
String context_name = str_lit("variable declaration");
if (type_expr != nullptr) {
@@ -1046,6 +1235,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
Operand o = {};
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
+
+ check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
@@ -1138,20 +1329,20 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d)
if (!both_have_where_clauses) switch (kind) {
case ProcOverload_Identical:
- error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
+ error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
// case ProcOverload_CallingConvention:
- // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
+ // error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// is_invalid = true;
// break;
case ProcOverload_ParamVariadic:
- error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
+ error(p->token, "Overloaded procedure '%.*s' has the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_ResultCount:
case ProcOverload_ResultTypes:
- error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
+ error(p->token, "Overloaded procedure '%.*s' has the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_Polymorphic:
@@ -1302,11 +1493,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
- if (scope == nullptr) {
- scope = scope_of_node(t->Struct.node);
- }
GB_ASSERT(scope != nullptr);
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
@@ -1324,11 +1512,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
}
-
- for_array(i, using_entities) {
+ MUTEX_GUARD_BLOCK(ctx->scope->mutex) for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
- Entity *prev = scope_insert(ctx->scope, uvar);
+ Entity *prev = scope_insert(ctx->scope, uvar, false);
if (prev != nullptr) {
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 7a0273805..ff1745535 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -119,6 +119,115 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+
+void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
+ auto results = did_you_mean_results(d);
+ if (results.count != 0) {
+ error_line("\tSuggestion: Did you mean?\n");
+ for_array(i, results) {
+ String const &target = results[i].target;
+ error_line("\t\t%s%.*s\n", prefix, LIT(target));
+ // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
+ }
+ }
+}
+
+void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
+ if (e->kind != Entity_TypeName) {
+ return;
+ }
+ if (e->TypeName.objc_metadata == nullptr) {
+ return;
+ }
+ TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Struct);
+
+ if (is_type) {
+ for_array(i, objc_metadata->type_entries) {
+ String name = objc_metadata->type_entries[i].name;
+ string_set_add(set, name);
+ }
+ } else {
+ for_array(i, objc_metadata->value_entries) {
+ String name = objc_metadata->value_entries[i].name;
+ string_set_add(set, name);
+ }
+ }
+
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (f->flags & EntityFlag_Using && f->type != nullptr) {
+ if (f->type->kind == Type_Named && f->type->Named.type_name) {
+ populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
+ }
+ }
+ }
+}
+
+
+void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+ ERROR_BLOCK();
+ GB_ASSERT(e->kind == Entity_TypeName);
+ GB_ASSERT(e->TypeName.objc_metadata != nullptr);
+ auto *objc_metadata = e->TypeName.objc_metadata;
+ mutex_lock(objc_metadata->mutex);
+ defer (mutex_unlock(objc_metadata->mutex));
+
+ StringSet set = {};
+ string_set_init(&set, heap_allocator());
+ defer (string_set_destroy(&set));
+ populate_check_did_you_mean_objc_entity(&set, e, is_type);
+
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+ for_array(i, set.entries) {
+ did_you_mean_append(&d, set.entries[i].value);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+
+void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ mutex_lock(&scope->mutex);
+ for_array(i, scope->elements.entries) {
+ Entity *e = scope->elements.entries[i].value;
+ did_you_mean_append(&d, e->token.string);
+ }
+ mutex_unlock(&scope->mutex);
+ check_did_you_mean_print(&d, prefix);
+}
+
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -176,42 +285,6 @@ void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reser
}
}
-
-isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
- Type *prev_src = src;
- src = type_deref(src);
- if (!src_is_ptr) {
- src_is_ptr = src != prev_src;
- }
- src = base_type(src);
-
- if (!is_type_struct(src)) {
- return 0;
- }
-
- for_array(i, src->Struct.fields) {
- Entity *f = src->Struct.fields[i];
- if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
- continue;
- }
-
- if (are_types_identical(f->type, dst)) {
- return level+1;
- }
- if (src_is_ptr && is_type_pointer(dst)) {
- if (are_types_identical(f->type, type_deref(dst))) {
- return level+1;
- }
- }
- isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
- if (nested_level > 0) {
- return nested_level;
- }
- }
-
- return 0;
-}
-
bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
///////////////////////////////////////////////////////////////////////////////
@@ -369,6 +442,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
final_proc_type->Proc.is_poly_specialized = true;
final_proc_type->Proc.is_polymorphic = true;
+ final_proc_type->Proc.variadic = src->Proc.variadic;
+ final_proc_type->Proc.require_results = src->Proc.require_results;
+ final_proc_type->Proc.c_vararg = src->Proc.c_vararg;
+ final_proc_type->Proc.has_named_results = src->Proc.has_named_results;
+ final_proc_type->Proc.diverging = src->Proc.diverging;
+ final_proc_type->Proc.return_by_pointer = src->Proc.return_by_pointer;
+ final_proc_type->Proc.optional_ok = src->Proc.optional_ok;
+
for (isize i = 0; i < operands.count; i++) {
Operand o = operands[i];
@@ -456,6 +537,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
+ }
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return -1;
@@ -621,6 +706,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 1;
}
}
+
+ // TODO(bill): Determine which rule is a better on in practice
+ #if 1
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ return score+2;
+ }
+ }
+ #else
+ // NOTE(bill): check to see you can assign to it with one of the variants?
+ i64 prev_lowest_score = -1;
+ i64 lowest_score = -1;
+ for_array(i, dst->Union.variants) {
+ Type *vt = dst->Union.variants[i];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ if (lowest_score < 0) {
+ lowest_score = score;
+ } else {
+ if (prev_lowest_score < 0) {
+ prev_lowest_score = lowest_score;
+ } else {
+ prev_lowest_score = gb_min(prev_lowest_score, lowest_score);
+ }
+ lowest_score = gb_min(lowest_score, score);
+ }
+ }
+ }
+ if (lowest_score >= 0) {
+ if (prev_lowest_score != lowest_score) { // remove possible ambiguities
+ return lowest_score+2;
+ }
+ }
+ #endif
}
if (is_type_relative_pointer(dst)) {
@@ -649,6 +770,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 4;
}
}
+
+ if (is_type_complex_or_quaternion(dst)) {
+ Type *elem = base_complex_elem_type(dst);
+ if (are_types_identical(elem, base_type(src))) {
+ return 5;
+ }
+ }
if (is_type_array(dst)) {
Type *elem = base_array_type(dst);
@@ -657,6 +785,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return distance + 6;
}
}
+
+ if (is_type_simd_vector(dst)) {
+ Type *dst_elem = base_array_type(dst);
+ i64 distance = check_distance_between_types(c, operand, dst_elem);
+ if (distance >= 0) {
+ return distance + 6;
+ }
+ }
if (is_type_matrix(dst)) {
Type *dst_elem = base_array_type(dst);
@@ -666,6 +802,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
}
+
if (is_type_any(dst)) {
if (!is_type_polymorphic(src)) {
if (operand->mode == Addressing_Context && operand->type == t_context) {
@@ -723,6 +860,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
return check_is_assignable_to_with_score(c, operand, type, &score);
}
+bool internal_check_is_assignable_to(Type *src, Type *dst) {
+ Operand x = {};
+ x.type = src;
+ x.mode = Addressing_Value;
+ return check_is_assignable_to(nullptr, &x, dst);
+}
+
AstPackage *get_package_of_type(Type *type) {
for (;;) {
if (type == nullptr) {
@@ -1201,6 +1345,19 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
}
}
return false;
+
+ case Type_SimdVector:
+ if (source->kind == Type_SimdVector) {
+ if (poly->SimdVector.generic_count != nullptr) {
+ if (!polymorphic_assign_index(&poly->SimdVector.generic_count, &poly->SimdVector.count, source->SimdVector.count)) {
+ return false;
+ }
+ }
+ if (poly->SimdVector.count == source->SimdVector.count) {
+ return is_polymorphic_type_assignable(c, poly->SimdVector.elem, source->SimdVector.elem, true, modify_type);
+ }
+ }
+ return false;
}
return false;
}
@@ -1227,7 +1384,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
-
Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
@@ -1363,8 +1519,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
case Entity_TypeName:
o->mode = Addressing_Type;
if (check_cycle(c, e, true)) {
- type = t_invalid;
+ o->type = t_invalid;
}
+ if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ o->type = base_type(o->type);
+ }
+
break;
case Entity_ImportName:
@@ -1437,9 +1597,11 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
Type *main_type = o->type;
+
// TODO(bill): Handle errors correctly
Type *type = base_type(core_array_type(main_type));
Type *ct = core_type(type);
+
switch (op.kind) {
case Token_Sub:
case Token_SubEq:
@@ -1456,6 +1618,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
if (is_type_matrix(main_type)) {
error(op, "Operator '%.*s' is only allowed with matrix types", LIT(op.string));
return false;
+ } else if (is_type_simd_vector(main_type) && is_type_integer(type)) {
+ error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string));
+ return false;
}
/*fallthrough*/
case Token_Mul:
@@ -1507,14 +1672,9 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
if (!is_type_integer(type)) {
error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
return false;
- }
- if (is_type_simd_vector(o->type)) {
- switch (op.kind) {
- case Token_ModMod:
- case Token_ModModEq:
- error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
- return false;
- }
+ } else if (is_type_simd_vector(main_type)) {
+ error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string));
+ return false;
}
break;
@@ -1524,14 +1684,6 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
error(op, "Operator '%.*s' is only allowed with integers and bit sets", LIT(op.string));
return false;
}
- if (is_type_simd_vector(o->type)) {
- switch (op.kind) {
- case Token_AndNot:
- case Token_AndNotEq:
- error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
- return false;
- }
- }
break;
case Token_CmpAnd:
@@ -1837,11 +1989,12 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
}
-void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
+bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
GB_ASSERT(o->mode == Addressing_Constant);
ExactValue out_value = o->value;
if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) {
o->value = out_value;
+ return true;
} else {
o->value = out_value;
@@ -1866,6 +2019,7 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
check_assignment_error_suggestion(ctx, o, type);
}
+ return false;
}
}
@@ -2355,6 +2509,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
gb_string_free(err_str);
}
+ // TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
+
if (!is_type_integer(x->type)) {
gbString err_str = expr_to_string(y->expr);
error(node, "Shift operand '%s' must be an integer", err_str);
@@ -2453,6 +2609,9 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return true;
}
+ if (is_type_float(src) && is_type_complex(dst)) {
+ return true;
+ }
if (is_type_float(src) && is_type_quaternion(dst)) {
return true;
}
@@ -2562,6 +2721,26 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return true;
}
+ if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
+ if (src->SimdVector.count != dst->SimdVector.count) {
+ return false;
+ }
+ Type *elem_src = base_array_type(src);
+ Type *elem_dst = base_array_type(dst);
+ Operand x = {};
+ x.type = elem_src;
+ x.mode = Addressing_Value;
+ return check_is_castable_to(c, &x, elem_dst);
+ }
+
+ if (is_type_simd_vector(dst)) {
+ Type *elem = base_array_type(dst);
+ if (check_is_castable_to(c, operand, elem)) {
+ return true;
+ }
+ }
+
+
return false;
}
@@ -2651,14 +2830,14 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
return false;
}
- if (o->mode == Addressing_Constant) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str);
- gb_string_free(expr_str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return false;
- }
+ // if (o->mode == Addressing_Constant) {
+ // gbString expr_str = expr_to_string(o->expr);
+ // error(o->expr, "Cannot transmute a constant expression: '%s'", expr_str);
+ // gb_string_free(expr_str);
+ // o->mode = Addressing_Invalid;
+ // o->expr = node;
+ // return false;
+ // }
if (is_type_untyped(o->type)) {
gbString expr_str = expr_to_string(o->expr);
@@ -2709,8 +2888,10 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
}
}
+ o->expr = node;
o->mode = Addressing_Value;
o->type = t;
+ o->value = {};
return true;
}
@@ -2778,7 +2959,14 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand
goto matrix_error;
}
x->mode = Addressing_Value;
- x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count);
+ if (are_types_identical(xt, yt)) {
+ if (!is_type_named(x->type) && is_type_named(y->type)) {
+ // prefer the named type
+ x->type = y->type;
+ }
+ } else {
+ x->type = alloc_type_matrix(xt->Matrix.elem, xt->Matrix.row_count, yt->Matrix.column_count);
+ }
goto matrix_success;
} else if (yt->kind == Type_Array) {
if (!are_types_identical(xt->Matrix.elem, yt->Array.elem)) {
@@ -2840,7 +3028,6 @@ void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand
matrix_success:
x->type = check_matrix_type_hint(x->type, type_hint);
-
return;
@@ -3068,13 +3255,19 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
return;
}
-
- if (!are_types_identical(x->type, y->type)) {
+ if ((op.kind == Token_CmpAnd || op.kind == Token_CmpOr) &&
+ is_type_boolean(x->type) && is_type_boolean(y->type)) {
+ // NOTE(bill, 2022-06-26)
+ // Allow any boolean types within `&&` and `||`
+ // This is an exception to all other binary expressions since the result
+ // of a comparison will always be an untyped boolean, and allowing
+ // any boolean between these two simplifies a lot of expressions
+ } else if (!are_types_identical(x->type, y->type)) {
if (x->type != t_invalid &&
y->type != t_invalid) {
gbString xt = type_to_string(x->type);
gbString yt = type_to_string(y->type);
- gbString expr_str = expr_to_string(x->expr);
+ gbString expr_str = expr_to_string(node);
error(op, "Mismatched types in binary expression '%s' : '%s' vs '%s'", expr_str, xt, yt);
gb_string_free(expr_str);
gb_string_free(yt);
@@ -3201,6 +3394,16 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
x->mode = Addressing_Value;
}
+Operand make_operand_from_node(Ast *node) {
+ GB_ASSERT(node != nullptr);
+ Operand x = {};
+ x.expr = node;
+ x.mode = node->tav.mode;
+ x.type = node->tav.type;
+ x.value = node->tav.value;
+ return x;
+}
+
void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
GB_ASSERT(e != nullptr);
@@ -3249,9 +3452,18 @@ void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final)
// See above note in UnaryExpr case
break;
}
-
- update_untyped_expr_type(c, te->x, type, final);
- update_untyped_expr_type(c, te->y, type, final);
+
+ // NOTE(bill): This is a bit of a hack to get around the edge cases of ternary if expressions
+ // having an untyped value
+ Operand x = make_operand_from_node(te->x);
+ Operand y = make_operand_from_node(te->y);
+ if (x.mode != Addressing_Constant || check_is_expressible(c, &x, type)) {
+ update_untyped_expr_type(c, te->x, type, final);
+ }
+ if (y.mode != Addressing_Constant || check_is_expressible(c, &y, type)) {
+ update_untyped_expr_type(c, te->y, type, final);
+ }
+
case_end;
case_ast_node(te, TernaryWhenExpr, e);
@@ -3330,7 +3542,16 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
}
}
}
+ ERROR_BLOCK();
+
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
+ if (operand->value.kind == ExactValue_String) {
+ String key = operand->value.value_string;
+ if (is_type_string(operand->type) && is_type_enum(target_type)) {
+ Type *et = base_type(target_type);
+ check_did_you_mean_type(key, et->Enum.fields, ".");
+ }
+ }
gb_string_free(from_type_str);
gb_string_free(type_str);
@@ -3948,58 +4169,14 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
if (success_) *success_ = true;
return empty_exact_value;
}
-void check_did_you_mean_print(DidYouMeanAnswers *d) {
- auto results = did_you_mean_results(d);
- if (results.count != 0) {
- error_line("\tSuggestion: Did you mean?\n");
- for_array(i, results) {
- String const &target = results[i].target;
- error_line("\t\t%.*s\n", LIT(target));
- // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
- }
- }
-}
-
-void check_did_you_mean_type(String const &name, Array const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_type(String const &name, Slice const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_scope(String const &name, Scope *scope) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, scope->elements.entries) {
- Entity *e = scope->elements.entries[i].value;
- did_you_mean_append(&d, e->token.string);
- }
- check_did_you_mean_print(&d);
-}
Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
Type *array_type = base_type(type_deref(original_type));
- GB_ASSERT(array_type->kind == Type_Array);
+ GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
+ if (array_type->kind == Type_SimdVector) {
+ Type *elem_type = array_type->SimdVector.elem;
+ return alloc_type_simd_vector(new_count, elem_type);
+ }
Type *elem_type = array_type->Array.elem;
Type *swizzle_array_type = nullptr;
@@ -4020,6 +4197,101 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n
}
+bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
+ bool is_declared = entity != nullptr;
+ if (is_declared) {
+ if (entity->kind == Entity_Builtin) {
+ // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
+ // This means that we should just ignore the found result through it
+ *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
+ } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
+ is_declared = false;
+ }
+ }
+ return is_declared;
+}
+
+// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
+Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
+ if (node->kind == Ast_Ident) {
+ String name = node->Ident.token.string;
+ return scope_lookup(c->scope, name);
+ } else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, node);
+ if (se->token.kind == Token_ArrowRight) {
+ return nullptr;
+ }
+
+ Ast *op_expr = se->expr;
+ Ast *selector = unparen_expr(se->selector);
+ if (selector == nullptr) {
+ return nullptr;
+ }
+ if (selector->kind != Ast_Ident) {
+ return nullptr;
+ }
+
+ Entity *entity = nullptr;
+ Entity *expr_entity = nullptr;
+ bool check_op_expr = true;
+
+ if (op_expr->kind == Ast_Ident) {
+ String op_name = op_expr->Ident.token.string;
+ Entity *e = scope_lookup(c->scope, op_name);
+ if (e == nullptr) {
+ return nullptr;
+ }
+ add_entity_use(c, op_expr, e);
+ expr_entity = e;
+
+ if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
+ // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
+ // It pretty much needs to be in this order and this way
+ // If you can clean this up, please do but be really careful
+ String import_name = op_name;
+ Scope *import_scope = e->ImportName.scope;
+ String entity_name = selector->Ident.token.string;
+
+ check_op_expr = false;
+ entity = scope_lookup_current(import_scope, entity_name);
+ bool allow_builtin = false;
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
+ return nullptr;
+ }
+
+ check_entity_decl(c, entity, nullptr, nullptr);
+ if (entity->kind == Entity_ProcGroup) {
+ return entity;
+ }
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
+ }
+ }
+
+ Operand operand = {};
+ if (check_op_expr) {
+ check_expr_base(c, &operand, op_expr, nullptr);
+ if (operand.mode == Addressing_Invalid) {
+ return nullptr;
+ }
+ }
+
+ if (entity == nullptr && selector->kind == Ast_Ident) {
+ String field_name = selector->Ident.token.string;
+ if (is_type_dynamic_array(type_deref(operand.type))) {
+ init_mem_allocator(c->checker);
+ }
+ auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type);
+ entity = sel.entity;
+ }
+
+ if (entity != nullptr) {
+ return entity;
+ }
+ }
+ return nullptr;
+}
+
+
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
@@ -4068,18 +4340,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
- bool is_declared = entity != nullptr;
bool allow_builtin = false;
- if (is_declared) {
- if (entity->kind == Entity_Builtin) {
- // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
- // This means that we should just ignore the found result through it
- allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
- } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
- is_declared = false;
- }
- }
- if (!is_declared) {
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4169,7 +4431,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
// TODO(bill): Simd_Vector swizzling
String field_name = selector->Ident.token.string;
@@ -4270,14 +4532,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (entity == nullptr) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
if (operand->type != nullptr && selector->kind == Ast_Ident) {
String const &name = selector->Ident.token.string;
Type *bt = base_type(operand->type);
- if (bt->kind == Type_Struct) {
+ if (operand->type->kind == Type_Named &&
+ operand->type->Named.type_name &&
+ operand->type->Named.type_name->kind == Entity_TypeName &&
+ operand->type->Named.type_name->TypeName.objc_metadata) {
+ check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+ } else if (bt->kind == Type_Struct) {
check_did_you_mean_type(name, bt->Struct.fields);
} else if (bt->kind == Type_Enum) {
check_did_you_mean_type(name, bt->Enum.fields);
@@ -4306,7 +4573,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4331,7 +4598,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4344,7 +4611,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4667,25 +4934,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
-
-CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
+isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
+ GB_ASSERT(pt != nullptr);
+ GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
- bool variadic = pt->variadic;
- bool vari_expand = (ce->ellipsis.pos.line != 0);
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
-
-
+ bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
+ if (pt->Proc.params != nullptr) {
+ param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4725,6 +4983,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
+ if (param_count_) *param_count_ = param_count;
+ return param_count_excluding_defaults;
+}
+
+
+CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(is_type_proc(proc_type));
+ proc_type = base_type(proc_type);
+ TypeProc *pt = &proc_type->Proc;
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count);
+ bool variadic = pt->variadic;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ i64 score = 0;
+ bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
+
+
+ TypeTuple *param_tuple = nullptr;
+ if (pt->params != nullptr) {
+ param_tuple = &pt->params->Tuple;
+ }
+
+
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5397,7 +5680,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- Array procs = proc_group_entities(c, *operand);
+ auto procs = proc_group_entities_cloned(c, *operand);
+
+ if (procs.count > 1) {
+ isize max_arg_count = args.count;
+ for_array(i, args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ Ast *arg = strip_or_return_expr(args[i]);
+ if (arg && arg->kind == Ast_CallExpr) {
+ max_arg_count = ISIZE_MAX;
+ break;
+ }
+ }
+
+ for (isize proc_index = 0; proc_index < procs.count; /**/) {
+ Entity *proc = procs[proc_index];
+ Type *pt = base_type(proc->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count);
+
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ } else {
+ proc_index++;
+ }
+ }
+ }
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5427,6 +5740,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
+
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -5711,8 +6025,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -5725,6 +6043,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *e = entity_of_node(ident);
+
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
gb_unused(err);
@@ -5733,7 +6052,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (entity_to_use != nullptr) {
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
-
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
DeclInfo *decl = data.gen_entity->decl_info;
@@ -5745,8 +6063,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -6040,7 +6362,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
// NOTE(bill): Add type info the parameters
- add_type_info_type(c, o->type);
+ // TODO(bill, 2022-01-23): why was this line added in the first place? I'm commenting it out for the time being
+ // add_type_info_type(c, o->type);
}
{
@@ -6244,10 +6567,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
return builtin_procs[id].kind;
}
- Entity *e = entity_of_node(operand->expr);
+ Entity *initial_entity = entity_of_node(operand->expr);
- if (e != nullptr && e->kind == Entity_Procedure) {
- if (e->Procedure.deferred_procedure.entity != nullptr) {
+ if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) {
+ if (initial_entity->Procedure.deferred_procedure.entity != nullptr) {
call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure;
}
}
@@ -6815,6 +7138,1912 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ
}
+struct TypeAndToken {
+ Type *type;
+ Token token;
+};
+
+typedef PtrMap SeenMap;
+
+void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
+ if (operand.mode != Addressing_Constant) {
+ return;
+ }
+ if (operand.value.kind == ExactValue_Invalid) {
+ return;
+ }
+
+ uintptr key = hash_exact_value(operand.value);
+ TypeAndToken *found = map_get(seen, key);
+ if (found != nullptr) {
+ isize count = multi_map_count(seen, key);
+ TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
+
+ multi_map_get_all(seen, key, taps);
+ for (isize i = 0; i < count; i++) {
+ TypeAndToken tap = taps[i];
+ if (!are_types_identical(operand.type, tap.type)) {
+ continue;
+ }
+
+ TokenPos pos = tap.token.pos;
+ if (use_expr) {
+ gbString expr_str = expr_to_string(operand.expr);
+ error(operand.expr,
+ "Duplicate case '%s'\n"
+ "\tprevious case at %s",
+ expr_str,
+ token_pos_to_string(pos));
+ gb_string_free(expr_str);
+ } else {
+ error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
+ }
+ return;
+ }
+ }
+
+ TypeAndToken tap = {operand.type, ast_token(operand.expr)};
+ multi_map_insert(seen, key, tap);
+}
+
+
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
+ if (is_type_enum(x.type)) {
+ // TODO(bill): Fix this logic so it's fast!!!
+
+ i64 v0 = exact_value_to_i64(lhs.value);
+ i64 v1 = exact_value_to_i64(rhs.value);
+ Operand v = {};
+ v.mode = Addressing_Constant;
+ v.type = x.type;
+ v.expr = x.expr;
+
+ Type *bt = base_type(x.type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for (i64 vi = v0; vi <= v1; vi++) {
+ if (upper_op != Token_LtEq && vi == v1) {
+ break;
+ }
+
+ bool found = false;
+ for_array(j, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[j];
+ GB_ASSERT(f->kind == Entity_Constant);
+
+ i64 fv = exact_value_to_i64(f->Constant.value);
+ if (fv == vi) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ v.value = exact_value_i64(vi);
+ add_constant_switch_case(ctx, seen, v);
+ }
+ }
+ } else {
+ add_constant_switch_case(ctx, seen, lhs);
+ if (upper_op == Token_LtEq) {
+ add_constant_switch_case(ctx, seen, rhs);
+ }
+ }
+}
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
+ add_constant_switch_case(ctx, seen, x);
+}
+
+ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(bd, BasicDirective, node);
+
+ ExprKind kind = Expr_Expr;
+
+ o->mode = Addressing_Constant;
+ String name = bd->name.string;
+ if (name == "file") {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ } else if (name == "line") {
+ o->type = t_untyped_integer;
+ o->value = exact_value_i64(bd->token.pos.line);
+ } else if (name == "procedure") {
+ if (c->curr_proc_decl == nullptr) {
+ error(node, "#procedure may only be used within procedures");
+ o->type = t_untyped_string;
+ o->value = exact_value_string(str_lit(""));
+ } else {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(c->proc_name);
+ }
+ } else if (name == "caller_location") {
+ init_core_source_code_location(c->checker);
+ error(node, "#caller_location may only be used as a default argument parameter");
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
+ } else {
+ if (name == "location") {
+ init_core_source_code_location(c->checker);
+ error(node, "'#%.*s' must be used in a call expression", LIT(name));
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
+ } else if (
+ name == "assert" ||
+ name == "defined" ||
+ name == "config" ||
+ name == "load" ||
+ name == "load_hash" ||
+ name == "load_or"
+ ) {
+ error(node, "'#%.*s' must be used as a call", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
+ } else {
+ error(node, "Unknown directive: #%.*s", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
+ }
+
+ }
+ return kind;
+}
+
+ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {Addressing_Invalid};
+ ast_node(te, TernaryIfExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+
+ if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
+ error(te->cond, "Non-boolean condition in ternary if expression");
+ }
+
+ Operand x = {Addressing_Invalid};
+ Operand y = {Addressing_Invalid};
+ check_expr_or_type(c, &x, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+
+ if (te->y != nullptr) {
+ Type *th = type_hint;
+ if (type_hint == nullptr && is_type_typed(x.type)) {
+ th = x.type;
+ }
+ check_expr_or_type(c, &y, te->y, th);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary expression must have an else clause");
+ return kind;
+ }
+
+ if (x.type == nullptr || x.type == t_invalid ||
+ y.type == nullptr || y.type == t_invalid) {
+ return kind;
+ }
+
+ convert_to_typed(c, &x, y.type);
+ if (x.mode == Addressing_Invalid) {
+ return kind;
+ }
+ convert_to_typed(c, &y, x.type);
+ if (y.mode == Addressing_Invalid) {
+ x.mode = Addressing_Invalid;
+ return kind;
+ }
+
+ if (!ternary_compare_types(x.type, y.type)) {
+ gbString its = type_to_string(x.type);
+ gbString ets = type_to_string(y.type);
+ error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
+ gb_string_free(ets);
+ gb_string_free(its);
+ return kind;
+ }
+
+ o->type = x.type;
+ if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ o->type = y.type;
+ }
+
+ o->mode = Addressing_Value;
+ o->expr = node;
+ if (type_hint != nullptr && is_type_untyped(o->type)) {
+ if (check_cast_internal(c, &x, type_hint) &&
+ check_cast_internal(c, &y, type_hint)) {
+ convert_to_typed(c, o, type_hint);
+ update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
+ }
+ }
+ return kind;
+}
+
+ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {};
+ ast_node(te, TernaryWhenExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+
+ if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
+ error(te->cond, "Expected a constant boolean condition in ternary when expression");
+ return kind;
+ }
+
+ if (cond.value.value_bool) {
+ check_expr_or_type(c, o, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+ } else {
+ if (te->y != nullptr) {
+ check_expr_or_type(c, o, te->y, type_hint);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary when expression must have an else clause");
+ return kind;
+ }
+ }
+ return kind;
+}
+
+ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(oe, OrElseExpr, node);
+
+ String name = oe->token.string;
+ Ast *arg = oe->x;
+ Ast *default_value = oe->y;
+
+ Operand x = {};
+ Operand y = {};
+ check_multi_expr_with_type_hint(c, &x, arg, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ check_multi_expr_with_type_hint(c, &y, default_value, x.type);
+ error_operand_no_value(&y);
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+
+ if (left_type != nullptr) {
+ check_assignment(c, &y, left_type, name);
+ } else {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ }
+
+ if (left_type == nullptr) {
+ left_type = t_invalid;
+ }
+ o->mode = Addressing_Value;
+ o->type = left_type;
+ o->expr = node;
+ return Expr_Expr;
+}
+
+ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(re, OrReturnExpr, node);
+
+ String name = re->token.string;
+ Operand x = {};
+ check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_return_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+
+ if (right_type == nullptr) {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ } else {
+ Type *proc_type = base_type(c->curr_proc_sig);
+ GB_ASSERT(proc_type->kind == Type_Proc);
+ Type *result_type = proc_type->Proc.results;
+ if (result_type == nullptr) {
+ error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
+ } else {
+ GB_ASSERT(result_type->kind == Type_Tuple);
+
+ auto const &vars = result_type->Tuple.variables;
+ Type *end_type = vars[vars.count-1]->type;
+
+ if (vars.count > 1) {
+ if (!proc_type->Proc.has_named_results) {
+ error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
+ }
+ }
+
+ Operand rhs = {};
+ rhs.type = right_type;
+ rhs.mode = Addressing_Value;
+
+ // TODO(bill): better error message
+ if (!check_is_assignable_to(c, &rhs, end_type)) {
+ gbString a = type_to_string(right_type);
+ gbString b = type_to_string(end_type);
+ gbString ret_type = type_to_string(result_type);
+ error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
+ if (vars.count == 1) {
+ error_line("\tProcedure return value type: %s\n", ret_type);
+ } else {
+ error_line("\tProcedure return value types: (%s)\n", ret_type);
+ }
+ gb_string_free(ret_type);
+ gb_string_free(b);
+ gb_string_free(a);
+ }
+ }
+ }
+
+ o->expr = node;
+ o->type = left_type;
+ if (left_type != nullptr) {
+ o->mode = Addressing_Value;
+ } else {
+ o->mode = Addressing_NoValue;
+ }
+
+ if (c->curr_proc_sig == nullptr) {
+ error(node, "'%.*s' can only be used within a procedure", LIT(name));
+ }
+
+ if (c->in_defer) {
+ error(node, "'or_return' cannot be used within a defer statement");
+ }
+
+ return Expr_Expr;
+}
+
+ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(cl, CompoundLit, node);
+
+ Type *type = type_hint;
+ if (type != nullptr && is_type_untyped(type)) {
+ type = nullptr;
+ }
+ bool is_to_be_determined_array_count = false;
+ bool is_constant = true;
+ if (cl->type != nullptr) {
+ type = nullptr;
+
+ // [?]Type
+ if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
+ Ast *count = cl->type->ArrayType.count;
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
+ is_to_be_determined_array_count = true;
+ }
+ if (cl->elems.count > 0) {
+ if (cl->type->ArrayType.tag != nullptr) {
+ Ast *tag = cl->type->ArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
+ }
+ }
+ }
+ if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ if (cl->elems.count > 0) {
+ Ast *tag = cl->type->DynamicArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
+ }
+ }
+
+ if (type == nullptr) {
+ type = check_type(c, cl->type);
+ }
+ }
+
+ if (type == nullptr) {
+ error(node, "Missing type in compound literal");
+ return kind;
+ }
+
+
+ Type *t = base_type(type);
+ if (is_type_polymorphic(t)) {
+ gbString str = type_to_string(type);
+ error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
+ o->expr = node;
+ o->type = type;
+ gb_string_free(str);
+ return kind;
+ }
+
+
+ switch (t->kind) {
+ case Type_Struct: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = false;
+
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ if (cl->elems.count != 1) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ gb_string_free(type_str);
+ } else {
+ Ast *elem = cl->elems[0];
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ break;
+ }
+
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ break;
+ }
+
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ break;
+ }
+
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+
+ }
+ }
+ break;
+ }
+
+
+ isize field_count = t->Struct.fields.count;
+ isize min_field_count = t->Struct.fields.count;
+ for (isize i = min_field_count-1; i >= 0; i--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
+ } else {
+ break;
+ }
+ }
+
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ continue;
+ }
+
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+
+ if (fields_visited[sel.index[0]]) {
+ error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ fields_visited[sel.index[0]] = true;
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ } else {
+ bool seen_field_value = false;
+
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
+
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
+ }
+
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_Slice:
+ case Type_Array:
+ case Type_DynamicArray:
+ case Type_SimdVector:
+ case Type_Matrix:
+ {
+ Type *elem_type = nullptr;
+ String context_name = {};
+ i64 max_type_count = -1;
+ if (t->kind == Type_Slice) {
+ elem_type = t->Slice.elem;
+ context_name = str_lit("slice literal");
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
+ } else if (t->kind == Type_DynamicArray) {
+ elem_type = t->DynamicArray.elem;
+ context_name = str_lit("dynamic array literal");
+ is_constant = false;
+
+ if (!build_context.no_dynamic_literals) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
+ }
+ } else if (t->kind == Type_SimdVector) {
+ elem_type = t->SimdVector.elem;
+ context_name = str_lit("simd vector literal");
+ max_type_count = t->SimdVector.count;
+ } else if (t->kind == Type_Matrix) {
+ elem_type = t->Matrix.elem;
+ context_name = str_lit("matrix literal");
+ max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ } else {
+ GB_PANIC("unreachable");
+ }
+
+
+ i64 max = 0;
+
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
+ break;
+ }
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
+
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
+ error(x.expr, "Expected a constant integer as an array field");
+ continue;
+ }
+
+ if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
+ error(y.expr, "Expected a constant integer as an array field");
+ continue;
+ }
+
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) { // ..< (exclusive)
+ hi -= 1;
+ } else { // .. (inclusive)
+ max_index += 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
+ continue;
+ }
+
+
+ if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
+ continue;
+ }
+ if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ } else {
+ Operand op_index = {};
+ check_expr(c, &op_index, fv->field);
+
+ if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
+ error(elem, "Expected a constant integer as an array field");
+ continue;
+ }
+ // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
+
+ i64 index = exact_value_to_i64(op_index.value);
+
+ if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
+ continue;
+ }
+
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
+ continue;
+ }
+
+ if (max < index+1) {
+ max = index+1;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+ }
+
+ cl->max_count = max;
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
+
+
+ if (t->kind == Type_Array) {
+ if (is_to_be_determined_array_count) {
+ t->Array.count = max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Array.count) {
+ error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
+ }
+ }
+ }
+
+
+ if (t->kind == Type_SimdVector) {
+ if (!is_constant) {
+ // error(node, "Expected all constant elements for a simd vector");
+ }
+ }
+
+
+ if (t->kind == Type_DynamicArray) {
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ }
+ }
+
+ if (t->kind == Type_Matrix) {
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < max_type_count) {
+ error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_EnumeratedArray:
+ {
+ Type *elem_type = t->EnumeratedArray.elem;
+ Type *index_type = t->EnumeratedArray.index;
+ String context_name = str_lit("enumerated array literal");
+ i64 max_type_count = t->EnumeratedArray.count;
+
+ gbString index_type_str = type_to_string(index_type);
+ defer (gb_string_free(index_type_str));
+
+ i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
+ i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+
+ String total_lo_string = {};
+ String total_hi_string = {};
+ GB_ASSERT(is_type_enum(index_type));
+ {
+ Type *bt = base_type(index_type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for_array(i, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
+ total_lo_string = f->token.string;
+ }
+ if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
+ total_hi_string = f->token.string;
+ }
+ if (total_lo_string.len != 0 && total_hi_string.len != 0) {
+ break;
+ }
+ }
+ }
+
+ i64 max = 0;
+
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
+ break;
+ }
+ bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial");
+
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ map_init(&seen, heap_allocator());
+ defer (map_destroy(&seen));
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
+
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) {
+ hi -= 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ gbString lo_str = expr_to_string(x.expr);
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
+ gb_string_free(hi_str);
+ gb_string_free(lo_str);
+ continue;
+ }
+
+
+ // NOTE(bill): These are sanity checks for invalid enum values
+ if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
+ gbString lo_str = expr_to_string(x.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(lo_str);
+ continue;
+ }
+ if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(hi_str);
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ TokenKind upper_op = Token_LtEq;
+ if (op.kind == Token_RangeHalf) {
+ upper_op = Token_Lt;
+ }
+ add_to_seen_map(c, &seen, upper_op, x, x, y);
+ } else {
+ Operand op_index = {};
+ check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+
+ if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
+ error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ i64 index = exact_value_to_i64(op_index.value);
+
+ if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
+
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
+
+ if (max < index+1) {
+ max = index+1;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ add_to_seen_map(c, &seen, op_index);
+ }
+ }
+
+ cl->max_count = max;
+
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
+
+ bool was_error = false;
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->EnumeratedArray.count) {
+ error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
+ was_error = true;
+ } else {
+ error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ was_error = true;
+ }
+ }
+
+ // NOTE(bill): Check for missing cases when `#partial literal` is not present
+ if (cl->elems.count > 0 && !was_error && !is_partial) {
+ Type *et = base_type(index_type);
+ GB_ASSERT(et->kind == Type_Enum);
+ auto fields = et->Enum.fields;
+
+ auto unhandled = array_make(temporary_allocator(), 0, fields.count);
+
+ for_array(i, fields) {
+ Entity *f = fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ ExactValue v = f->Constant.value;
+ auto found = map_get(&seen, hash_exact_value(v));
+ if (!found) {
+ array_add(&unhandled, f);
+ }
+ }
+
+ if (unhandled.count > 0) {
+ begin_error_block();
+ defer (end_error_block());
+
+ if (unhandled.count == 1) {
+ error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
+ } else {
+ error(node, "Unhandled enumerated array cases:");
+ for_array(i, unhandled) {
+ Entity *f = unhandled[i];
+ error_line("\t%.*s\n", LIT(f->token.string));
+ }
+ }
+ error_line("\n");
+
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
+ }
+
+ break;
+ }
+
+ case Type_Basic: {
+ if (!is_type_any(t)) {
+ if (cl->elems.count != 0) {
+ gbString s = type_to_string(t);
+ error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s);
+ gb_string_free(s);
+ is_constant = false;
+ }
+ break;
+ }
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ { // Checker values
+ Type *field_types[2] = {t_rawptr, t_typeid};
+ isize field_count = 2;
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool fields_visited[2] = {};
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ if (sel.entity == nullptr) {
+ error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
+ continue;
+ }
+
+ isize index = sel.index[0];
+
+ if (fields_visited[index]) {
+ error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
+ continue;
+ }
+
+ fields_visited[index] = true;
+ check_expr(c, o, fv->value);
+
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
+ continue;
+ }
+
+
+ check_expr(c, o, elem);
+ if (index >= field_count) {
+ error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
+ break;
+ }
+
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ if (cl->elems.count < field_count) {
+ error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_Map: {
+ if (cl->elems.count == 0) {
+ break;
+ }
+ is_constant = false;
+ { // Checker values
+ bool key_is_typeid = is_type_typeid(t->Map.key);
+ bool value_is_typeid = is_type_typeid(t->Map.value);
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Only 'field = value' elements are allowed in a map literal");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (key_is_typeid) {
+ check_expr_or_type(c, o, fv->field, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ }
+ check_assignment(c, o, t->Map.key, str_lit("map literal"));
+ if (o->mode == Addressing_Invalid) {
+ continue;
+ }
+
+ if (value_is_typeid) {
+ check_expr_or_type(c, o, fv->value, t->Map.value);
+ } else {
+ check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ }
+ check_assignment(c, o, t->Map.value, str_lit("map literal"));
+ }
+ }
+
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ } else {
+ add_package_dependency(c, "runtime", "__dynamic_map_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ }
+ break;
+ }
+
+ case Type_BitSet: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ Type *et = base_type(t->BitSet.elem);
+ isize field_count = 0;
+ if (et->kind == Type_Enum) {
+ field_count = et->Enum.fields.count;
+ }
+
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
+ is_constant = false;
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set a literal is not allowed");
+ continue;
+ }
+
+ check_expr_with_type_hint(c, o, elem, et);
+
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
+
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ if (lower <= v && v <= upper) {
+ // okay
+ } else {
+ error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+ continue;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ default: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+
+ gbString str = type_to_string(type);
+ error(node, "Invalid compound literal type '%s'", str);
+ gb_string_free(str);
+ return kind;
+ }
+ }
+
+ if (is_constant) {
+ o->mode = Addressing_Constant;
+
+ if (is_type_bit_set(type)) {
+ // NOTE(bill): Encode as an integer
+
+ Type *bt = base_type(type);
+ BigInt bits = {};
+ BigInt one = {};
+ big_int_from_u64(&one, 1);
+
+ for_array(i, cl->elems) {
+ Ast *e = cl->elems[i];
+ GB_ASSERT(e->kind != Ast_FieldValue);
+
+ TypeAndValue tav = e->tav;
+ if (tav.mode != Addressing_Constant) {
+ continue;
+ }
+ GB_ASSERT(tav.value.kind == ExactValue_Integer);
+ i64 v = big_int_to_i64(&tav.value.value_integer);
+ i64 lower = bt->BitSet.lower;
+ u64 index = cast(u64)(v-lower);
+ BigInt bit = {};
+ big_int_from_u64(&bit, index);
+ big_int_shl(&bit, &one, &bit);
+ big_int_or(&bits, &bits, &bit);
+ }
+ o->value.kind = ExactValue_Integer;
+ o->value.value_integer = bits;
+ } else if (is_type_constant_type(type) && cl->elems.count == 0) {
+ ExactValue value = exact_value_compound(node);
+ Type *bt = core_type(type);
+ if (bt->kind == Type_Basic) {
+ if (bt->Basic.flags & BasicFlag_Boolean) {
+ value = exact_value_bool(false);
+ } else if (bt->Basic.flags & BasicFlag_Integer) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Unsigned) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Float) {
+ value = exact_value_float(0);
+ } else if (bt->Basic.flags & BasicFlag_Complex) {
+ value = exact_value_complex(0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Quaternion) {
+ value = exact_value_quaternion(0, 0, 0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Pointer) {
+ value = exact_value_pointer(0);
+ } else if (bt->Basic.flags & BasicFlag_String) {
+ String empty_string = {};
+ value = exact_value_string(empty_string);
+ } else if (bt->Basic.flags & BasicFlag_Rune) {
+ value = exact_value_i64(0);
+ }
+ }
+
+ o->value = value;
+ } else {
+ o->value = exact_value_compound(node);
+ }
+ } else {
+ o->mode = Addressing_Value;
+ }
+ o->type = type;
+ return kind;
+}
+
+ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ta, TypeAssertion, node);
+ check_expr(c, o, ta->expr);
+ node->viral_state_flags |= ta->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+ if (o->mode == Addressing_Constant) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (is_type_untyped(o->type)) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ Type *src = type_deref(o->type);
+ Type *bsrc = base_type(src);
+
+
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
+ bool allowed = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, type_hint)) {
+ allowed = true;
+ add_type_info_type(c, vt);
+ break;
+ }
+ }
+ if (allowed) {
+ add_type_info_type(c, o->type);
+ o->type = type_hint;
+ o->mode = Addressing_OptionalOk;
+ return kind;
+ }
+ }
+
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, bsrc->Union.variants[0]);
+
+ o->type = bsrc->Union.variants[0];
+ o->mode = Addressing_OptionalOk;
+ } else {
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+ } else {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
+
+ if ((c->state_flags & StateFlag_no_type_assert) == 0) {
+ add_package_dependency(c, "runtime", "type_assertion_check");
+ add_package_dependency(c, "runtime", "type_assertion_check2");
+ }
+ return kind;
+}
+
+ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(se, SelectorCallExpr, node);
+ // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
+ // COM APIs is a great example of where this kind of thing is extremely useful
+ // General idea:
+ //
+ // x->y(123) == x.y(x, 123)
+ //
+ // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
+ // Just regenerating a new AST aids things
+ //
+ // TODO(bill): Is this a good hack or not?
+ //
+ // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
+
+
+ if (se->modified_call) {
+ // Prevent double evaluation
+ o->expr = node;
+ o->type = node->tav.type;
+ o->value = node->tav.value;
+ o->mode = node->tav.mode;
+ return Expr_Expr;
+ }
+
+ bool allow_arrow_right_selector_expr;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ Operand x = {};
+ ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return kind;
+ }
+ if (!is_type_proc(x.type)) {
+ gbString type_str = type_to_string(x.type);
+ error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
+ gb_string_free(type_str);
+
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ ast_node(ce, CallExpr, se->call);
+
+ GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+
+ Ast *first_arg = x.expr->SelectorExpr.expr;
+ GB_ASSERT(first_arg != nullptr);
+
+ first_arg->state_flags |= StateFlag_SelectorCallExpr;
+
+ Type *pt = base_type(x.type);
+ GB_ASSERT(pt->kind == Type_Proc);
+ Type *first_type = nullptr;
+ String first_arg_name = {};
+ if (pt->Proc.param_count > 0) {
+ Entity *f = pt->Proc.params->Tuple.variables[0];
+ first_type = f->type;
+ first_arg_name = f->token.string;
+ }
+ if (first_arg_name.len == 0) {
+ first_arg_name = str_lit("_");
+ }
+
+ if (first_type == nullptr) {
+ error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ Operand y = {};
+ y.mode = first_arg->tav.mode;
+ y.type = first_arg->tav.type;
+ y.value = first_arg->tav.value;
+
+ if (check_is_assignable_to(c, &y, first_type)) {
+ // Do nothing, it's valid
+ } else {
+ Operand z = y;
+ z.type = type_deref(y.type);
+ if (check_is_assignable_to(c, &z, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_Pointer};
+ first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+ } else if (y.mode == Addressing_Variable) {
+ Operand w = y;
+ w.type = alloc_type_pointer(y.type);
+ if (check_is_assignable_to(c, &w, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_And};
+ first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ }
+ }
+ }
+
+ if (ce->args.count > 0) {
+ bool fail = false;
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ fail = true;
+ break;
+ }
+ }
+ if (!fail && first_is_field_value) {
+ Token op = {Token_Eq};
+ AstFile *f = first_arg->file();
+ first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
+ }
+ }
+
+
+
+ auto modified_args = slice_make(heap_allocator(), ce->args.count+1);
+ modified_args[0] = first_arg;
+ slice_copy(&modified_args, ce->args, 1);
+ ce->args = modified_args;
+ se->modified_call = true;
+
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ check_expr_base(c, o, se->call, type_hint);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ o->expr = node;
+ return Expr_Expr;
+}
+
+
+ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ie, IndexExpr, node);
+ check_expr(c, o, ie->expr);
+ node->viral_state_flags |= ie->expr->viral_state_flags;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+
+ Type *t = base_type(type_deref(o->type));
+ bool is_ptr = is_type_pointer(o->type);
+ bool is_const = o->mode == Addressing_Constant;
+
+ if (is_type_map(t)) {
+ Operand key = {};
+ if (is_type_typeid(t->Map.key)) {
+ check_expr_or_type(c, &key, ie->index, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ }
+ check_assignment(c, &key, t->Map.key, str_lit("map index"));
+ if (key.mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ o->mode = Addressing_MapIndex;
+ o->type = t->Map.value;
+ o->expr = node;
+
+ add_package_dependency(c, "runtime", "__dynamic_map_get");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ return Expr_Expr;
+ }
+
+ i64 max_count = -1;
+ bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+
+ if (is_const) {
+ if (is_type_array(t)) {
+ // OKay
+ } else if (is_type_slice(t)) {
+ // Okay
+ } else if (is_type_enumerated_array(t)) {
+ // Okay
+ } else if (is_type_string(t)) {
+ // Okay
+ } else if (is_type_relative_slice(t)) {
+ // Okay
+ } else if (is_type_matrix(t)) {
+ // Okay
+ } else {
+ valid = false;
+ }
+ }
+
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ defer (gb_string_free(str));
+ defer (gb_string_free(type_str));
+ if (is_const) {
+ error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
+ } else {
+ error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (ie->index == nullptr) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Missing index for '%s'", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ Type *index_type_hint = nullptr;
+ if (is_type_enumerated_array(t)) {
+ Type *bt = base_type(t);
+ GB_ASSERT(bt->kind == Type_EnumeratedArray);
+ index_type_hint = bt->EnumeratedArray.index;
+ }
+
+ i64 index = 0;
+ bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
+ if (is_const) {
+ if (index < 0) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s'", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ } else if (ok) {
+ ExactValue value = type_and_value_of_expr(ie->expr).value;
+ o->mode = Addressing_Constant;
+ bool success = false;
+ bool finish = false;
+ o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
+ if (!success) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
+ }
+
+ if (type_hint != nullptr && is_type_matrix(t)) {
+ // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
+ // if a type hint exists
+ }
+ return kind;
+}
+
+ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Stmt;
+ ast_node(se, SliceExpr, node);
+ check_expr(c, o, se->expr);
+ node->viral_state_flags |= se->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ bool valid = false;
+ i64 max_count = -1;
+ Type *t = base_type(type_deref(o->type));
+ switch (t->kind) {
+ case Type_Basic:
+ if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ max_count = o->value.value_string.len;
+ }
+ o->type = type_deref(o->type);
+ }
+ break;
+
+ case Type_Array:
+ valid = true;
+ max_count = t->Array.count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice array '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ o->type = alloc_type_slice(t->Array.elem);
+ break;
+
+ case Type_MultiPointer:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
+
+ case Type_Slice:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
+
+ case Type_DynamicArray:
+ valid = true;
+ o->type = alloc_type_slice(t->DynamicArray.elem);
+ break;
+
+ case Type_Struct:
+ if (is_type_soa_struct(t)) {
+ valid = true;
+ o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
+ }
+ break;
+
+ case Type_RelativeSlice:
+ valid = true;
+ o->type = t->RelativeSlice.slice_type;
+ if (o->mode != Addressing_Variable) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot relative slice '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ break;
+ }
+
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
+ gb_string_free(type_str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (se->low == nullptr && se->high != nullptr) {
+ // It is okay to continue as it will assume the 1st index is zero
+ }
+
+ i64 indices[2] = {};
+ Ast *nodes[2] = {se->low, se->high};
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ i64 index = max_count;
+ if (nodes[i] != nullptr) {
+ i64 capacity = -1;
+ if (max_count >= 0) {
+ capacity = max_count;
+ }
+ i64 j = 0;
+ if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
+ index = j;
+ }
+
+ node->viral_state_flags |= nodes[i]->viral_state_flags;
+ } else if (i == 0) {
+ index = 0;
+ }
+ indices[i] = index;
+ }
+
+ for (isize i = 0; i < gb_count_of(indices); i++) {
+ i64 a = indices[i];
+ for (isize j = i+1; j < gb_count_of(indices); j++) {
+ i64 b = indices[j];
+ if (a > b && b >= 0) {
+ error(se->close, "Invalid slice indices: [%td > %td]", a, b);
+ }
+ }
+ }
+
+ if (max_count < 0) {
+ if (o->mode == Addressing_Constant) {
+ gbString s = expr_to_string(se->expr);
+ error(se->expr, "Cannot slice constant value '%s'", s);
+ gb_string_free(s);
+ }
+ }
+
+ if (t->kind == Type_MultiPointer && se->high != nullptr) {
+ /*
+ x[:] -> [^]T
+ x[i:] -> [^]T
+ x[:n] -> []T
+ x[i:n] -> []T
+ */
+ o->type = alloc_type_slice(t->MultiPointer.elem);
+ }
+
+ o->mode = Addressing_Value;
+
+ if (is_type_string(t) && max_count >= 0) {
+ bool all_constant = true;
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ if (nodes[i] != nullptr) {
+ TypeAndValue tav = type_and_value_of_expr(nodes[i]);
+ if (tav.mode != Addressing_Constant) {
+ all_constant = false;
+ break;
+ }
+ }
+ }
+ if (!all_constant) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot slice '%s' with non-constant indices", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
+ o->expr = node;
+ return kind;
+ }
+
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
+ return kind;
+}
+
ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
u32 prev_state_flags = c->state_flags;
defer (c->state_flags = prev_state_flags);
@@ -6830,6 +9059,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
c->state_flags = out;
}
@@ -6837,6 +9074,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Invalid;
o->type = t_invalid;
+ o->value = {ExactValue_Invalid};
switch (node->kind) {
default:
@@ -6909,52 +9147,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(bd, BasicDirective, node);
- o->mode = Addressing_Constant;
- String name = bd->name.string;
- if (name == "file") {
- o->type = t_untyped_string;
- o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
- } else if (name == "line") {
- o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.line);
- } else if (name == "procedure") {
- if (c->curr_proc_decl == nullptr) {
- error(node, "#procedure may only be used within procedures");
- o->type = t_untyped_string;
- o->value = exact_value_string(str_lit(""));
- } else {
- o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
- }
- } else if (name == "caller_location") {
- init_core_source_code_location(c->checker);
- error(node, "#caller_location may only be used as a default argument parameter");
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else {
- if (name == "location") {
- init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else if (
- name == "assert" ||
- name == "defined" ||
- name == "config" ||
- name == "load" ||
- name == "load_hash" ||
- name == "load_or"
- ) {
- error(node, "'#%.*s' must be used as a call", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- } else {
- error(node, "Unknown directive: #%.*s", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- }
-
- }
+ kind = check_basic_directive_expr(c, o, node, type_hint);
case_end;
case_ast_node(pg, ProcGroup, node);
@@ -7003,1102 +9196,23 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(te, TernaryIfExpr, node);
- Operand cond = {Addressing_Invalid};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
- error(te->cond, "Non-boolean condition in ternary if expression");
- }
-
- Operand x = {Addressing_Invalid};
- Operand y = {Addressing_Invalid};
- check_expr_or_type(c, &x, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
-
- if (te->y != nullptr) {
- check_expr_or_type(c, &y, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary expression must have an else clause");
- return kind;
- }
-
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
- convert_to_typed(c, &x, y.type);
- if (x.mode == Addressing_Invalid) {
- return kind;
- }
- convert_to_typed(c, &y, x.type);
- if (y.mode == Addressing_Invalid) {
- x.mode = Addressing_Invalid;
- return kind;
- }
-
- if (!ternary_compare_types(x.type, y.type)) {
- gbString its = type_to_string(x.type);
- gbString ets = type_to_string(y.type);
- error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
- gb_string_free(ets);
- gb_string_free(its);
- return kind;
- }
-
- o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
- o->type = y.type;
- }
-
- o->mode = Addressing_Value;
- o->expr = node;
- if (type_hint != nullptr && is_type_untyped(o->type)) {
- if (check_cast_internal(c, &x, type_hint) &&
- check_cast_internal(c, &y, type_hint)) {
- convert_to_typed(c, o, type_hint);
- update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
- }
- }
+ kind = check_ternary_if_expr(c, o, node, type_hint);
case_end;
case_ast_node(te, TernaryWhenExpr, node);
- Operand cond = {};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
- error(te->cond, "Expected a constant boolean condition in ternary when expression");
- return kind;
- }
-
- if (cond.value.value_bool) {
- check_expr_or_type(c, o, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
- } else {
- if (te->y != nullptr) {
- check_expr_or_type(c, o, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary when expression must have an else clause");
- return kind;
- }
- }
+ kind = check_ternary_when_expr(c, o, node, type_hint);
case_end;
case_ast_node(oe, OrElseExpr, node);
- String name = oe->token.string;
- Ast *arg = oe->x;
- Ast *default_value = oe->y;
-
- Operand x = {};
- Operand y = {};
- check_multi_expr_with_type_hint(c, &x, arg, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
- if (y.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
-
- if (left_type != nullptr) {
- check_assignment(c, &y, left_type, name);
- } else {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- }
-
- if (left_type == nullptr) {
- left_type = t_invalid;
- }
- o->mode = Addressing_Value;
- o->type = left_type;
- o->expr = node;
- return Expr_Expr;
+ return check_or_else_expr(c, o, node, type_hint);
case_end;
case_ast_node(re, OrReturnExpr, node);
- String name = re->token.string;
- Operand x = {};
- check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_return_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
-
- if (right_type == nullptr) {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- } else {
- Type *proc_type = base_type(c->curr_proc_sig);
- GB_ASSERT(proc_type->kind == Type_Proc);
- Type *result_type = proc_type->Proc.results;
- if (result_type == nullptr) {
- error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
- } else {
- GB_ASSERT(result_type->kind == Type_Tuple);
-
- auto const &vars = result_type->Tuple.variables;
- Type *end_type = vars[vars.count-1]->type;
-
- if (vars.count > 1) {
- if (!proc_type->Proc.has_named_results) {
- error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
- }
- }
-
- Operand rhs = {};
- rhs.type = right_type;
- rhs.mode = Addressing_Value;
-
- // TODO(bill): better error message
- if (!check_is_assignable_to(c, &rhs, end_type)) {
- gbString a = type_to_string(right_type);
- gbString b = type_to_string(end_type);
- gbString ret_type = type_to_string(result_type);
- error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
- if (vars.count == 1) {
- error_line("\tProcedure return value type: %s\n", ret_type);
- } else {
- error_line("\tProcedure return value types: (%s)\n", ret_type);
- }
- gb_string_free(ret_type);
- gb_string_free(b);
- gb_string_free(a);
- }
- }
- }
-
- o->expr = node;
- o->type = left_type;
- if (left_type != nullptr) {
- o->mode = Addressing_Value;
- } else {
- o->mode = Addressing_NoValue;
- }
-
- if (c->curr_proc_sig == nullptr) {
- error(node, "'%.*s' can only be used within a procedure", LIT(name));
- }
-
- if (c->in_defer) {
- error(node, "'or_return' cannot be used within a defer statement");
- }
-
- return Expr_Expr;
+ return check_or_return_expr(c, o, node, type_hint);
case_end;
case_ast_node(cl, CompoundLit, node);
- Type *type = type_hint;
- if (type != nullptr && is_type_untyped(type)) {
- type = nullptr;
- }
- bool is_to_be_determined_array_count = false;
- bool is_constant = true;
- if (cl->type != nullptr) {
- type = nullptr;
-
- // [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->ArrayType.count;
- if (count->kind == Ast_UnaryExpr &&
- count->UnaryExpr.op.kind == Token_Question) {
- type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
- is_to_be_determined_array_count = true;
- }
- if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
- if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
-
- if (type == nullptr) {
- type = check_type(c, cl->type);
- }
- }
-
- if (type == nullptr) {
- error(node, "Missing type in compound literal");
- return kind;
- }
-
-
- Type *t = base_type(type);
- if (is_type_polymorphic(t)) {
- gbString str = type_to_string(type);
- error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
- o->expr = node;
- o->type = type;
- gb_string_free(str);
- return kind;
- }
-
-
- switch (t->kind) {
- case Type_Struct: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
-
- if (cl->elems[0]->kind != Ast_FieldValue) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
- gb_string_free(type_str);
- } else {
- if (cl->elems.count != 1) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
- gb_string_free(type_str);
- } else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
-
- }
- }
- break;
- }
-
-
- isize field_count = t->Struct.fields.count;
- isize min_field_count = t->Struct.fields.count;
- for (isize i = min_field_count-1; i >= 0; i--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
- break;
- }
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- fields_visited[sel.index[0]] = true;
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- } else {
- bool seen_field_value = false;
-
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- break;
- }
-
- if (field == nullptr) {
- field = t->Struct.fields[index];
- }
-
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- }
- }
- }
-
- break;
- }
-
- case Type_Slice:
- case Type_Array:
- case Type_DynamicArray:
- case Type_SimdVector:
- case Type_Matrix:
- {
- Type *elem_type = nullptr;
- String context_name = {};
- i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
- elem_type = t->Slice.elem;
- context_name = str_lit("slice literal");
- } else if (t->kind == Type_Array) {
- elem_type = t->Array.elem;
- context_name = str_lit("array literal");
- if (!is_to_be_determined_array_count) {
- max_type_count = t->Array.count;
- }
- } else if (t->kind == Type_DynamicArray) {
- elem_type = t->DynamicArray.elem;
- context_name = str_lit("dynamic array literal");
- is_constant = false;
-
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
- } else if (t->kind == Type_SimdVector) {
- elem_type = t->SimdVector.elem;
- context_name = str_lit("simd vector literal");
- max_type_count = t->SimdVector.count;
- } else if (t->kind == Type_Matrix) {
- elem_type = t->Matrix.elem;
- context_name = str_lit("matrix literal");
- max_type_count = t->Matrix.row_count*t->Matrix.column_count;
- } else {
- GB_PANIC("unreachable");
- }
-
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
- break;
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_simd_vector(t)) {
- error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
- } else {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
- error(x.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
- error(y.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) { // ..< (exclusive)
- hi -= 1;
- } else { // .. (inclusive)
- max_index += 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
- continue;
- }
-
-
- if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr(c, &op_index, fv->field);
-
- if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
- error(elem, "Expected a constant integer as an array field");
- continue;
- }
- // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
-
- cl->max_count = max;
- }
-
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
-
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
-
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
-
- if (max < index) {
- max = index;
- }
- }
-
-
- if (t->kind == Type_Array) {
- if (is_to_be_determined_array_count) {
- t->Array.count = max;
- } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->Array.count) {
- error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
- }
- }
- }
-
-
- if (t->kind == Type_SimdVector) {
- if (!is_constant) {
- error(node, "Expected all constant elements for a simd vector");
- }
- }
-
-
- if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- }
- }
-
- break;
- }
-
- case Type_EnumeratedArray:
- {
- Type *elem_type = t->EnumeratedArray.elem;
- Type *index_type = t->EnumeratedArray.index;
- String context_name = str_lit("enumerated array literal");
- i64 max_type_count = t->EnumeratedArray.count;
-
- gbString index_type_str = type_to_string(index_type);
- defer (gb_string_free(index_type_str));
-
- i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
- i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
-
- String total_lo_string = {};
- String total_hi_string = {};
- GB_ASSERT(is_type_enum(index_type));
- {
- Type *bt = base_type(index_type);
- GB_ASSERT(bt->kind == Type_Enum);
- for_array(i, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[i];
- if (f->kind != Entity_Constant) {
- continue;
- }
- if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
- total_lo_string = f->token.string;
- }
- if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
- total_hi_string = f->token.string;
- }
- if (total_lo_string.len != 0 && total_hi_string.len != 0) {
- break;
- }
- }
- }
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
- break;
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) {
- hi -= 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- gbString lo_str = expr_to_string(x.expr);
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
- gb_string_free(hi_str);
- gb_string_free(lo_str);
- continue;
- }
-
-
- // NOTE(bill): These are sanity checks for invalid enum values
- if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
- gbString lo_str = expr_to_string(x.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(lo_str);
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(hi_str);
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr_with_type_hint(c, &op_index, fv->field, index_type);
-
- if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
- error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(idx_str);
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
- gb_string_free(idx_str);
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
-
- cl->max_count = max;
-
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
-
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
-
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
-
- if (max < index) {
- max = index;
- }
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->EnumeratedArray.count) {
- error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
- } else {
- error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
- }
- }
-
- break;
- }
-
- case Type_Basic: {
- if (!is_type_any(t)) {
- if (cl->elems.count != 0) {
- error(node, "Illegal compound literal");
- }
- break;
- }
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- { // Checker values
- Type *field_types[2] = {t_rawptr, t_typeid};
- isize field_count = 2;
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool fields_visited[2] = {};
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- if (sel.entity == nullptr) {
- error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
-
- isize index = sel.index[0];
-
- if (fields_visited[index]) {
- error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
-
- fields_visited[index] = true;
- check_expr(c, o, fv->value);
-
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
-
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
- }
- } else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
-
-
- check_expr(c, o, elem);
- if (index >= field_count) {
- error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
- break;
- }
-
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
-
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
- }
- if (cl->elems.count < field_count) {
- error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
- }
- }
- }
-
- break;
- }
-
- case Type_Map: {
- if (cl->elems.count == 0) {
- break;
- }
- is_constant = false;
- { // Checker values
- bool key_is_typeid = is_type_typeid(t->Map.key);
- bool value_is_typeid = is_type_typeid(t->Map.value);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Only 'field = value' elements are allowed in a map literal");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (key_is_typeid) {
- check_expr_or_type(c, o, fv->field, t->Map.key);
- } else {
- check_expr_with_type_hint(c, o, fv->field, t->Map.key);
- }
- check_assignment(c, o, t->Map.key, str_lit("map literal"));
- if (o->mode == Addressing_Invalid) {
- continue;
- }
-
- if (value_is_typeid) {
- check_expr_or_type(c, o, fv->value, t->Map.value);
- } else {
- check_expr_with_type_hint(c, o, fv->value, t->Map.value);
- }
- check_assignment(c, o, t->Map.value, str_lit("map literal"));
- }
- }
-
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_reserve");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- }
- break;
- }
-
- case Type_BitSet: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- Type *et = base_type(t->BitSet.elem);
- isize field_count = 0;
- if (et->kind == Type_Enum) {
- field_count = et->Enum.fields.count;
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
- is_constant = false;
- } else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
- continue;
- }
-
- check_expr_with_type_hint(c, o, elem, et);
-
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
- }
-
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- if (lower <= v && v <= upper) {
- // okay
- } else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
- continue;
- }
- }
- }
- }
- break;
- }
-
- default: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
-
- gbString str = type_to_string(type);
- error(node, "Invalid compound literal type '%s'", str);
- gb_string_free(str);
- return kind;
- }
- }
-
- if (is_constant) {
- o->mode = Addressing_Constant;
-
- if (is_type_bit_set(type)) {
- // NOTE(bill): Encode as an integer
-
- i64 lower = base_type(type)->BitSet.lower;
-
- u64 bits = 0;
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- GB_ASSERT(elem->kind != Ast_FieldValue);
- TypeAndValue tav = elem->tav;
- ExactValue i = exact_value_to_integer(tav.value);
- if (i.kind != ExactValue_Integer) {
- continue;
- }
- i64 val = big_int_to_i64(&i.value_integer);
- val -= lower;
- u64 bit = u64(1ll<value = exact_value_u64(bits);
- } else if (is_type_constant_type(type) && cl->elems.count == 0) {
- ExactValue value = exact_value_compound(node);
- Type *bt = core_type(type);
- if (bt->kind == Type_Basic) {
- if (bt->Basic.flags & BasicFlag_Boolean) {
- value = exact_value_bool(false);
- } else if (bt->Basic.flags & BasicFlag_Integer) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Unsigned) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Float) {
- value = exact_value_float(0);
- } else if (bt->Basic.flags & BasicFlag_Complex) {
- value = exact_value_complex(0, 0);
- } else if (bt->Basic.flags & BasicFlag_Quaternion) {
- value = exact_value_quaternion(0, 0, 0, 0);
- } else if (bt->Basic.flags & BasicFlag_Pointer) {
- value = exact_value_pointer(0);
- } else if (bt->Basic.flags & BasicFlag_String) {
- String empty_string = {};
- value = exact_value_string(empty_string);
- } else if (bt->Basic.flags & BasicFlag_Rune) {
- value = exact_value_i64(0);
- }
- }
-
- o->value = value;
- } else {
- o->value = exact_value_compound(node);
- }
- } else {
- o->mode = Addressing_Value;
- }
- o->type = type;
+ kind = check_compound_literal(c, o, node, type_hint);
case_end;
case_ast_node(pe, ParenExpr, node);
@@ -8118,127 +9232,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(ta, TypeAssertion, node);
- check_expr(c, o, ta->expr);
- node->viral_state_flags |= ta->expr->viral_state_flags;
-
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
- if (o->mode == Addressing_Constant) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
- gb_string_free(expr_str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (is_type_untyped(o->type)) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
- gb_string_free(expr_str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- Type *src = type_deref(o->type);
- Type *bsrc = base_type(src);
-
-
- if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
- if (!is_type_union(src)) {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
- bool allowed = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, type_hint)) {
- allowed = true;
- add_type_info_type(c, vt);
- break;
- }
- }
- if (allowed) {
- add_type_info_type(c, o->type);
- o->type = type_hint;
- o->mode = Addressing_OptionalOk;
- return kind;
- }
- }
-
- if (bsrc->Union.variants.count != 1) {
- error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, bsrc->Union.variants[0]);
-
- o->type = bsrc->Union.variants[0];
- o->mode = Addressing_OptionalOk;
- } else {
- Type *t = check_type(c, ta->type);
- Type *dst = t;
-
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
- }
-
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
-
- o->type = t;
- o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
- } else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
-
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
+ kind = check_type_assertion(c, o, node, type_hint);
case_end;
case_ast_node(tc, TypeCast, node);
@@ -8326,443 +9320,19 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(se, SelectorCallExpr, node);
- // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
- // COM APIs is a great example of where this kind of thing is extremely useful
- // General idea:
- //
- // x->y(123) == x.y(x, 123)
- //
- // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
- // Just regenerating a new AST aids things
- //
- // TODO(bill): Is this a good hack or not?
- //
- // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
-
-
- if (se->modified_call) {
- // Prevent double evaluation
- o->expr = node;
- o->type = node->tav.type;
- o->value = node->tav.value;
- o->mode = node->tav.mode;
- return Expr_Expr;
- }
-
- bool allow_arrow_right_selector_expr;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- Operand x = {};
- ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
-
- if (x.mode == Addressing_Invalid || x.type == t_invalid) {
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return kind;
- }
- if (!is_type_proc(x.type)) {
- gbString type_str = type_to_string(x.type);
- error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
- gb_string_free(type_str);
-
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Stmt;
- }
-
- ast_node(ce, CallExpr, se->call);
-
- GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
-
- Ast *first_arg = x.expr->SelectorExpr.expr;
- GB_ASSERT(first_arg != nullptr);
-
- Type *pt = base_type(x.type);
- GB_ASSERT(pt->kind == Type_Proc);
- Type *first_type = nullptr;
- String first_arg_name = {};
- if (pt->Proc.param_count > 0) {
- Entity *f = pt->Proc.params->Tuple.variables[0];
- first_type = f->type;
- first_arg_name = f->token.string;
- }
- if (first_arg_name.len == 0) {
- first_arg_name = str_lit("_");
- }
-
- if (first_type == nullptr) {
- error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Stmt;
- }
-
- Operand y = {};
- y.mode = first_arg->tav.mode;
- y.type = first_arg->tav.type;
- y.value = first_arg->tav.value;
- if (check_is_assignable_to(c, &y, first_type)) {
- // Do nothing, it's valid
- } else {
- Operand z = y;
- z.type = type_deref(y.type);
- if (check_is_assignable_to(c, &z, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_Pointer};
- first_arg = ast_deref_expr(first_arg->file, first_arg, op);
- } else if (y.mode == Addressing_Variable) {
- Operand w = y;
- w.type = alloc_type_pointer(y.type);
- if (check_is_assignable_to(c, &w, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_And};
- first_arg = ast_unary_expr(first_arg->file, op, first_arg);
- }
- }
- }
-
- if (ce->args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
- }
- if (mix) {
- fail = true;
- break;
- }
- }
- if (!fail && first_is_field_value) {
- Token op = {Token_Eq};
- AstFile *f = first_arg->file;
- first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
- }
- }
-
-
-
- auto modified_args = slice_make(heap_allocator(), ce->args.count+1);
- modified_args[0] = first_arg;
- slice_copy(&modified_args, ce->args, 1);
- ce->args = modified_args;
- se->modified_call = true;
-
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- check_expr_base(c, o, se->call, type_hint);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
-
- o->expr = node;
- return Expr_Expr;
+ return check_selector_call_expr(c, o, node, type_hint);
case_end;
-
case_ast_node(ise, ImplicitSelectorExpr, node);
return check_implicit_selector_expr(c, o, node, type_hint);
case_end;
case_ast_node(ie, IndexExpr, node);
- check_expr(c, o, ie->expr);
- node->viral_state_flags |= ie->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
-
- Type *t = base_type(type_deref(o->type));
- bool is_ptr = is_type_pointer(o->type);
- bool is_const = o->mode == Addressing_Constant;
-
- if (is_type_map(t)) {
- Operand key = {};
- if (is_type_typeid(t->Map.key)) {
- check_expr_or_type(c, &key, ie->index, t->Map.key);
- } else {
- check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
- }
- check_assignment(c, &key, t->Map.key, str_lit("map index"));
- if (key.mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->mode = Addressing_MapIndex;
- o->type = t->Map.value;
- o->expr = node;
-
- add_package_dependency(c, "runtime", "__dynamic_map_get");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- return Expr_Expr;
- }
-
- i64 max_count = -1;
- bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
-
- if (is_const) {
- if (is_type_array(t)) {
- // OKay
- } else if (is_type_slice(t)) {
- // Okay
- } else if (is_type_enumerated_array(t)) {
- // Okay
- } else if (is_type_string(t)) {
- // Okay
- } else if (is_type_relative_slice(t)) {
- // Okay
- } else if (is_type_matrix(t)) {
- // Okay
- } else {
- valid = false;
- }
- }
-
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- defer (gb_string_free(str));
- defer (gb_string_free(type_str));
- if (is_const) {
- error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
- } else {
- error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (ie->index == nullptr) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Missing index for '%s'", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- Type *index_type_hint = nullptr;
- if (is_type_enumerated_array(t)) {
- Type *bt = base_type(t);
- GB_ASSERT(bt->kind == Type_EnumeratedArray);
- index_type_hint = bt->EnumeratedArray.index;
- }
-
- i64 index = 0;
- bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
- if (is_const) {
- if (index < 0) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s'", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- } else if (ok) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
- o->mode = Addressing_Constant;
- bool success = false;
- bool finish = false;
- o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
- if (!success) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
- }
-
- if (type_hint != nullptr && is_type_matrix(t)) {
- // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
- // if a type hint exists
- }
-
+ kind = check_index_expr(c, o, node, type_hint);
case_end;
case_ast_node(se, SliceExpr, node);
- check_expr(c, o, se->expr);
- node->viral_state_flags |= se->expr->viral_state_flags;
-
- if (o->mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- bool valid = false;
- i64 max_count = -1;
- Type *t = base_type(type_deref(o->type));
- switch (t->kind) {
- case Type_Basic:
- if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
- valid = true;
- if (o->mode == Addressing_Constant) {
- max_count = o->value.value_string.len;
- }
- o->type = type_deref(o->type);
- }
- break;
-
- case Type_Array:
- valid = true;
- max_count = t->Array.count;
- if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
- gbString str = expr_to_string(node);
- error(node, "Cannot slice array '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->type = alloc_type_slice(t->Array.elem);
- break;
-
- case Type_MultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
- case Type_Slice:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
- case Type_DynamicArray:
- valid = true;
- o->type = alloc_type_slice(t->DynamicArray.elem);
- break;
-
- case Type_Struct:
- if (is_type_soa_struct(t)) {
- valid = true;
- o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
- }
- break;
-
- case Type_RelativeSlice:
- valid = true;
- o->type = t->RelativeSlice.slice_type;
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(node);
- error(node, "Cannot relative slice '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- break;
- }
-
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
- gb_string_free(type_str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (se->low == nullptr && se->high != nullptr) {
- // It is okay to continue as it will assume the 1st index is zero
- }
-
- i64 indices[2] = {};
- Ast *nodes[2] = {se->low, se->high};
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- i64 index = max_count;
- if (nodes[i] != nullptr) {
- i64 capacity = -1;
- if (max_count >= 0) {
- capacity = max_count;
- }
- i64 j = 0;
- if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
- index = j;
- }
-
- node->viral_state_flags |= nodes[i]->viral_state_flags;
- } else if (i == 0) {
- index = 0;
- }
- indices[i] = index;
- }
-
- for (isize i = 0; i < gb_count_of(indices); i++) {
- i64 a = indices[i];
- for (isize j = i+1; j < gb_count_of(indices); j++) {
- i64 b = indices[j];
- if (a > b && b >= 0) {
- error(se->close, "Invalid slice indices: [%td > %td]", a, b);
- }
- }
- }
-
- if (max_count < 0) {
- if (o->mode == Addressing_Constant) {
- gbString s = expr_to_string(se->expr);
- error(se->expr, "Cannot slice constant value '%s'", s);
- gb_string_free(s);
- }
- }
-
- if (t->kind == Type_MultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- o->type = alloc_type_slice(t->MultiPointer.elem);
- }
-
- o->mode = Addressing_Value;
-
- if (is_type_string(t) && max_count >= 0) {
- bool all_constant = true;
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- if (nodes[i] != nullptr) {
- TypeAndValue tav = type_and_value_of_expr(nodes[i]);
- if (tav.mode != Addressing_Constant) {
- all_constant = false;
- break;
- }
- }
- }
- if (!all_constant) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot slice '%s' with non-constant indices", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
- o->expr = node;
- return kind;
- }
-
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
-
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
- }
-
+ kind = check_slice_expr(c, o, node, type_hint);
case_end;
case_ast_node(mie, MatrixIndexExpr, node);
@@ -8807,7 +9377,15 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
} else {
gbString str = expr_to_string(o->expr);
gbString typ = type_to_string(o->type);
+ begin_error_block();
+
error(o->expr, "Cannot dereference '%s' of type '%s'", str, typ);
+ if (o->type && is_type_multi_pointer(o->type)) {
+ error_line("\tDid you mean '%s[0]'?\n", str);
+ }
+
+ end_error_block();
+
gb_string_free(typ);
gb_string_free(str);
o->mode = Addressing_Invalid;
@@ -8887,6 +9465,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
+
+
ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = check_expr_base_internal(c, o, node, type_hint);
if (o->type != nullptr && core_type(o->type) == nullptr) {
@@ -8902,6 +9482,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
if (o->type != nullptr && is_type_untyped(o->type)) {
add_untyped(c, node, o->mode, o->type, o->value);
}
+ check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed");
+
add_type_and_value(c->info, node, o->mode, o->type, o->value);
return kind;
}
@@ -9061,18 +9643,7 @@ gbString string_append_string(gbString str, String string) {
gbString string_append_token(gbString str, Token token) {
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
str = string_append_string(str, token.string);
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
-
return str;
}
@@ -9299,6 +9870,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, fv->value, shorthand);
case_end;
+ case_ast_node(fv, EnumFieldValue, node);
+ str = write_expr_to_string(str, fv->name, shorthand);
+ if (fv->value) {
+ str = gb_string_appendc(str, " = ");
+ str = write_expr_to_string(str, fv->value, shorthand);
+ }
+ case_end;
case_ast_node(ht, HelperType, node);
str = gb_string_appendc(str, "#type ");
@@ -9390,6 +9968,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
+ if (f->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
for_array(i, f->names) {
Ast *name = f->names[i];
@@ -9469,9 +10050,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, ce->proc, shorthand);
str = gb_string_appendc(str, "(");
- for_array(i, ce->args) {
+ isize idx0 = cast(isize)ce->was_selector;
+ for (isize i = idx0; i < ce->args.count; i++) {
Ast *arg = ce->args[i];
- if (i > 0) {
+ if (i > idx0) {
str = gb_string_appendc(str, ", ");
}
str = write_expr_to_string(str, arg, shorthand);
@@ -9548,8 +10130,10 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
- if (st->maybe) str = gb_string_appendc(str, "#maybe ");
+ switch (st->kind) {
+ case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
+ }
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 24ad0eec1..a6f6f1a7d 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
ctx->state_flags = out;
}
@@ -607,7 +615,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
@@ -635,10 +643,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
bool is_ptr = is_type_pointer(e->type);
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
- Scope *found = scope_of_node(t->Struct.node);
- if (found == nullptr) {
- found = t->Struct.scope;
- }
+ Scope *found = t->Struct.scope;
GB_ASSERT(found != nullptr);
for_array(i, found->elements.entries) {
Entity *f = found->elements.entries[i].value;
@@ -692,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
-
-struct TypeAndToken {
- Type *type;
- Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) {
- if (operand.mode != Addressing_Constant) {
- return;
- }
- if (operand.value.kind == ExactValue_Invalid) {
- return;
- }
-
- uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
- isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
-
- multi_map_get_all(seen, key, taps);
- for (isize i = 0; i < count; i++) {
- TypeAndToken tap = taps[i];
- if (!are_types_identical(operand.type, tap.type)) {
- continue;
- }
-
- TokenPos pos = tap.token.pos;
- if (use_expr) {
- gbString expr_str = expr_to_string(operand.expr);
- error(operand.expr,
- "Duplicate case '%s'\n"
- "\tprevious case at %s",
- expr_str,
- token_pos_to_string(pos));
- gb_string_free(expr_str);
- } else {
- error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
- }
- return;
- }
- }
-
- TypeAndToken tap = {operand.type, ast_token(operand.expr)};
- multi_map_insert(seen, key, tap);
-}
-
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -964,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
- PtrMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1004,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
- case Token_Ellipsis: upper_op = Token_GtEq; break;
- case Token_RangeFull: upper_op = Token_GtEq; break;
- case Token_RangeHalf: upper_op = Token_Gt; break;
+ case Token_Ellipsis: upper_op = Token_LtEq; break;
+ case Token_RangeFull: upper_op = Token_LtEq; break;
+ case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1027,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
- if (is_type_enum(x.type)) {
- // TODO(bill): Fix this logic so it's fast!!!
-
- i64 v0 = exact_value_to_i64(lhs.value);
- i64 v1 = exact_value_to_i64(rhs.value);
- Operand v = {};
- v.mode = Addressing_Constant;
- v.type = x.type;
- v.expr = x.expr;
-
- Type *bt = base_type(x.type);
- GB_ASSERT(bt->kind == Type_Enum);
- for (i64 vi = v0; vi <= v1; vi++) {
- if (upper_op != Token_GtEq && vi == v1) {
- break;
- }
-
- bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
- GB_ASSERT(f->kind == Entity_Constant);
-
- i64 fv = exact_value_to_i64(f->Constant.value);
- if (fv == vi) {
- found = true;
- break;
- }
- }
- if (found) {
- v.value = exact_value_i64(vi);
- add_constant_switch_case(ctx, &seen, v);
- }
- }
- } else {
- add_constant_switch_case(ctx, &seen, lhs);
- if (upper_op == Token_GtEq) {
- add_constant_switch_case(ctx, &seen, rhs);
- }
- }
+ add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1110,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
- add_constant_switch_case(ctx, &seen, y);
+ add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1146,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
- error_no_newline(node, "Unhandled switch cases: ");
+ error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
@@ -1399,9 +1318,9 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) {
ast_node(bs, BlockStmt, body);
// NOTE(bill, 2020-09-23): This logic is prevent common erros with block statements
// e.g. if cond { x := 123; } // this is an error
- if (body->scope != nullptr && body->scope->elements.entries.count > 0) {
- if (body->scope->parent->node != nullptr) {
- switch (body->scope->parent->node->kind) {
+ if (bs->scope != nullptr && bs->scope->elements.entries.count > 0) {
+ if (bs->scope->parent->node != nullptr) {
+ switch (bs->scope->parent->node->kind) {
case Ast_IfStmt:
case Ast_ForStmt:
case Ast_RangeStmt:
@@ -1462,6 +1381,18 @@ bool all_operands_valid(Array const &operands) {
return true;
}
+bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *id_) {
+ BuiltinProcId id = BuiltinProc_Invalid;
+ Entity *e = entity_of_node(expr);
+ if (e != nullptr && e->kind == Entity_Builtin) {
+ if (e->Builtin.id && e->Builtin.id != BuiltinProc_DIRECTIVE) {
+ id = cast(BuiltinProcId)e->Builtin.id;
+ }
+ }
+ if (id_) *id_ = id;
+ return id != BuiltinProc_Invalid;
+}
+
void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -1486,29 +1417,43 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (kind == Expr_Stmt) {
return;
}
- Ast *expr = strip_or_return_expr(operand.expr);
+ Ast *expr = strip_or_return_expr(operand.expr);
if (expr->kind == Ast_CallExpr) {
+ BuiltinProcId builtin_id = BuiltinProc_Invalid;
+ bool do_require = false;
+
AstCallExpr *ce = &expr->CallExpr;
- Type *t = type_of_expr(ce->proc);
- if (is_type_proc(t)) {
- if (t->Proc.require_results) {
- gbString expr_str = expr_to_string(ce->proc);
- error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
- }
+ Type *t = base_type(type_of_expr(ce->proc));
+ if (t->kind == Type_Proc) {
+ do_require = t->Proc.require_results;
+ } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+ auto const &bp = builtin_procs[builtin_id];
+ do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+ }
+ if (do_require) {
+ gbString expr_str = expr_to_string(ce->proc);
+ error(node, "'%s' requires that its results must be handled", expr_str);
+ gb_string_free(expr_str);
}
return;
} else if (expr->kind == Ast_SelectorCallExpr) {
+ BuiltinProcId builtin_id = BuiltinProc_Invalid;
+ bool do_require = false;
+
AstSelectorCallExpr *se = &expr->SelectorCallExpr;
ast_node(ce, CallExpr, se->call);
- Type *t = type_of_expr(ce->proc);
- if (is_type_proc(t)) {
- if (t->Proc.require_results) {
- gbString expr_str = expr_to_string(ce->proc);
- error(node, "'%s' requires that its results must be handled", expr_str);
- gb_string_free(expr_str);
- }
+ Type *t = base_type(type_of_expr(ce->proc));
+ if (t->kind == Type_Proc) {
+ do_require = t->Proc.require_results;
+ } else if (check_stmt_internal_builtin_proc_id(ce->proc, &builtin_id)) {
+ auto const &bp = builtin_procs[builtin_id];
+ do_require = bp.kind == Expr_Expr && !bp.ignore_results;
+ }
+ if (do_require) {
+ gbString expr_str = expr_to_string(ce->proc);
+ error(node, "'%s' requires that its results must be handled", expr_str);
+ gb_string_free(expr_str);
}
return;
}
@@ -1616,7 +1561,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
Operand lhs = {Addressing_Invalid};
Operand rhs = {Addressing_Invalid};
- Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
+ Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
ast_node(be, BinaryExpr, binary_expr);
be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
@@ -2197,7 +2142,26 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
if (new_name_count == 0) {
- error(node, "No new declarations on the lhs");
+ begin_error_block();
+ error(node, "No new declarations on the left hand side");
+ bool all_underscore = true;
+ for_array(i, vd->names) {
+ Ast *name = vd->names[i];
+ if (name->kind == Ast_Ident) {
+ if (!is_blank_ident(name)) {
+ all_underscore = false;
+ break;
+ }
+ } else {
+ all_underscore = false;
+ break;
+ }
+ }
+ if (all_underscore) {
+ error_line("\tSuggestion: Try changing the declaration (:=) to an assignment (=)\n");
+ }
+
+ end_error_block();
}
Type *init_type = nullptr;
@@ -2233,7 +2197,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
e->state = EntityState_Resolved;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
- e->Variable.thread_local_model = ac.thread_local_model;
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -2246,6 +2209,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'static' variables cannot be declared within a defer statement");
+ }
}
}
if (ac.thread_local_model != "") {
@@ -2254,10 +2220,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'thread_local' variables cannot be declared within a defer statement");
+ }
}
e->Variable.thread_local_model = ac.thread_local_model;
}
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
+
if (ac.is_static && ac.thread_local_model != "") {
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
}
@@ -2339,7 +2313,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else if (is_type_struct(t) || is_type_raw_union(t)) {
ERROR_BLOCK();
- Scope *scope = scope_of_node(t->Struct.node);
+ Scope *scope = t->Struct.scope;
+ GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 398967af8..dea523599 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -109,14 +109,19 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
}
i32 field_src_index = 0;
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
+
ast_node(p, Field, param);
Ast *type_expr = p->type;
Type *type = nullptr;
+ CommentGroup *docs = p->docs;
+ CommentGroup *comment = p->comment;
if (type_expr != nullptr) {
type = check_type_expr(ctx, type_expr, nullptr);
@@ -139,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
}
bool is_using = (p->flags&FieldFlag_using) != 0;
+ bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
for_array(j, p->names) {
Ast *name = p->names[j];
@@ -152,6 +158,18 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx, ctx->scope, name, field);
+ field->Variable.field_group_index = field_group_index;
+ if (is_subtype) {
+ field->flags |= EntityFlag_Subtype;
+ }
+
+ if (j == 0) {
+ field->Variable.docs = docs;
+ }
+ if (j+1 == p->names.count) {
+ field->Variable.comment = comment;
+ }
+
array_add(&fields_array, field);
String tag = p->tag.string;
if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -180,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
populate_using_entity_scope(ctx, node, p, type);
}
+
+ if (is_subtype && p->names.count > 0) {
+ Type *first_type = fields_array[fields_array.count-1]->type;
+ Type *t = base_type(type_deref(first_type));
+
+ if (!does_field_type_allow_using(t) &&
+ p->names.count >= 1 &&
+ p->names[0]->kind == Ast_Ident) {
+ Token name_token = p->names[0]->Ident.token;
+ gbString type_str = type_to_string(first_type);
+ error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
+ gb_string_free(type_str);
+ }
+ }
}
*fields = slice_from_array(fields_array);
@@ -309,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
}
named_type->Named.type_name = e;
+ GB_ASSERT(original_type->kind == Type_Named);
+ e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
+ // TODO(bill): Is this even correct? Or should the metadata be copied?
+ e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
@@ -639,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) {
+ if (!type_has_nil(t)) {
+ gbString s = type_to_string(t);
+ error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
+ gb_string_free(s);
+ }
+ }
}
}
}
union_type->Union.variants = slice_from_array(variants);
- union_type->Union.no_nil = ut->no_nil;
- union_type->Union.maybe = ut->maybe;
- if (union_type->Union.no_nil) {
+ union_type->Union.kind = ut->kind;
+ switch (ut->kind) {
+ case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
- }
- if (union_type->Union.maybe) {
+ break;
+ case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
+ break;
}
if (ut->align != nullptr) {
@@ -718,20 +763,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
Ast *ident = nullptr;
Ast *init = nullptr;
u32 entity_flags = 0;
- if (field->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, field);
- if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
- error(field, "An enum field's name must be an identifier");
- continue;
- }
- ident = fv->field;
- init = fv->value;
- } else if (field->kind == Ast_Ident) {
- ident = field;
- } else {
+ if (field->kind != Ast_EnumFieldValue) {
error(field, "An enum field's name must be an identifier");
continue;
}
+ ident = field->EnumFieldValue.name;
+ init = field->EnumFieldValue.value;
+ if (ident == nullptr || ident->kind != Ast_Ident) {
+ error(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ CommentGroup *docs = field->EnumFieldValue.docs;
+ CommentGroup *comment = field->EnumFieldValue.comment;
+
String name = ident->Ident.token.string;
if (init != nullptr) {
@@ -789,6 +833,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags |= entity_flags;
+ e->Constant.docs = docs;
+ e->Constant.comment = comment;
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -922,20 +968,19 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
i64 lower = big_int_to_i64(&i);
i64 upper = big_int_to_i64(&j);
- bool lower_changed = false;
+ i64 actual_lower = lower;
i64 bits = MAX_BITS;
if (type->BitSet.underlying != nullptr) {
bits = 8*type_size_of(type->BitSet.underlying);
if (lower > 0) {
- lower = 0;
- lower_changed = true;
+ actual_lower = 0;
} else if (lower < 0) {
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower);
}
}
- i64 bits_required = upper-lower;
+ i64 bits_required = upper-actual_lower;
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
@@ -959,7 +1004,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
break;
}
if (!is_valid) {
- if (lower_changed) {
+ if (actual_lower != lower) {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
@@ -1189,13 +1234,13 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ
}
-Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) {
+Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand const &operand) {
bool modify_type = !ctx->no_polymorphic_errors;
bool show_error = modify_type && !ctx->hide_polymorphic_errors;
if (!is_operand_value(operand)) {
if (show_error) {
gbString pts = type_to_string(poly_type);
- gbString ots = type_to_string(operand.type);
+ gbString ots = type_to_string(operand.type, true);
defer (gb_string_free(pts));
defer (gb_string_free(ots));
error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts);
@@ -1208,7 +1253,7 @@ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Oper
}
if (show_error) {
gbString pts = type_to_string(poly_type);
- gbString ots = type_to_string(operand.type);
+ gbString ots = type_to_string(operand.type, true);
defer (gb_string_free(pts));
defer (gb_string_free(ots));
error(operand.expr, "Cannot determine polymorphic type from parameter: '%s' to '%s'", ots, pts);
@@ -1300,7 +1345,9 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} else {
- error(expr, "Default parameter must be a constant, %d", o.mode);
+ gbString s = expr_to_string(o.expr);
+ error(expr, "Default parameter must be a constant, got %s", s);
+ gb_string_free(s);
}
}
} else {
@@ -1367,11 +1414,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
isize variadic_index = -1;
bool is_c_vararg = false;
auto variables = array_make(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
ast_node(p, Field, param);
Ast *type_expr = unparen_expr(p->type);
Type *type = nullptr;
@@ -1564,9 +1613,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
p->flags &= ~FieldFlag_const;
}
if (p->flags&FieldFlag_any_int) {
- error(name, "'#const' can only be applied to variable fields");
+ error(name, "'#any_int' can only be applied to variable fields");
p->flags &= ~FieldFlag_any_int;
}
+ if (p->flags&FieldFlag_by_ptr) {
+ error(name, "'#by_ptr' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_by_ptr;
+ }
param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
param->TypeName.is_type_alias = true;
@@ -1614,7 +1667,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
ok = false;
}
} else if (p->flags&FieldFlag_any_int) {
- if (!is_type_integer(op.type) || !is_type_integer(type)) {
+ if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
ok = false;
} else if (!check_is_castable_to(ctx, &op, type)) {
ok = false;
@@ -1643,10 +1696,17 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_no_alias) {
if (!is_type_pointer(type)) {
- error(name, "'#no_alias' can only be applied to fields of pointer type");
+ error(name, "'#no_alias' can only be applied pointer typed parameters");
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
+ if (p->flags&FieldFlag_by_ptr) {
+ if (is_type_internally_pointer_like(type)) {
+ error(name, "'#by_ptr' can only be applied to non-pointer-like parameters");
+ p->flags &= ~FieldFlag_by_ptr; // Remove the flag
+ }
+ }
+
if (is_poly_name) {
if (p->flags&FieldFlag_no_alias) {
error(name, "'#no_alias' can only be applied to non constant values");
@@ -1664,6 +1724,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
error(name, "'#const' can only be applied to variable fields");
p->flags &= ~FieldFlag_const;
}
+ if (p->flags&FieldFlag_by_ptr) {
+ error(name, "'#by_ptr' can only be applied to variable fields");
+ p->flags &= ~FieldFlag_by_ptr;
+ }
if (!is_type_constant_type(type) && !is_type_polymorphic(type)) {
gbString str = type_to_string(type);
@@ -1672,9 +1736,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type));
+ param->Constant.field_group_index = field_group_index;
} else {
param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
}
}
if (p->flags&FieldFlag_no_alias) {
@@ -1684,7 +1750,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
param->flags |= EntityFlag_AutoCast;
}
if (p->flags&FieldFlag_any_int) {
- if (!is_type_integer(param->type)) {
+ if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
gbString str = type_to_string(param->type);
error(name, "A parameter with '#any_int' must be an integer, got %s", str);
gb_string_free(str);
@@ -1694,6 +1760,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
if (p->flags&FieldFlag_const) {
param->flags |= EntityFlag_ConstInput;
}
+ if (p->flags&FieldFlag_by_ptr) {
+ param->flags |= EntityFlag_ByPtr;
+ }
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
add_entity(ctx, scope, name, param);
@@ -1768,7 +1837,10 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
}
auto variables = array_make(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, results) {
+ field_group_index += 1;
+
ast_node(field, Field, results[i]);
Ast *default_value = unparen_expr(field->default_value);
ParameterValue param_value = {};
@@ -1799,6 +1871,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
token.string = str_lit("");
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = -1;
array_add(&variables, param);
} else {
for_array(j, field->names) {
@@ -1822,6 +1895,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->flags |= EntityFlag_Result;
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
array_add(&variables, param);
add_entity(ctx, scope, name, param);
// NOTE(bill): Removes `declared but not used` when using -vet
@@ -1883,6 +1957,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
c->scope->flags &= ~ScopeFlag_ContextDefined;
}
+ TargetArchKind arch = build_context.metrics.arch;
+ switch (cc) {
+ case ProcCC_StdCall:
+ case ProcCC_FastCall:
+ if (arch != TargetArch_i386 && arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ case ProcCC_Win64:
+ case ProcCC_SysV:
+ if (arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ }
+
+
bool variadic = false;
isize variadic_index = -1;
bool success = true;
@@ -1896,20 +1989,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
- if (param_count > 0) {
- for_array(i, params->Tuple.variables) {
- Entity *param = params->Tuple.variables[i];
- if (param->kind == Entity_Variable) {
- ParameterValue pv = param->Variable.param_value;
- if (pv.kind == ParameterValue_Constant &&
- pv.value.kind == ExactValue_Procedure) {
- type->Proc.has_proc_default_values = true;
- break;
- }
- }
- }
- }
-
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";
@@ -1939,8 +2018,9 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
} else {
bool ok = false;
- if (proc_type_node->file && proc_type_node->file->pkg) {
- ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
+ AstFile *file = proc_type_node->file();
+ if (file && file->pkg) {
+ ok = file->pkg->scope == ctx->info->runtime_package->scope;
}
if (!ok) {
@@ -1966,10 +2046,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (param_count > 0) {
Entity *end = params->Tuple.variables[param_count-1];
if (end->flags&EntityFlag_CVarArg) {
- if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) {
+ switch (cc) {
+ default:
type->Proc.c_vararg = true;
- } else {
+ break;
+ case ProcCC_Odin:
+ case ProcCC_Contextless:
error(end->token, "Calling convention does not support #c_vararg");
+ break;
}
}
}
@@ -2105,7 +2189,7 @@ void init_map_entry_type(Type *type) {
/*
struct {
- hash: runtime.Map_Hash,
+ hash: uintptr,
next: int,
key: Key,
value: Value,
@@ -2284,10 +2368,21 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
}
if (!is_type_valid_for_matrix_elems(elem)) {
+ if (elem == t_typeid) {
+ Entity *e = entity_of_node(mt->elem);
+ if (e && e->kind == Entity_TypeName && e->TypeName.is_type_alias) {
+ // HACK TODO(bill): This is to allow polymorphic parameters for matrix elements
+ // proc($T: typeid) -> matrix[2, 2]T
+ //
+ // THIS IS NEEDS TO BE FIXED AND NOT USE THIS HACK
+ goto type_assign;
+ }
+ }
gbString s = type_to_string(elem);
error(column.expr, "Matrix elements types are limited to integers, floats, and complex, got %s", s);
gb_string_free(s);
}
+type_assign:;
*type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
@@ -2610,7 +2705,28 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
case_end;
case_ast_node(pt, PointerType, e);
- *type = alloc_type_pointer(check_type(ctx, pt->type));
+ CheckerContext c = *ctx;
+ c.type_path = new_checker_type_path();
+ defer (destroy_checker_type_path(c.type_path));
+
+ Type *elem = t_invalid;
+ Operand o = {};
+ check_expr_or_type(&c, &o, pt->type);
+ if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) {
+ // NOTE(bill): call check_type_expr again to get a consistent error message
+ begin_error_block();
+ elem = check_type_expr(&c, pt->type, nullptr);
+ if (o.mode == Addressing_Variable) {
+ gbString s = expr_to_string(pt->type);
+ error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s);
+ gb_string_free(s);
+ }
+ end_error_block();
+ } else {
+ elem = o.type;
+ }
+
+ *type = alloc_type_pointer(elem);
set_base_type(named_type, *type);
return true;
case_end;
@@ -2678,29 +2794,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
- bool is_partial = false;
+ bool is_sparse = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name.string;
- if (name == "partial") {
- is_partial = true;
+ if (name == "sparse") {
+ is_sparse = true;
} else {
error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
}
}
- if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
error(e, "Non-contiguous enumeration used as an index in an enumerated array");
long long ea_count = cast(long long)t->EnumeratedArray.count;
long long enum_count = cast(long long)bt->Enum.fields.count;
error_line("\tenumerated array length: %lld\n", ea_count);
error_line("\tenum field count: %lld\n", enum_count);
- error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n");
+ error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
if (2*enum_count < ea_count) {
error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
- error_line("\t this warning will be removed if #partial is applied\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
}
}
+ t->EnumeratedArray.is_sparse = is_sparse;
*type = t;
@@ -2719,15 +2836,27 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (name == "soa") {
*type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
} else if (name == "simd") {
- if (!is_type_valid_vector_elem(elem)) {
+ if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) {
gbString str = type_to_string(elem);
- error(at->elem, "Invalid element type for 'intrinsics.simd_vector', expected an integer or float with no specific endianness, got '%s'", str);
+ error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str);
gb_string_free(str);
*type = alloc_type_array(elem, count, generic_type);
goto array_end;
}
- *type = alloc_type_simd_vector(count, elem);
+ if (generic_type != nullptr) {
+ // Ignore
+ } else if (count < 1 || !is_power_of_two(count)) {
+ error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count);
+ *type = alloc_type_array(elem, count, generic_type);
+ goto array_end;
+ }
+
+ *type = alloc_type_simd_vector(count, elem, generic_type);
+
+ if (count > SIMD_ELEMENT_COUNT_MAX) {
+ error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count);
+ }
} else {
error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name));
*type = alloc_type_array(elem, count, generic_type);
@@ -2950,5 +3079,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
}
set_base_type(named_type, type);
+ check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed");
+
return type;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index cffaad348..874839ece 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -4,7 +4,7 @@
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
-
+Type *check_type(CheckerContext *ctx, Ast *e);
bool is_operand_value(Operand o) {
switch (o.mode) {
@@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) {
return o.mode == Addressing_Value && o.type == t_untyped_undef;
}
+bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
+ if (build_context.disallow_rtti && type) {
+ if (is_type_any(type)) {
+ gbString t = type_to_string(type);
+ error(token, format, t);
+ gb_string_free(t);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) {
+ GB_ASSERT(expr != nullptr);
+ return check_rtti_type_disallowed(ast_token(expr), type, format);
+}
+
void scope_reset(Scope *scope) {
if (scope == nullptr) return;
@@ -318,7 +335,43 @@ void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
GB_ASSERT(node != nullptr);
GB_ASSERT(scope != nullptr);
scope->node = node;
- node->scope = scope;
+ switch (node->kind) {
+ case Ast_BlockStmt: node->BlockStmt.scope = scope; break;
+ case Ast_IfStmt: node->IfStmt.scope = scope; break;
+ case Ast_ForStmt: node->ForStmt.scope = scope; break;
+ case Ast_RangeStmt: node->RangeStmt.scope = scope; break;
+ case Ast_UnrollRangeStmt: node->UnrollRangeStmt.scope = scope; break;
+ case Ast_CaseClause: node->CaseClause.scope = scope; break;
+ case Ast_SwitchStmt: node->SwitchStmt.scope = scope; break;
+ case Ast_TypeSwitchStmt: node->TypeSwitchStmt.scope = scope; break;
+ case Ast_ProcType: node->ProcType.scope = scope; break;
+ case Ast_StructType: node->StructType.scope = scope; break;
+ case Ast_UnionType: node->UnionType.scope = scope; break;
+ case Ast_EnumType: node->EnumType.scope = scope; break;
+ default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
+ }
+}
+
+Scope *scope_of_node(Ast *node) {
+ if (node == nullptr) {
+ return nullptr;
+ }
+ switch (node->kind) {
+ case Ast_BlockStmt: return node->BlockStmt.scope;
+ case Ast_IfStmt: return node->IfStmt.scope;
+ case Ast_ForStmt: return node->ForStmt.scope;
+ case Ast_RangeStmt: return node->RangeStmt.scope;
+ case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.scope;
+ case Ast_CaseClause: return node->CaseClause.scope;
+ case Ast_SwitchStmt: return node->SwitchStmt.scope;
+ case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.scope;
+ case Ast_ProcType: return node->ProcType.scope;
+ case Ast_StructType: return node->StructType.scope;
+ case Ast_UnionType: return node->UnionType.scope;
+ case Ast_EnumType: return node->EnumType.scope;
+ }
+ GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
+ return nullptr;
}
@@ -410,7 +463,7 @@ Entity *scope_lookup(Scope *s, String const &name) {
-Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
+Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity, bool use_mutex=true) {
if (name == "") {
return nullptr;
}
@@ -418,8 +471,8 @@ Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
Entity **found = nullptr;
Entity *result = nullptr;
- mutex_lock(&s->mutex);
- defer (mutex_unlock(&s->mutex));
+ if (use_mutex) mutex_lock(&s->mutex);
+ defer (if (use_mutex) mutex_unlock(&s->mutex));
found = string_map_get(&s->elements, key);
@@ -449,9 +502,9 @@ end:;
return result;
}
-Entity *scope_insert(Scope *s, Entity *entity) {
+Entity *scope_insert(Scope *s, Entity *entity, bool use_mutex) {
String name = entity->token.string;
- return scope_insert_with_name(s, name, entity);
+ return scope_insert_with_name(s, name, entity, use_mutex);
}
@@ -468,6 +521,7 @@ enum VettedEntityKind {
VettedEntity_Unused,
VettedEntity_Shadowed,
+ VettedEntity_Shadowed_And_Unused,
};
struct VettedEntity {
VettedEntityKind kind;
@@ -490,6 +544,28 @@ GB_COMPARE_PROC(vetted_entity_variable_pos_cmp) {
return token_pos_cmp(x->token.pos, y->token.pos);
}
+bool check_vet_shadowing_assignment(Checker *c, Entity *shadowed, Ast *expr) {
+ Ast *init = unparen_expr(expr);
+ if (init == nullptr) {
+ return false;
+ }
+ if (init->kind == Ast_Ident) {
+ // TODO(bill): Which logic is better? Same name or same entity
+ // bool ignore = init->Ident.token.string == name;
+ bool ignore = init->Ident.entity == shadowed;
+ if (ignore) {
+ return true;
+ }
+ } else if (init->kind == Ast_TernaryIfExpr) {
+ bool x = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.x);
+ bool y = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.y);
+ if (x || y) {
+ return true;
+ }
+ }
+
+ return false;
+}
bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
@@ -540,17 +616,14 @@ bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
}
// NOTE(bill): Ignore intentional redeclaration
- // x := x;
+ // x := x
// Suggested in issue #637 (2020-05-11)
+ // Also allow the following
+ // x := x if cond else y
+ // x := z if cond else x
if ((e->flags & EntityFlag_Using) == 0 && e->kind == Entity_Variable) {
- Ast *init = unparen_expr(e->Variable.init_expr);
- if (init != nullptr && init->kind == Ast_Ident) {
- // TODO(bill): Which logic is better? Same name or same entity
- // bool ignore = init->Ident.token.string == name;
- bool ignore = init->Ident.entity == shadowed;
- if (ignore) {
- return false;
- }
+ if (check_vet_shadowing_assignment(c, shadowed, e->Variable.init_expr)) {
+ return false;
}
}
@@ -586,15 +659,21 @@ void check_scope_usage(Checker *c, Scope *scope) {
Array vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
if (e == nullptr) continue;
- VettedEntity ve = {};
- if (vet_unused && check_vet_unused(c, e, &ve)) {
- array_add(&vetted_entities, ve);
- }
- if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
- array_add(&vetted_entities, ve);
+ VettedEntity ve_unused = {};
+ VettedEntity ve_shadowed = {};
+ bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
+ if (is_unused && is_shadowed) {
+ VettedEntity ve_both = ve_shadowed;
+ ve_both.kind = VettedEntity_Shadowed_And_Unused;
+ array_add(&vetted_entities, ve_both);
+ } else if (is_unused) {
+ array_add(&vetted_entities, ve_unused);
+ } else if (is_shadowed) {
+ array_add(&vetted_entities, ve_shadowed);
}
}
@@ -606,16 +685,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
Entity *other = ve.other;
String name = e->token.string;
- if (build_context.vet) {
+ if (ve.kind == VettedEntity_Shadowed_And_Unused) {
+ error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
+ } else if (build_context.vet) {
switch (ve.kind) {
case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name));
break;
case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) {
- error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
} else {
- error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
}
break;
default:
@@ -652,12 +733,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
ptr_set_add(&d->deps, e);
mutex_unlock(&info->deps_mutex);
}
-void add_type_info_dependency(DeclInfo *d, Type *type) {
+void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
if (d == nullptr) {
return;
}
- // NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
+ if (require_mutex) {
+ mutex_lock(&info->deps_mutex);
+ }
ptr_set_add(&d->type_info_deps, type);
+ if (require_mutex) {
+ mutex_unlock(&info->deps_mutex);
+ }
}
AstPackage *get_core_package(CheckerInfo *info, String name) {
@@ -683,12 +769,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
String n = make_string_c(name);
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
Entity *e = scope_lookup(p->scope, n);
- e->flags |= EntityFlag_Used;
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
add_dependency(c->info, c->decl, e);
}
+void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+ String n = make_string_c(name);
+ AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
+ Entity *e = scope_lookup(p->scope, n);
+ if (e == nullptr) {
+ return;
+ }
+ GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
+ add_dependency(c->info, c->decl, e);
+}
+
+
void add_declaration_dependency(CheckerContext *c, Entity *e) {
if (e == nullptr) {
return;
@@ -744,6 +843,69 @@ AstPackage *create_builtin_package(char const *name) {
return pkg;
}
+struct GlobalEnumValue {
+ char const *name;
+ i64 value;
+};
+
+Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) {
+ Scope *scope = create_scope(nullptr, builtin_pkg->scope);
+ Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+
+ Type *enum_type = alloc_type_enum();
+ Type *named_type = alloc_type_named(type_name, enum_type, entity);
+ set_base_type(named_type, enum_type);
+ enum_type->Enum.base_type = t_int;
+ enum_type->Enum.scope = scope;
+ entity->type = named_type;
+
+ auto fields = array_make(permanent_allocator(), value_count);
+ for (isize i = 0; i < value_count; i++) {
+ i64 value = values[i].value;
+ Entity *e = alloc_entity_constant(scope, make_token_ident(values[i].name), named_type, exact_value_i64(value));
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ fields[i] = e;
+
+ Entity *ie = scope_insert(scope, e);
+ GB_ASSERT(ie == nullptr);
+ }
+
+
+ enum_type->Enum.fields = fields;
+ enum_type->Enum.min_value_index = 0;
+ enum_type->Enum.max_value_index = value_count-1;
+ enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value;
+ enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value;
+
+
+ if (enum_type_) *enum_type_ = named_type;
+
+ return slice_from_array(fields);
+}
+void add_global_enum_constant(Slice const &fields, char const *name, i64 value) {
+ for (Entity *field : fields) {
+ GB_ASSERT(field->kind == Entity_Constant);
+ if (value == exact_value_to_i64(field->Constant.value)) {
+ add_global_constant(name, field->type, field->Constant.value);
+ return;
+ }
+ }
+ GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
+}
+
+Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) {
+ Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+ Type *named_type = alloc_type_named(type_name, backing_type, e);
+ e->type = named_type;
+ set_base_type(named_type, backing_type);
+ if (scope_insert(scope, e)) {
+ compiler_error("double declaration of %.*s", LIT(e->token.string));
+ }
+ return named_type;
+}
+
+
void init_universal(void) {
BuildContext *bc = &build_context;
@@ -753,7 +915,8 @@ void init_universal(void) {
// Types
for (isize i = 0; i < gb_count_of(basic_types); i++) {
- add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]);
+ String const &name = basic_types[i].Basic.name;
+ add_global_type_entity(name, &basic_types[i]);
}
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
@@ -772,14 +935,97 @@ void init_universal(void) {
add_global_bool_constant("false", false);
// TODO(bill): Set through flags in the compiler
- add_global_string_constant("ODIN_OS", bc->ODIN_OS);
- add_global_string_constant("ODIN_ARCH", bc->ODIN_ARCH);
- add_global_string_constant("ODIN_ENDIAN", bc->ODIN_ENDIAN);
add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+
+ {
+ GlobalEnumValue values[TargetOs_COUNT] = {
+ {"Unknown", TargetOs_Invalid},
+ {"Windows", TargetOs_windows},
+ {"Darwin", TargetOs_darwin},
+ {"Linux", TargetOs_linux},
+ {"Essence", TargetOs_essence},
+ {"FreeBSD", TargetOs_freebsd},
+ {"OpenBSD", TargetOs_openbsd},
+ {"WASI", TargetOs_wasi},
+ {"JS", TargetOs_js},
+ {"Freestanding", TargetOs_freestanding},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+ add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
+ }
+
+ {
+ GlobalEnumValue values[TargetArch_COUNT] = {
+ {"Unknown", TargetArch_Invalid},
+ {"amd64", TargetArch_amd64},
+ {"i386", TargetArch_i386},
+ {"arm32", TargetArch_arm32},
+ {"arm64", TargetArch_arm64},
+ {"wasm32", TargetArch_wasm32},
+ {"wasm64", TargetArch_wasm64},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
+ add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
+ }
- add_global_string_constant("ODIN_BUILD_MODE", bc->ODIN_BUILD_MODE);
+ {
+ GlobalEnumValue values[BuildMode_COUNT] = {
+ {"Executable", BuildMode_Executable},
+ {"Dynamic", BuildMode_DynamicLibrary},
+ {"Object", BuildMode_Object},
+ {"Assembly", BuildMode_Assembly},
+ {"LLVM_IR", BuildMode_LLVM_IR},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Build_Mode_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode);
+ }
+
+ {
+ GlobalEnumValue values[TargetEndian_COUNT] = {
+ {"Unknown", TargetEndian_Invalid},
+
+ {"Little", TargetEndian_Little},
+ {"Big", TargetEndian_Big},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Endian_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ENDIAN", target_endians[bc->metrics.arch]);
+ add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
+ }
+
+ {
+ GlobalEnumValue values[ErrorPosStyle_COUNT] = {
+ {"Default", ErrorPosStyle_Default},
+ {"Unix", ErrorPosStyle_Unix},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
+ }
+
+ {
+ GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = {
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst},
+ };
+
+ add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order);
+ GB_ASSERT(t_atomic_memory_order->kind == Type_Named);
+ scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
+ }
+
+
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
@@ -787,6 +1033,9 @@ void init_universal(void) {
add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
+ add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
+ add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
+ add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti);
// Builtin Procedures
@@ -851,6 +1100,17 @@ void init_universal(void) {
t_f64_ptr = alloc_type_pointer(t_f64);
t_u8_slice = alloc_type_slice(t_u8);
t_string_slice = alloc_type_slice(t_string);
+
+ // intrinsics types for objective-c stuff
+ {
+ t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct());
+ t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
+ t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct());
+
+ t_objc_id = alloc_type_pointer(t_objc_object);
+ t_objc_SEL = alloc_type_pointer(t_objc_selector);
+ t_objc_Class = alloc_type_pointer(t_objc_class);
+ }
}
@@ -905,6 +1165,11 @@ void init_checker_info(CheckerInfo *i) {
mutex_init(&i->foreign_mutex);
semaphore_init(&i->collect_semaphore);
+
+ mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
+
+ mutex_init(&i->objc_types_mutex);
+ map_init(&i->objc_msgSend_types, a);
}
void destroy_checker_info(CheckerInfo *i) {
@@ -937,6 +1202,9 @@ void destroy_checker_info(CheckerInfo *i) {
mutex_destroy(&i->type_and_value_mutex);
mutex_destroy(&i->identifier_uses_mutex);
mutex_destroy(&i->foreign_mutex);
+
+ mutex_destroy(&i->objc_types_mutex);
+ map_destroy(&i->objc_msgSend_types);
}
CheckerContext make_checker_context(Checker *c) {
@@ -1081,9 +1349,6 @@ AstFile *ast_file_of_filename(CheckerInfo *i, String filename) {
}
return nullptr;
}
-Scope *scope_of_node(Ast *node) {
- return node->scope;
-}
ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) {
if (c->untyped != nullptr) {
ExprInfo **found = map_get(c->untyped, expr);
@@ -1144,7 +1409,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
// TODO(bill): This is O(n) and can be very slow
for_array(i, info->type_info_map.entries){
auto *e = &info->type_info_map.entries[i];
- if (are_types_identical(e->key, type)) {
+ if (are_types_identical_unique_tuples(e->key, type)) {
entry_index = e->value;
// NOTE(bill): Add it to the search map
map_set(&info->type_info_map, type, entry_index);
@@ -1195,7 +1460,7 @@ void add_type_and_value(CheckerInfo *i, Ast *expr, AddressingMode mode, Type *ty
while (prev_expr != expr) {
prev_expr = expr;
expr->tav.mode = mode;
- if (type != nullptr && expr->tav.type != nullptr &&
+ if (type != nullptr && expr->tav.type != nullptr &&
is_type_any(type) && is_type_untyped(expr->tav.type)) {
// ignore
} else {
@@ -1391,7 +1656,7 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
return false;
} else if (name == "linkage") {
return false;
- }
+ }
}
}
}
@@ -1463,6 +1728,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) {
void add_type_info_type(CheckerContext *c, Type *t) {
void add_type_info_type_internal(CheckerContext *c, Type *t);
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
mutex_lock(&c->info->type_info_mutex);
add_type_info_type_internal(c, t);
mutex_unlock(&c->info->type_info_mutex);
@@ -1480,7 +1749,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
return;
}
- add_type_info_dependency(c->decl, t);
+ add_type_info_dependency(c->info, c->decl, t, false);
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
@@ -1492,7 +1761,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
isize ti_index = -1;
for_array(i, c->info->type_info_map.entries) {
auto *e = &c->info->type_info_map.entries[i];
- if (are_types_identical(t, e->key)) {
+ if (are_types_identical_unique_tuples(t, e->key)) {
// Duplicate entry
ti_index = e->value;
prev = true;
@@ -1609,6 +1878,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
} else {
add_type_info_type_internal(c, t_type_info_ptr);
}
+ add_type_info_type_internal(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_type_info_type_internal(c, bt->Union.variants[i]);
}
@@ -1632,6 +1902,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
}
}
}
+ add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_type_info_type_internal(c, f->type);
@@ -1671,7 +1942,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -1825,6 +2096,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
} else {
add_min_dep_type_info(c, t_type_info_ptr);
}
+ add_min_dep_type_info(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_min_dep_type_info(c, bt->Union.variants[i]);
}
@@ -1848,6 +2120,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
}
}
}
+ add_min_dep_type_info(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_min_dep_type_info(c, f->type);
@@ -1886,7 +2159,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->RelativeSlice.slice_type);
add_min_dep_type_info(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -1971,23 +2244,27 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
- String required_runtime_entities[] = {
+#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
+ if (condition) { \
+ String entities[] = {__VA_ARGS__}; \
+ for (isize i = 0; i < gb_count_of(entities); i++) { \
+ force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
+ } \
+ } \
+} while (0)
+
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
// Odin types
- str_lit("Type_Info"),
str_lit("Source_Code_Location"),
str_lit("Context"),
str_lit("Allocator"),
str_lit("Logger"),
- // Global variables
- str_lit("args__"),
- str_lit("type_table"),
-
// Odin internal procedures
str_lit("__init_context"),
- str_lit("__type_info_of"),
str_lit("cstring_to_string"),
- str_lit("_cleanup_runtime"),
+ str_lit("_cleanup_runtime"),
// Pseudo-CRT required procedures
str_lit("memset"),
@@ -2014,38 +2291,40 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("gnu_h2f_ieee"),
str_lit("gnu_f2h_ieee"),
str_lit("extendhfsf2"),
-
+
// WASM Specific
str_lit("__ashlti3"),
- };
- for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
- }
+ str_lit("__multi3"),
+ );
- if (build_context.no_crt) {
- String required_no_crt_entities[] = {
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- };
- for (isize i = 0; i < gb_count_of(required_no_crt_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_no_crt_entities[i]);
- }
- }
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti,
+ // Odin types
+ str_lit("Type_Info"),
- if (!build_context.no_bounds_check) {
- String bounds_check_entities[] = {
- // Bounds checking related procedures
- str_lit("bounds_check_error"),
- str_lit("matrix_bounds_check_error"),
- str_lit("slice_expr_error_hi"),
- str_lit("slice_expr_error_lo_hi"),
- str_lit("multi_pointer_slice_expr_error"),
- };
- for (isize i = 0; i < gb_count_of(bounds_check_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, bounds_check_entities[i]);
- }
- }
+ // Global variables
+ str_lit("type_table"),
+ str_lit("__type_info_of"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
+ // Global variables
+ str_lit("args__"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
+ // NOTE(bill): Only if these exist
+ str_lit("_tls_index"),
+ str_lit("_fltused"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
+ // Bounds checking related procedures
+ str_lit("bounds_check_error"),
+ str_lit("matrix_bounds_check_error"),
+ str_lit("slice_expr_error_hi"),
+ str_lit("slice_expr_error_lo_hi"),
+ str_lit("multi_pointer_slice_expr_error"),
+ );
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
@@ -2076,34 +2355,38 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
case Entity_Variable:
if (e->Variable.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
break;
case Entity_Procedure:
if (e->Procedure.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
if (e->flags & EntityFlag_Init) {
Type *t = base_type(e->type);
GB_ASSERT(t->kind == Type_Proc);
-
+
bool is_init = true;
-
+
if (t->Proc.param_count != 0 || t->Proc.result_count != 0) {
gbString str = type_to_string(t);
error(e->token, "@(init) procedures must have a signature type with no parameters nor results, got %s", str);
gb_string_free(str);
is_init = false;
}
-
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(init) procedures must be declared at the file scope");
is_init = false;
}
-
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
- }
+ }
}
break;
}
@@ -2163,6 +2446,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
start->flags |= EntityFlag_Used;
add_dependency_to_set(c, start);
}
+
+#undef FORCE_ADD_RUNTIME_ENTITIES
}
bool is_entity_a_dependency(Entity *e) {
@@ -2411,6 +2696,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) {
return procs;
}
+Array proc_group_entities_cloned(CheckerContext *c, Operand o) {
+ auto entities = proc_group_entities(c, o);
+ if (entities.count == 0) {
+ return {};
+ }
+ return array_clone(permanent_allocator(), entities);
+}
+
+
void init_core_type_info(Checker *c) {
@@ -2570,11 +2864,26 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
return ev;
}
+Type *check_decl_attribute_type(CheckerContext *c, Ast *value) {
+ if (value != nullptr) {
+ return check_type(c, value);
+ }
+ return nullptr;
+}
+
+
+#define ATTRIBUTE_USER_TAG_NAME "tag"
+
DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
- if (name == "default_calling_convention") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "default_calling_convention") {
if (ev.kind == ExactValue_String) {
auto cc = string_to_calling_convention(ev.value_string);
if (cc == ProcCC_Invalid) {
@@ -2622,7 +2931,13 @@ DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
}
DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
- if (name == "test") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "test") {
if (value != nullptr) {
error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
}
@@ -2856,6 +3171,58 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "objc_name") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_name = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_is_class_method") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_class_method = ev.value_bool;
+ } else {
+ error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_type") {
+ if (value == nullptr) {
+ error(elem, "Expected a type for '%.*s'", LIT(name));
+ } else {
+ Type *objc_type = check_type(c, value);
+ if (objc_type != nullptr) {
+ if (!has_type_got_objc_class_attribute(objc_type)) {
+ gbString t = type_to_string(objc_type);
+ error(value, "'%.*s' expected a named type with the attribute @(obj_class=), got type %s", LIT(name), t);
+ gb_string_free(t);
+ } else {
+ ac->objc_type = objc_type;
+ }
+ }
+ }
+ return true;
+ } else if (name == "require_target_feature") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ ac->require_target_feature = ev.value_string;
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "enable_target_feature") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ ac->enable_target_feature = ev.value_string;
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
}
return false;
}
@@ -2863,7 +3230,12 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
- if (name == "static") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "static") {
if (value != nullptr) {
error(elem, "'static' does not have any parameters");
}
@@ -2978,7 +3350,13 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
}
DECL_ATTRIBUTE_PROC(const_decl_attribute) {
- if (name == "private") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
}
@@ -2986,9 +3364,23 @@ DECL_ATTRIBUTE_PROC(const_decl_attribute) {
}
DECL_ATTRIBUTE_PROC(type_decl_attribute) {
- if (name == "private") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "objc_class") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String || ev.value_string == "") {
+ error(elem, "Expected a non-empty string value for '%.*s'", LIT(name));
+ } else {
+ ac->objc_class = ev.value_string;
+ }
+ return true;
}
return false;
}
@@ -3302,6 +3694,16 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
}
+ if (entity_visibility_kind == EntityVisiblity_Public &&
+ (c->scope->flags&ScopeFlag_File) &&
+ c->scope->file) {
+ if (c->scope->file->flags & AstFile_IsPrivateFile) {
+ entity_visibility_kind = EntityVisiblity_PrivateToFile;
+ } else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+ entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ }
+ }
+
if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
error(decl, "Attribute 'private' is not allowed on a non file scope entity");
}
@@ -3390,9 +3792,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (is_ast_type(init)) {
e = alloc_entity_type_name(d->scope, token, nullptr);
- // if (vd->type != nullptr) {
- // error(name, "A type declaration cannot have an type parameter");
- // }
} else if (init->kind == Ast_ProcLit) {
if (c->scope->flags&ScopeFlag_Type) {
error(name, "Procedure declarations are not allowed within a struct");
@@ -3495,6 +3894,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
check_collect_entities(&c, block->stmts);
}
+bool correct_single_type_alias(CheckerContext *c, Entity *e) {
+ if (e->kind == Entity_Constant) {
+ DeclInfo *d = e->decl_info;
+ if (d != nullptr && d->init_expr != nullptr) {
+ Ast *init = d->init_expr;
+ Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
+ if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
+ e->kind = Entity_TypeName;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = n-1; i >= 0; i--) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = 0; i < n; i++) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+
+
+void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
+ // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
+ // of type aliases being "confused" as constants
+ //
+ // A :: C
+ // B :: A
+ // C :: struct {b: ^B}
+ //
+ // See @TypeAliasingProblem for more information
+ for (;;) {
+ bool corrections = false;
+ corrections |= correct_type_alias_in_scope_backwards(c, s);
+ corrections |= correct_type_alias_in_scope_forwards(c, s);
+ if (!corrections) {
+ return;
+ }
+ }
+}
+
+
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
void check_collect_entities(CheckerContext *c, Slice const &nodes) {
AstFile *curr_file = nullptr;
@@ -3566,6 +4018,7 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) {
}
}
+ // correct_type_aliases(c);
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
@@ -3613,11 +4066,6 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) {
error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
return;
}
- } else if (pkg->kind == Package_Runtime) {
- if (e->token.string == "main") {
- error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
- return;
- }
}
check_entity_decl(ctx, e, d, nullptr);
@@ -3712,7 +4160,7 @@ String path_to_entity_name(String name, String fullpath, bool strip_extension=tr
#if 1
void add_import_dependency_node(Checker *c, Ast *decl, PtrMap *M) {
- AstPackage *parent_pkg = decl->file->pkg;
+ AstPackage *parent_pkg = decl->file()->pkg;
switch (decl->kind) {
case_ast_node(id, ImportDecl, decl);
@@ -3777,7 +4225,7 @@ void add_import_dependency_node(Checker *c, Ast *decl, PtrMap generate_import_dependency_graph(Checker *c) {
- PtrMap M = {};
+ PtrMap M = {};
map_init(&M, heap_allocator(), 2*c->parser->packages.count);
defer (map_destroy(&M));
@@ -3924,25 +4372,21 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
}
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
+ if (is_blank_ident(import_name)) {
+ force_use = true;
+ }
// NOTE(bill, 2019-05-19): If the directory path is not a valid entity name, force the user to assign a custom one
// if (import_name.len == 0 || import_name == "_") {
// import_name = scope->pkg->name;
// }
- if (import_name.len == 0 || is_blank_ident(import_name)) {
- if (id->is_using) {
- // TODO(bill): Should this be a warning?
- } else {
- if (id->import_name.string == "") {
- String invalid_name = id->fullpath;
- invalid_name = get_invalid_import_name(invalid_name);
+ if (import_name.len == 0) {
+ String invalid_name = id->fullpath;
+ invalid_name = get_invalid_import_name(invalid_name);
- error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
- } else {
- error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
- }
- }
+ error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
+ error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
} else {
GB_ASSERT(id->import_name.pos.line != 0);
id->import_name.string = import_name;
@@ -3951,43 +4395,22 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
scope);
add_entity(ctx, parent_scope, nullptr, e);
- if (force_use || id->is_using) {
+ if (force_use) {
add_entity_use(ctx, nullptr, e);
}
}
- if (id->is_using) {
- if (parent_scope->flags & ScopeFlag_Global) {
- error(id->import_name, "built-in package imports cannot use using");
- return;
- }
-
- // NOTE(bill): Add imported entities to this file's scope
- for_array(elem_index, scope->elements.entries) {
- String name = scope->elements.entries[elem_index].key.string;
- Entity *e = scope->elements.entries[elem_index].value;
- if (e->scope == parent_scope) continue;
-
- if (is_entity_exported(e, true)) {
- Entity *found = scope_lookup_current(parent_scope, name);
- if (found != nullptr) {
- // NOTE(bill):
- // Date: 2019-03-17
- // The order has to be the other way around as `using` adds the entity into the that
- // file scope otherwise the error would be the wrong way around
- redeclaration_error(name, found, e);
- } else {
- add_entity_with_name(ctx, parent_scope, e->identifier, e, name);
- }
- }
- }
- }
-
scope->flags |= ScopeFlag_HasBeenImported;
}
DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
- if (name == "force" || name == "require") {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "force" || name == "require") {
if (value != nullptr) {
error(elem, "Expected no parameter for '%.*s'", LIT(name));
} else if (name == "force") {
@@ -3995,6 +4418,14 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
}
ac->require_declaration = true;
return true;
+ } else if (name == "priority_index") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_Integer) {
+ error(elem, "Expected an integer value for '%.*s'", LIT(name));
+ } else {
+ ac->foreign_import_priority_index = exact_value_to_i64(ev);
+ }
+ return true;
}
return false;
}
@@ -4051,6 +4482,17 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
add_entity_use(ctx, nullptr, e);
}
+ if (ac.foreign_import_priority_index != 0) {
+ e->LibraryName.priority_index = ac.foreign_import_priority_index;
+ }
+
+ if (has_asm_extension(fullpath)) {
+ if (build_context.metrics.arch != TargetArch_amd64 ||
+ build_context.metrics.os != TargetOs_windows) {
+ error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
+ LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
+ }
+ }
}
// Returns true if a new package is present
@@ -4202,10 +4644,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice const &decls) {
for_array(i, decls) {
if (collect_file_decl(ctx, decls[i])) {
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return true;
}
}
-
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return false;
}
@@ -4249,7 +4692,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
-
+
semaphore_post(&c->info.collect_semaphore, cast(i32)thread_count);
if (worker_count == 0) {
ThreadProcCheckerSection section_all = {};
@@ -4273,7 +4716,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
}
GB_ASSERT(remaining_count <= 0);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(proc, thread_data+i);
}
@@ -4475,6 +4918,15 @@ void check_import_entities(Checker *c) {
}
add_untyped_expressions(ctx.info, &untyped);
}
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ correct_type_aliases_in_scope(&ctx, pkg->scope);
+ }
+
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
@@ -4672,11 +5124,11 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.decl = pi->decl;
ctx.procs_to_check_queue = procs_to_check_queue;
GB_ASSERT(procs_to_check_queue != nullptr);
-
+
GB_ASSERT(pi->type->kind == Type_Proc);
TypeProc *pt = &pi->type->Proc;
String name = pi->token.string;
-
+
if (pt->is_polymorphic && !pt->is_poly_specialized) {
Token token = pi->token;
if (pi->poly_def_node != nullptr) {
@@ -4696,6 +5148,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
+ bool type_assert = (pi->tags & ProcTag_type_assert) != 0;
+ bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
+
if (bounds_check) {
ctx.state_flags |= StateFlag_bounds_check;
ctx.state_flags &= ~StateFlag_no_bounds_check;
@@ -4703,6 +5158,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.state_flags |= StateFlag_no_bounds_check;
ctx.state_flags &= ~StateFlag_bounds_check;
}
+
+ if (type_assert) {
+ ctx.state_flags |= StateFlag_type_assert;
+ ctx.state_flags &= ~StateFlag_no_type_assert;
+ } else if (no_type_assert) {
+ ctx.state_flags |= StateFlag_no_type_assert;
+ ctx.state_flags &= ~StateFlag_type_assert;
+ }
+
if (pi->body != nullptr && e != nullptr) {
GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
}
@@ -4754,7 +5218,7 @@ void check_unchecked_bodies(Checker *c) {
if (pi->body == nullptr) {
continue;
}
-
+
debugf("unchecked: %.*s\n", LIT(e->token.string));
mpmc_enqueue(&c->procs_to_check_queue, pi);
}
@@ -4773,6 +5237,10 @@ void check_unchecked_bodies(Checker *c) {
}
void check_test_procedures(Checker *c) {
+ if (build_context.test_names.entries.count == 0) {
+ return;
+ }
+
AstPackage *pkg = c->info.init_package;
Scope *s = pkg->scope;
@@ -4859,17 +5327,16 @@ void check_procedure_bodies(Checker *c) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
- worker_count = 0;
if (worker_count == 0) {
auto *this_queue = &c->procs_to_check_queue;
-
+
UntypedExprInfoMap untyped = {};
map_init(&untyped, heap_allocator());
-
+
for (ProcInfo *pi = nullptr; mpmc_dequeue(this_queue, &pi); /**/) {
consume_proc_info_queue(c, pi, this_queue, &untyped);
}
-
+
map_destroy(&untyped);
debugf("Total Procedure Bodies Checked: %td\n", total_bodies_checked.load(std::memory_order_relaxed));
@@ -4913,7 +5380,7 @@ void check_procedure_bodies(Checker *c) {
GB_ASSERT(total_queued == original_queue_count);
semaphore_post(&c->procs_to_check_semaphore, cast(i32)thread_count);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(thread_proc_body, thread_data+i);
}
@@ -4950,7 +5417,7 @@ void check_deferred_procedures(Checker *c) {
Entity *dst = src->Procedure.deferred_procedure.entity;
GB_ASSERT(dst != nullptr);
GB_ASSERT(dst->kind == Entity_Procedure);
-
+
char const *attribute = "deferred_none";
switch (dst_kind) {
case DeferredProcedure_none:
@@ -5113,12 +5580,18 @@ void check_unique_package_names(Checker *c) {
string_map_set(&pkgs, key, pkg);
continue;
}
+ auto *curr = pkg->files[0]->pkg_decl;
+ auto *prev = (*found)->files[0]->pkg_decl;
+ if (curr == prev) {
+ // NOTE(bill): A false positive was found, ignore it
+ continue;
+ }
- error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
+ error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
- error((*found)->files[0]->pkg_decl, "found at previous location");
+ error(prev, "found at previous location");
}
}
@@ -5151,7 +5624,7 @@ GB_COMPARE_PROC(init_procedures_cmp) {
cmp = 0;
return cmp;
}
-
+
if (x->pkg != y->pkg) {
isize order_x = x->pkg ? x->pkg->order : 0;
isize order_y = y->pkg ? y->pkg->order : 0;
@@ -5165,14 +5638,14 @@ GB_COMPARE_PROC(init_procedures_cmp) {
String fullpath_y = y->file ? y->file->fullpath : (String{});
String file_x = filename_from_path(fullpath_x);
String file_y = filename_from_path(fullpath_y);
-
+
cmp = string_compare(file_x, file_y);
if (cmp) {
return cmp;
}
}
-
+
cmp = u64_cmp(x->order_in_src, y->order_in_src);
if (cmp) {
return cmp;
@@ -5310,9 +5783,6 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("generate minimum dependency set");
- generate_minimum_dependency_set(c, c->info.entry_point);
-
TIME_SECTION("check test procedures");
check_test_procedures(c);
@@ -5323,6 +5793,9 @@ void check_parsed_files(Checker *c) {
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
+ TIME_SECTION("generate minimum dependency set");
+ generate_minimum_dependency_set(c, c->info.entry_point);
+
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
Scope *s = c->info.init_scope;
@@ -5352,9 +5825,21 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("sanity checks");
GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
-
+
TIME_SECTION("sort init procedures");
check_sort_init_procedures(c);
+ if (c->info.intrinsics_entry_point_usage.count > 0) {
+ TIME_SECTION("check intrinsics.__entry_point usage");
+ Ast *node = nullptr;
+ while (mpmc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
+ if (c->info.entry_point == nullptr && node != nullptr) {
+ if (node->file()->pkg->kind != Package_Runtime) {
+ warning(node, "usage of intrinsics.__entry_point will be a no-op");
+ }
+ }
+ }
+ }
+
TIME_SECTION("type check finish");
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 6511dad32..f11a00532 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -60,6 +60,7 @@ struct BuiltinProc {
ExprKind kind;
BuiltinProcPkg pkg;
bool diverging;
+ bool ignore_results; // ignores require results handling
};
@@ -118,6 +119,15 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
+ i64 foreign_import_priority_index;
+
+ String objc_class;
+ String objc_name;
+ bool objc_is_class_method;
+ Type * objc_type;
+
+ String require_target_feature; // required by the target micro-architecture
+ String enable_target_feature; // will be enabled for the procedure only
};
AttributeContext make_attribute_context(String link_prefix) {
@@ -267,6 +277,17 @@ struct UntypedExprInfo {
typedef PtrMap UntypedExprInfoMap;
typedef MPMCQueue ProcBodyQueue;
+enum ObjcMsgKind : u32 {
+ ObjcMsg_normal,
+ ObjcMsg_fpret,
+ ObjcMsg_fp2ret,
+ ObjcMsg_stret,
+};
+struct ObjcMsgData {
+ ObjcMsgKind kind;
+ Type *proc_type;
+};
+
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -338,6 +359,10 @@ struct CheckerInfo {
MPMCQueue required_global_variable_queue;
MPMCQueue required_foreign_imports_through_force_queue;
+ MPMCQueue intrinsics_entry_point_usage;
+
+ BlockingMutex objc_types_mutex;
+ PtrMap objc_msgSend_types;
};
struct CheckerContext {
@@ -410,7 +435,6 @@ gb_global AstPackage *config_pkg = nullptr;
TypeAndValue type_and_value_of_expr (Ast *expr);
Type * type_of_expr (Ast *expr);
Entity * implicit_entity_of_node(Ast *clause);
-Scope * scope_of_node (Ast *node);
DeclInfo * decl_info_of_ident (Ast *ident);
DeclInfo * decl_info_of_entity (Entity * e);
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
@@ -424,7 +448,7 @@ Entity *entity_of_node(Ast *expr);
Entity *scope_lookup_current(Scope *s, String const &name);
Entity *scope_lookup (Scope *s, String const &name);
void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
-Entity *scope_insert (Scope *s, Entity *entity);
+Entity *scope_insert (Scope *s, Entity *entity, bool use_mutex=true);
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index abd9fc6ca..05f256775 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -45,7 +45,6 @@ enum BuiltinProcId {
// "Intrinsics"
BuiltinProc_is_package_imported,
- BuiltinProc_simd_vector,
BuiltinProc_soa_struct,
BuiltinProc_alloca,
@@ -66,6 +65,7 @@ enum BuiltinProcId {
BuiltinProc_overflow_mul,
BuiltinProc_sqrt,
+ BuiltinProc_fused_mul_add,
BuiltinProc_mem_copy,
BuiltinProc_mem_copy_non_overlapping,
@@ -80,83 +80,39 @@ enum BuiltinProcId {
BuiltinProc_unaligned_store,
BuiltinProc_unaligned_load,
+ BuiltinProc_non_temporal_store,
+ BuiltinProc_non_temporal_load,
BuiltinProc_prefetch_read_instruction,
BuiltinProc_prefetch_read_data,
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
- BuiltinProc_atomic_fence,
- BuiltinProc_atomic_fence_acq,
- BuiltinProc_atomic_fence_rel,
- BuiltinProc_atomic_fence_acqrel,
-
+ BuiltinProc_atomic_type_is_lock_free,
+ BuiltinProc_atomic_thread_fence,
+ BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
- BuiltinProc_atomic_store_rel,
- BuiltinProc_atomic_store_relaxed,
- BuiltinProc_atomic_store_unordered,
-
+ BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
- BuiltinProc_atomic_load_acq,
- BuiltinProc_atomic_load_relaxed,
- BuiltinProc_atomic_load_unordered,
-
+ BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
- BuiltinProc_atomic_add_acq,
- BuiltinProc_atomic_add_rel,
- BuiltinProc_atomic_add_acqrel,
- BuiltinProc_atomic_add_relaxed,
+ BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
- BuiltinProc_atomic_sub_acq,
- BuiltinProc_atomic_sub_rel,
- BuiltinProc_atomic_sub_acqrel,
- BuiltinProc_atomic_sub_relaxed,
+ BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
- BuiltinProc_atomic_and_acq,
- BuiltinProc_atomic_and_rel,
- BuiltinProc_atomic_and_acqrel,
- BuiltinProc_atomic_and_relaxed,
+ BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
- BuiltinProc_atomic_nand_acq,
- BuiltinProc_atomic_nand_rel,
- BuiltinProc_atomic_nand_acqrel,
- BuiltinProc_atomic_nand_relaxed,
+ BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
- BuiltinProc_atomic_or_acq,
- BuiltinProc_atomic_or_rel,
- BuiltinProc_atomic_or_acqrel,
- BuiltinProc_atomic_or_relaxed,
+ BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
- BuiltinProc_atomic_xor_acq,
- BuiltinProc_atomic_xor_rel,
- BuiltinProc_atomic_xor_acqrel,
- BuiltinProc_atomic_xor_relaxed,
-
- BuiltinProc_atomic_xchg,
- BuiltinProc_atomic_xchg_acq,
- BuiltinProc_atomic_xchg_rel,
- BuiltinProc_atomic_xchg_acqrel,
- BuiltinProc_atomic_xchg_relaxed,
-
- BuiltinProc_atomic_cxchg,
- BuiltinProc_atomic_cxchg_acq,
- BuiltinProc_atomic_cxchg_rel,
- BuiltinProc_atomic_cxchg_acqrel,
- BuiltinProc_atomic_cxchg_relaxed,
- BuiltinProc_atomic_cxchg_failrelaxed,
- BuiltinProc_atomic_cxchg_failacq,
- BuiltinProc_atomic_cxchg_acq_failrelaxed,
- BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
-
- BuiltinProc_atomic_cxchgweak,
- BuiltinProc_atomic_cxchgweak_acq,
- BuiltinProc_atomic_cxchgweak_rel,
- BuiltinProc_atomic_cxchgweak_acqrel,
- BuiltinProc_atomic_cxchgweak_relaxed,
- BuiltinProc_atomic_cxchgweak_failrelaxed,
- BuiltinProc_atomic_cxchgweak_failacq,
- BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
- BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
+ BuiltinProc_atomic_xor_explicit,
+ BuiltinProc_atomic_exchange,
+ BuiltinProc_atomic_exchange_explicit,
+ BuiltinProc_atomic_compare_exchange_strong,
+ BuiltinProc_atomic_compare_exchange_strong_explicit,
+ BuiltinProc_atomic_compare_exchange_weak,
+ BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -164,10 +120,76 @@ enum BuiltinProcId {
BuiltinProc_fixed_point_div_sat,
BuiltinProc_expect,
+
+BuiltinProc__simd_begin,
+ BuiltinProc_simd_add,
+ BuiltinProc_simd_sub,
+ BuiltinProc_simd_mul,
+ BuiltinProc_simd_div,
+ BuiltinProc_simd_rem,
+ BuiltinProc_simd_shl, // Odin logic
+ BuiltinProc_simd_shr, // Odin logic
+ BuiltinProc_simd_shl_masked, // C logic
+ BuiltinProc_simd_shr_masked, // C logic
+
+ BuiltinProc_simd_add_sat, // saturation arithmetic
+ BuiltinProc_simd_sub_sat, // saturation arithmetic
+
+ BuiltinProc_simd_and,
+ BuiltinProc_simd_or,
+ BuiltinProc_simd_xor,
+ BuiltinProc_simd_and_not,
+
+ BuiltinProc_simd_neg,
+ BuiltinProc_simd_abs,
+
+ BuiltinProc_simd_min,
+ BuiltinProc_simd_max,
+ BuiltinProc_simd_clamp,
+
+ BuiltinProc_simd_lanes_eq,
+ BuiltinProc_simd_lanes_ne,
+ BuiltinProc_simd_lanes_lt,
+ BuiltinProc_simd_lanes_le,
+ BuiltinProc_simd_lanes_gt,
+ BuiltinProc_simd_lanes_ge,
+
+ BuiltinProc_simd_extract,
+ BuiltinProc_simd_replace,
+
+ BuiltinProc_simd_reduce_add_ordered,
+ BuiltinProc_simd_reduce_mul_ordered,
+ BuiltinProc_simd_reduce_min,
+ BuiltinProc_simd_reduce_max,
+ BuiltinProc_simd_reduce_and,
+ BuiltinProc_simd_reduce_or,
+ BuiltinProc_simd_reduce_xor,
+
+ BuiltinProc_simd_shuffle,
+ BuiltinProc_simd_select,
+
+ BuiltinProc_simd_ceil,
+ BuiltinProc_simd_floor,
+ BuiltinProc_simd_trunc,
+ BuiltinProc_simd_nearest,
+
+ BuiltinProc_simd_to_bits,
+
+ BuiltinProc_simd_lanes_reverse,
+ BuiltinProc_simd_lanes_rotate_left,
+ BuiltinProc_simd_lanes_rotate_right,
+
+
+ // Platform specific SIMD intrinsics
+ BuiltinProc_simd_x86__MM_SHUFFLE,
+BuiltinProc__simd_end,
// Platform specific intrinsics
BuiltinProc_syscall,
+ BuiltinProc_x86_cpuid,
+ BuiltinProc_x86_xgetbv,
+
// Constant type tests
BuiltinProc__type_begin,
@@ -204,6 +226,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_named,
BuiltinProc_type_is_pointer,
+ BuiltinProc_type_is_multi_pointer,
BuiltinProc_type_is_array,
BuiltinProc_type_is_enumerated_array,
BuiltinProc_type_is_slice,
@@ -213,8 +236,6 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_union,
BuiltinProc_type_is_enum,
BuiltinProc_type_is_proc,
- BuiltinProc_type_is_bit_field,
- BuiltinProc_type_is_bit_field_value,
BuiltinProc_type_is_bit_set,
BuiltinProc_type_is_simd_vector,
BuiltinProc_type_is_matrix,
@@ -227,6 +248,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_field,
+ BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
@@ -243,6 +265,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_polymorphic_record_parameter_count,
BuiltinProc_type_polymorphic_record_parameter_value,
+ BuiltinProc_type_is_subtype_of,
+
BuiltinProc_type_field_index_of,
BuiltinProc_type_equal_proc,
@@ -250,6 +274,21 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
+ BuiltinProc___entry_point,
+
+ BuiltinProc_objc_send,
+ BuiltinProc_objc_find_selector,
+ BuiltinProc_objc_find_class,
+ BuiltinProc_objc_register_selector,
+ BuiltinProc_objc_register_class,
+
+ BuiltinProc_constant_utf16_cstring,
+
+ BuiltinProc_wasm_memory_grow,
+ BuiltinProc_wasm_memory_size,
+ BuiltinProc_wasm_memory_atomic_wait32,
+ BuiltinProc_wasm_memory_atomic_notify32,
+
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -297,7 +336,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
// "Intrinsics"
{STR_LIT("is_package_imported"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("simd_vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("soa_struct"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type
{STR_LIT("alloca"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -319,6 +357,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("fused_mul_add"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -333,84 +372,39 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("unaligned_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("unaligned_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("non_temporal_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("non_temporal_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_read_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+ {STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -418,8 +412,74 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("fixed_point_div_sat"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("expect"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_div"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_rem"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_shl"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_and_not"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_neg"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_abs"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_min"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_max"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_clamp"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_lanes_eq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_ne"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_lt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_le"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_gt"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_ge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_extract"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_replace"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_reduce_add_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_mul_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_min"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_max"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_and"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_or"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_reduce_xor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_ceil") , 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_nearest"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_to_bits"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_lanes_reverse"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_rotate_left"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_lanes_rotate_right"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+
+ {STR_LIT("syscall"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("x86_cpuid"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("x86_xgetbv"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -455,6 +515,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_multi_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -464,8 +525,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -477,6 +536,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -493,10 +553,29 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+
+
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+
+ {STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+
+ {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_atomic_wait32"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_atomic_notify32"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
diff --git a/src/common.cpp b/src/common.cpp
index cca478421..77caddfe8 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -47,6 +47,13 @@ void debugf(char const *fmt, ...);
#include "range_cache.cpp"
+bool is_power_of_two(i64 x) {
+ if (x <= 0) {
+ return false;
+ }
+ return !(x & (x-1));
+}
+
int isize_cmp(isize x, isize y) {
if (x < y) {
return -1;
@@ -83,9 +90,20 @@ int i32_cmp(i32 x, i32 y) {
u32 fnv32a(void const *data, isize len) {
u8 const *bytes = cast(u8 const *)data;
u32 h = 0x811c9dc5;
- for (isize i = 0; i < len; i++) {
- u32 b = cast(u32)bytes[i];
- h = (h ^ b) * 0x01000193;
+
+ for (; len >= 8; len -= 8, bytes += 8) {
+ h = (h ^ bytes[0]) * 0x01000193;
+ h = (h ^ bytes[1]) * 0x01000193;
+ h = (h ^ bytes[2]) * 0x01000193;
+ h = (h ^ bytes[3]) * 0x01000193;
+ h = (h ^ bytes[4]) * 0x01000193;
+ h = (h ^ bytes[5]) * 0x01000193;
+ h = (h ^ bytes[6]) * 0x01000193;
+ h = (h ^ bytes[7]) * 0x01000193;
+ }
+
+ while (len--) {
+ h = (h ^ *bytes++) * 0x01000193;
}
return h;
}
@@ -93,20 +111,48 @@ u32 fnv32a(void const *data, isize len) {
u64 fnv64a(void const *data, isize len) {
u8 const *bytes = cast(u8 const *)data;
u64 h = 0xcbf29ce484222325ull;
- for (isize i = 0; i < len; i++) {
- u64 b = cast(u64)bytes[i];
- h = (h ^ b) * 0x100000001b3ull;
+
+ for (; len >= 8; len -= 8, bytes += 8) {
+ h = (h ^ bytes[0]) * 0x100000001b3ull;
+ h = (h ^ bytes[1]) * 0x100000001b3ull;
+ h = (h ^ bytes[2]) * 0x100000001b3ull;
+ h = (h ^ bytes[3]) * 0x100000001b3ull;
+ h = (h ^ bytes[4]) * 0x100000001b3ull;
+ h = (h ^ bytes[5]) * 0x100000001b3ull;
+ h = (h ^ bytes[6]) * 0x100000001b3ull;
+ h = (h ^ bytes[7]) * 0x100000001b3ull;
+ }
+
+ while (len--) {
+ h = (h ^ *bytes++) * 0x100000001b3ull;
}
return h;
}
u64 u64_digit_value(Rune r) {
- if ('0' <= r && r <= '9') {
- return r - '0';
- } else if ('a' <= r && r <= 'f') {
- return r - 'a' + 10;
- } else if ('A' <= r && r <= 'F') {
- return r - 'A' + 10;
+ switch (r) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ case 'a': return 10;
+ case 'b': return 11;
+ case 'c': return 12;
+ case 'd': return 13;
+ case 'e': return 14;
+ case 'f': return 15;
+ case 'A': return 10;
+ case 'B': return 11;
+ case 'C': return 12;
+ case 'D': return 13;
+ case 'E': return 14;
+ case 'F': return 15;
}
return 16; // NOTE(bill): Larger than highest possible
}
@@ -636,262 +682,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
#endif
-
-#if defined(GB_SYSTEM_WINDOWS)
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return res;
-}
-
-
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-ReadDirectoryError read_directory(String path, Array *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- i64 size = cast(i64)file_data.nFileSizeLow;
- size |= (cast(i64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
-
-#include
-
-ReadDirectoryError read_directory(String path, Array *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#else
-#error Implement read_directory
-#endif
-
-
+#include "path.cpp"
struct LoadedFile {
void *handle;
@@ -982,7 +773,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
#endif
}
- gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
+ gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
if (fc.size > I32_MAX) {
err = LoadedFile_FileTooLarge;
diff --git a/src/common_memory.cpp b/src/common_memory.cpp
index 2d7a7a246..953462077 100644
--- a/src/common_memory.cpp
+++ b/src/common_memory.cpp
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
};
+gb_global std::atomic global_platform_memory_total_usage;
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+ global_platform_memory_total_usage -= block->total_size;
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
isize size = block->total_size;
+ global_platform_memory_total_usage -= size;
munmap(block, size);
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -325,18 +340,32 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
- case gbAllocation_Alloc: {
- isize aligned_size = align_formula_isize(size, alignment);
- // TODO(bill): Make sure this is aligned correctly
- ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
- } break;
- case gbAllocation_Free:
- HeapFree(GetProcessHeap(), 0, old_memory);
+ case gbAllocation_Alloc:
+ if (size == 0) {
+ return NULL;
+ } else {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
+ break;
+ case gbAllocation_Free:
+ if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ }
+ break;
+ case gbAllocation_Resize:
+ if (old_memory != nullptr && size > 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
+ } else if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ } else if (size != 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
break;
- case gbAllocation_Resize: {
- isize aligned_size = align_formula_isize(size, alignment);
- ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
- } break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
diff --git a/src/docs.cpp b/src/docs.cpp
index 8d65cb83a..3ea3cce1b 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -67,6 +67,14 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) {
#include "docs_format.cpp"
#include "docs_writer.cpp"
+void print_doc_line(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+ gb_printf("\n");
+}
+
void print_doc_line(i32 indent, char const *fmt, ...) {
while (indent --> 0) {
gb_printf("\t");
@@ -86,6 +94,13 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
gb_printf_va(fmt, va);
va_end(va);
}
+void print_doc_line_no_newline(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+}
+
bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
if (g == nullptr) {
@@ -106,8 +121,9 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -131,7 +147,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
if (slash_slash) {
- print_doc_line(indent, "%.*s", LIT(comment));
+ print_doc_line(indent, comment);
count += 1;
} else {
isize pos = 0;
@@ -143,7 +159,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -159,7 +175,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
line = substring(line, 2, line.len);
}
- print_doc_line(indent, "%.*s", LIT(line));
+ print_doc_line(indent, line);
count += 1;
}
}
@@ -263,7 +279,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
- print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
+ print_doc_line_no_newline(2, e->token.string);
if (type_expr != nullptr) {
gbString t = expr_to_string(type_expr);
gb_printf(": %s ", t);
@@ -298,7 +314,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
String filename = remove_directory_from_path(f->fullpath);
- print_doc_line(2, "%.*s", LIT(filename));
+ print_doc_line(2, filename);
}
}
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 4cdb19a68..ee32d0e05 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
#define OdinDocVersionType_Major 0
#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 0
+#define OdinDocVersionType_Patch 4
struct OdinDocHeaderBase {
u8 magic[8];
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
+ OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {
@@ -137,6 +138,7 @@ struct OdinDocType {
OdinDocArray entities;
OdinDocTypeIndex polmorphic_params;
OdinDocArray where_clauses;
+ OdinDocArray tags; // struct field tags
};
struct OdinDocAttribute {
@@ -153,6 +155,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
+ OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -169,20 +172,27 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Type_Alias = 1ull<<20,
+ OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30,
+ OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
+
OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
OdinDocEntityFlag_Var_Static = 1ull<<41,
+
+ OdinDocEntityFlag_Private = 1ull<<50,
};
struct OdinDocEntity {
OdinDocEntityKind kind;
- u32 flags;
+ u32 reserved;
+ u64 flags;
OdinDocPosition pos;
OdinDocString name;
OdinDocTypeIndex type;
OdinDocString init_string;
u32 reserved_for_init;
- OdinDocString comment;
- OdinDocString docs;
+ OdinDocString comment; // line comment
+ OdinDocString docs; // preceding comment
+ i32 field_group_index;
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray attributes;
@@ -196,15 +206,21 @@ enum OdinDocPkgFlags : u32 {
OdinDocPkgFlag_Init = 1<<2,
};
+struct OdinDocScopeEntry {
+ OdinDocString name;
+ OdinDocEntityIndex entity;
+};
+
struct OdinDocPkg {
OdinDocString fullpath;
OdinDocString name;
u32 flags;
OdinDocString docs;
- OdinDocArray files;
- OdinDocArray entities;
+ OdinDocArray files;
+ OdinDocArray entries;
};
+
struct OdinDocHeader {
OdinDocHeaderBase base;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index e7254e16b..2f531a45c 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -257,7 +257,7 @@ OdinDocArray odin_write_item_as_slice(OdinDocWriter *w, T data) {
OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
OdinDocFileIndex file_index = 0;
if (pos.file_id != 0) {
- AstFile *file = get_ast_file_from_id(pos.file_id);
+ AstFile *file = global_files[pos.file_id];
if (file != nullptr) {
OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file);
GB_ASSERT(file_index_found != nullptr);
@@ -292,8 +292,9 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -330,7 +331,7 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -482,7 +483,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
for_array(i, w->type_cache.entries) {
// NOTE(bill): THIS IS SLOW
Type *other = w->type_cache.entries[i].key;
- if (are_types_identical(type, other)) {
+ if (are_types_identical_unique_tuples(type, other)) {
OdinDocTypeIndex index = w->type_cache.entries[i].value;
map_set(&w->type_cache, type, index);
return index;
@@ -511,10 +512,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
break;
case Type_Generic:
- doc_type.kind = OdinDocType_Generic;
- doc_type.name = odin_doc_write_string(w, type->Generic.name);
- if (type->Generic.specialized) {
- doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ {
+ String name = type->Generic.name;
+ if (type->Generic.entity) {
+ name = type->Generic.entity->token.string;
+ }
+ doc_type.kind = OdinDocType_Generic;
+ doc_type.name = odin_doc_write_string(w, name);
+ if (type->Generic.specialized) {
+ doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ }
}
break;
case Type_Pointer:
@@ -598,14 +605,24 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
}
+
+ auto tags = array_make(heap_allocator(), type->Struct.fields.count);
+ defer (array_free(&tags));
+
+ for_array(i, type->Struct.fields) {
+ tags[i] = odin_doc_write_string(w, type->Struct.tags[i]);
+ }
+
+ doc_type.tags = odin_write_slice(w, tags.data, tags.count);
}
break;
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
- if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
- if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
-
+ switch (type->Union.kind) {
+ case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
+ case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
+ }
{
auto variants = array_make(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));
@@ -667,40 +684,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
types[1] = odin_doc_type(w, type->Proc.results);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
- String calling_convention = {};
- switch (type->Proc.calling_convention) {
- case ProcCC_Invalid:
- // no need
- break;
- case ProcCC_Odin:
- if (default_calling_convention() != ProcCC_Odin) {
- calling_convention = str_lit("odin");
- }
- break;
- case ProcCC_Contextless:
- if (default_calling_convention() != ProcCC_Contextless) {
- calling_convention = str_lit("contextless");
- }
- break;
- case ProcCC_CDecl:
- calling_convention = str_lit("cdecl");
- break;
- case ProcCC_StdCall:
- calling_convention = str_lit("stdcall");
- break;
- case ProcCC_FastCall:
- calling_convention = str_lit("fastcall");
- break;
- case ProcCC_None:
- calling_convention = str_lit("none");
- break;
- case ProcCC_Naked:
- calling_convention = str_lit("naked");
- break;
- case ProcCC_InlineAsm:
- calling_convention = str_lit("inline-assembly");
- break;
- }
+ String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
}
break;
@@ -795,11 +779,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
+ if (e->kind == Entity_Variable) {
+ if (!comment) { comment = e->Variable.comment; }
+ if (!docs) { docs = e->Variable.docs; }
+ } else if (e->kind == Entity_Constant) {
+ if (!comment) { comment = e->Constant.comment; }
+ if (!docs) { docs = e->Constant.docs; }
+ }
+ String name = e->token.string;
String link_name = {};
+ TokenPos pos = e->token.pos;
OdinDocEntityKind kind = OdinDocEntity_Invalid;
- u32 flags = 0;
+ u64 flags = 0;
+ i32 field_group_index = -1;
switch (e->kind) {
case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
@@ -810,6 +804,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
+ case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
}
switch (e->kind) {
@@ -826,12 +821,36 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
}
if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
link_name = e->Variable.link_name;
+ if (init_expr == nullptr) {
+ init_expr = e->Variable.init_expr;
+ }
+ field_group_index = e->Variable.field_group_index;
+ break;
+ case Entity_Constant:
+ field_group_index = e->Constant.field_group_index;
break;
case Entity_Procedure:
if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
link_name = e->Procedure.link_name;
break;
+ case Entity_Builtin:
+ {
+ auto bp = builtin_procs[e->Builtin.id];
+ pos = {};
+ name = bp.name;
+ switch (bp.pkg) {
+ case BuiltinProcPkg_builtin:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
+ break;
+ case BuiltinProcPkg_intrinsics:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
+ break;
+ default:
+ GB_PANIC("Unhandled BuiltinProcPkg");
+ }
+ }
+ break;
}
if (e->flags & EntityFlag_Param) {
@@ -842,6 +861,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
}
+ if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
+ flags |= OdinDocEntityFlag_Private;
+ }
OdinDocString init_string = {};
if (init_expr) {
@@ -856,20 +878,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
}
} else if (e->kind == Entity_Variable) {
- if (e->Variable.param_expr) {
- init_string = odin_doc_expr_string(w, e->Variable.param_expr);
+ if (e->Variable.param_value.original_ast_expr) {
+ init_string = odin_doc_expr_string(w, e->Variable.param_value.original_ast_expr);
}
}
}
doc_entity.kind = kind;
doc_entity.flags = flags;
- doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
- doc_entity.name = odin_doc_write_string(w, e->token.string);
+ doc_entity.pos = odin_doc_token_pos_cast(w, pos);
+ doc_entity.name = odin_doc_write_string(w, name);
doc_entity.type = 0; // Set later
doc_entity.init_string = init_string;
doc_entity.comment = odin_doc_comment_group_string(w, comment);
doc_entity.docs = odin_doc_comment_group_string(w, docs);
+ doc_entity.field_group_index = field_group_index;
doc_entity.foreign_library = 0; // Set later
doc_entity.link_name = odin_doc_write_string(w, link_name);
if (e->decl_info != nullptr) {
@@ -941,7 +964,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
-OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
+OdinDocArray odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
if (pkg->scope == nullptr) {
return {};
}
@@ -949,14 +972,14 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
return {};
}
- auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count);
- defer (array_free(&entities));
+ auto entries = array_make(heap_allocator(), 0, w->entity_cache.entries.count);
+ defer (array_free(&entries));
for_array(i, pkg->scope->elements.entries) {
+ String name = pkg->scope->elements.entries[i].key.string;
Entity *e = pkg->scope->elements.entries[i].value;
switch (e->kind) {
case Entity_Invalid:
- case Entity_Builtin:
case Entity_Nil:
case Entity_Label:
continue;
@@ -967,34 +990,27 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
case Entity_ProcGroup:
case Entity_ImportName:
case Entity_LibraryName:
+ case Entity_Builtin:
// Fine
break;
}
- array_add(&entities, e);
- }
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
- auto entity_indices = array_make(heap_allocator(), 0, w->entity_cache.entries.count);
- defer (array_free(&entity_indices));
-
- for_array(i, entities) {
- Entity *e = entities[i];
if (e->pkg != pkg) {
continue;
}
- if (!is_entity_exported(e)) {
+ if (!is_entity_exported(e, true)) {
continue;
}
if (e->token.string.len == 0) {
continue;
}
- OdinDocEntityIndex doc_entity_index = 0;
- doc_entity_index = odin_doc_add_entity(w, e);
- array_add(&entity_indices, doc_entity_index);
+ OdinDocScopeEntry entry = {};
+ entry.name = odin_doc_write_string(w, name);
+ entry.entity = odin_doc_add_entity(w, e);
+ array_add(&entries, entry);
}
- return odin_write_slice(w, entity_indices.data, entity_indices.count);
+ return odin_write_slice(w, entries.data, entries.count);
}
@@ -1062,7 +1078,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
}
doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
- doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
+ doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
if (dst) {
*dst = doc_pkg;
diff --git a/src/entity.cpp b/src/entity.cpp
index ef4f7e0fa..3d3712328 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -45,9 +45,9 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
- EntityFlag_Sret = 1ull<<12,
- EntityFlag_ByVal = 1ull<<13,
- EntityFlag_BitFieldValue = 1ull<<14,
+
+
+
EntityFlag_PolyConst = 1ull<<15,
EntityFlag_NotExported = 1ull<<16,
EntityFlag_ConstInput = 1ull<<17,
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
EntityFlag_Test = 1ull<<30,
EntityFlag_Init = 1ull<<31,
+ EntityFlag_Subtype = 1ull<<32,
EntityFlag_CustomLinkName = 1ull<<40,
EntityFlag_CustomLinkage_Internal = 1ull<<41,
@@ -82,10 +83,15 @@ enum EntityFlag : u64 {
EntityFlag_CustomLinkage_LinkOnce = 1ull<<44,
EntityFlag_Require = 1ull<<50,
+ EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
EntityFlag_Overridden = 1ull<<63,
};
+enum : u64 {
+ EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
+};
+
enum EntityState : u32 {
EntityState_Unresolved = 0,
EntityState_InProgress = 1,
@@ -110,6 +116,16 @@ struct ParameterValue {
};
};
+bool has_parameter_value(ParameterValue const ¶m_value) {
+ if (param_value.kind != ParameterValue_Invalid) {
+ return true;
+ }
+ if (param_value.original_ast_expr != nullptr) {
+ return true;
+ }
+ return false;
+}
+
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
@@ -122,6 +138,28 @@ enum ProcedureOptimizationMode : u32 {
ProcedureOptimizationMode_Speed,
};
+
+BlockingMutex global_type_name_objc_metadata_mutex;
+
+struct TypeNameObjCMetadataEntry {
+ String name;
+ Entity *entity;
+};
+struct TypeNameObjCMetadata {
+ BlockingMutex *mutex;
+ Array type_entries;
+ Array value_entries;
+};
+
+TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
+ TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
+ md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
+ mutex_init(md->mutex);
+ array_init(&md->type_entries, heap_allocator());
+ array_init(&md->value_entries, heap_allocator());
+ return md;
+}
+
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -160,13 +198,16 @@ struct Entity {
ExactValue value;
ParameterValue param_value;
u32 flags;
+ i32 field_group_index;
+ CommentGroup *docs;
+ CommentGroup *comment;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
+ i32 field_group_index;
ParameterValue param_value;
- Ast * param_expr;
String thread_local_model;
Entity * foreign_library;
@@ -174,6 +215,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_section;
+ CommentGroup *docs;
+ CommentGroup *comment;
bool is_foreign;
bool is_export;
} Variable;
@@ -181,6 +224,8 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
+ String objc_class_name;
+ TypeNameObjCMetadata *objc_metadata;
} TypeName;
struct {
u64 tags;
@@ -189,10 +234,12 @@ struct Entity {
String link_name;
String link_prefix;
DeferredProcedure deferred_procedure;
- bool is_foreign;
- bool is_export;
- bool generated_from_polymorphic;
ProcedureOptimizationMode optimization_mode;
+ bool is_foreign : 1;
+ bool is_export : 1;
+ bool generated_from_polymorphic : 1;
+ bool target_feature_disabled : 1;
+ String target_feature;
} Procedure;
struct {
Array entities;
@@ -208,6 +255,7 @@ struct Entity {
struct {
Slice paths;
String name;
+ i64 priority_index;
} LibraryName;
i32 Nil;
struct {
@@ -240,7 +288,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
- if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
+ if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
return false;
}
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644
index 000000000..faf4d11fb
--- /dev/null
+++ b/src/error.cpp
@@ -0,0 +1,416 @@
+struct ErrorCollector {
+ TokenPos prev;
+ std::atomic count;
+ std::atomic warning_count;
+ std::atomic in_block;
+ BlockingMutex mutex;
+ BlockingMutex error_out_mutex;
+ BlockingMutex string_mutex;
+ RecursiveMutex block_mutex;
+
+ Array error_buffer;
+ Array errors;
+};
+
+gb_global ErrorCollector global_error_collector;
+
+#define MAX_ERROR_COLLECTOR_COUNT (36)
+
+
+bool any_errors(void) {
+ return global_error_collector.count.load() != 0;
+}
+
+void init_global_error_collector(void) {
+ mutex_init(&global_error_collector.mutex);
+ mutex_init(&global_error_collector.block_mutex);
+ mutex_init(&global_error_collector.error_out_mutex);
+ mutex_init(&global_error_collector.string_mutex);
+ array_init(&global_error_collector.errors, heap_allocator());
+ array_init(&global_error_collector.error_buffer, heap_allocator());
+ array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
+ array_init(&global_files, heap_allocator(), 1, 4096);
+}
+
+
+// temporary
+// defined in build_settings.cpp
+char *token_pos_to_string(TokenPos const &pos);
+
+bool set_file_path_string(i32 index, String const &path) {
+ bool ok = false;
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ if (index >= global_file_path_strings.count) {
+ array_resize(&global_file_path_strings, index+1);
+ }
+ String prev = global_file_path_strings[index];
+ if (prev.len == 0) {
+ global_file_path_strings[index] = path;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
+ bool ok = false;
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ if (index >= global_files.count) {
+ array_resize(&global_files, index+1);
+ }
+ AstFile *prev = global_files[index];
+ if (prev == nullptr) {
+ global_files[index] = file;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+String get_file_path_string(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ String path = {};
+ if (index < global_file_path_strings.count) {
+ path = global_file_path_strings[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return path;
+}
+
+AstFile *thread_safe_get_ast_file_from_id(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ AstFile *file = nullptr;
+ if (index < global_files.count) {
+ file = global_files[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return file;
+}
+
+
+
+void begin_error_block(void) {
+ mutex_lock(&global_error_collector.block_mutex);
+ global_error_collector.in_block.store(true);
+}
+
+void end_error_block(void) {
+ if (global_error_collector.error_buffer.count > 0) {
+ isize n = global_error_collector.error_buffer.count;
+ u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
+ gb_memmove(text, global_error_collector.error_buffer.data, n);
+ text[n] = 0;
+ String s = {text, n};
+ array_add(&global_error_collector.errors, s);
+ global_error_collector.error_buffer.count = 0;
+ }
+
+ global_error_collector.in_block.store(false);
+ mutex_unlock(&global_error_collector.block_mutex);
+}
+
+#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
+
+
+#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
+typedef ERROR_OUT_PROC(ErrorOutProc);
+
+ERROR_OUT_PROC(default_error_out_va) {
+ gbFile *f = gb_file_get_standard(gbFileStandard_Error);
+
+ char buf[4096] = {};
+ isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+ isize n = len-1;
+ if (global_error_collector.in_block) {
+ isize cap = global_error_collector.error_buffer.count + n;
+ array_reserve(&global_error_collector.error_buffer, cap);
+ u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count;
+ gb_memmove(data, buf, n);
+ global_error_collector.error_buffer.count += n;
+ } else {
+ mutex_lock(&global_error_collector.error_out_mutex);
+ {
+ u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
+ gb_memmove(text, buf, n);
+ text[n] = 0;
+ array_add(&global_error_collector.errors, make_string(text, n));
+ }
+ mutex_unlock(&global_error_collector.error_out_mutex);
+
+ }
+ gb_file_write(f, buf, n);
+}
+
+
+ErrorOutProc *error_out_va = default_error_out_va;
+
+// NOTE: defined in build_settings.cpp
+bool global_warnings_as_errors(void);
+bool global_ignore_warnings(void);
+bool show_error_line(void);
+gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
+
+void error_out(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_out_va(fmt, va);
+ va_end(va);
+}
+
+
+bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+ if (!show_error_line()) {
+ return false;
+ }
+
+ i32 offset = 0;
+ gbString the_line = get_file_line_as_string(pos, &offset);
+ defer (gb_string_free(the_line));
+
+ if (the_line != nullptr) {
+ String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
+
+ // TODO(bill): This assumes ASCII
+
+ enum {
+ MAX_LINE_LENGTH = 76,
+ MAX_TAB_WIDTH = 8,
+ ELLIPSIS_PADDING = 8
+ };
+
+ error_out("\n\t");
+ if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
+ i32 const half_width = MAX_LINE_LENGTH/2;
+ i32 left = cast(i32)(offset);
+ i32 right = cast(i32)(line.len - offset);
+ left = gb_min(left, half_width);
+ right = gb_min(right, half_width);
+
+ line.text += offset-left;
+ line.len -= offset+right-left;
+
+ line = string_trim_whitespace(line);
+
+ offset = left + ELLIPSIS_PADDING/2;
+
+ error_out("... %.*s ...", LIT(line));
+ } else {
+ error_out("%.*s", LIT(line));
+ }
+ error_out("\n\t");
+
+ for (i32 i = 0; i < offset; i++) {
+ error_out(" ");
+ }
+ error_out("^");
+ if (end.file_id == pos.file_id) {
+ if (end.line > pos.line) {
+ for (i32 i = offset; i < line.len; i++) {
+ error_out("~");
+ }
+ } else if (end.line == pos.line && end.column > pos.column) {
+ i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
+ for (i32 i = 1; i < length-1; i++) {
+ error_out("~");
+ }
+ if (length > 1) {
+ error_out("^");
+ }
+ }
+ }
+
+ error_out("\n\n");
+ return true;
+ }
+ return false;
+}
+
+void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ global_error_collector.count.fetch_add(1);
+
+ mutex_lock(&global_error_collector.mutex);
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ error_va(pos, end, fmt, va);
+ return;
+ }
+ global_error_collector.warning_count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+void error_line_va(char const *fmt, va_list va) {
+ error_out_va(fmt, va);
+}
+
+void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+
+void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Error: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
+ }
+
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ syntax_error_va(pos, end, fmt, va);
+ return;
+ }
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.warning_count++;
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+
+void warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ Token token = {};
+ token.pos = pos;
+ error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error_line(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_line_va(fmt, va);
+ va_end(va);
+}
+
+
+void syntax_error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+
+void compiler_error(char const *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ gb_printf_err("Internal Compiler Error: %s\n",
+ gb_bprintf_va(fmt, va));
+ va_end(va);
+ GB_DEBUG_TRAP();
+ gb_exit(1);
+}
+
+
+
+
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index fd90278e5..175cb61f6 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -50,9 +50,9 @@ struct ExactValue {
union {
bool value_bool;
String value_string;
- BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
+ BigInt value_integer;
f64 value_float;
- i64 value_pointer;
+ i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
Complex128 *value_complex;
Quaternion256 *value_quaternion;
Ast * value_compound;
@@ -177,7 +177,11 @@ ExactValue exact_value_typeid(Type *type) {
ExactValue exact_value_integer_from_string(String const &string) {
ExactValue result = {ExactValue_Integer};
- big_int_from_string(&result.value_integer, string);
+ bool success;
+ big_int_from_string(&result.value_integer, string, &success);
+ if (!success) {
+ result = {ExactValue_Invalid};
+ }
return result;
}
@@ -591,6 +595,7 @@ failure:
i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
+ case ExactValue_Compound:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -607,8 +612,6 @@ i32 exact_value_order(ExactValue const &v) {
return 6;
case ExactValue_Procedure:
return 7;
- // case ExactValue_Compound:
- // return 8;
default:
GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind);
@@ -630,6 +633,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Quaternion:
+ case ExactValue_Pointer:
+ case ExactValue_Procedure:
+ case ExactValue_Typeid:
return;
case ExactValue_Integer:
@@ -671,9 +677,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return;
}
break;
-
- case ExactValue_Procedure:
- return;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
@@ -932,6 +935,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
break;
}
+ case ExactValue_Pointer: {
+ switch (op) {
+ case Token_CmpEq: return x.value_pointer == y.value_pointer;
+ case Token_NotEq: return x.value_pointer != y.value_pointer;
+ case Token_Lt: return x.value_pointer < y.value_pointer;
+ case Token_LtEq: return x.value_pointer <= y.value_pointer;
+ case Token_Gt: return x.value_pointer > y.value_pointer;
+ case Token_GtEq: return x.value_pointer >= y.value_pointer;
+ }
+ }
+
case ExactValue_Typeid:
switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
diff --git a/src/gb/gb.h b/src/gb/gb.h
index f716b0840..48d3c9aec 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -79,6 +79,10 @@ extern "C" {
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
+ #elif defined(__OpenBSD__)
+ #ifndef GB_SYSTEM_OPENBSD
+ #define GB_SYSTEM_OPENBSD 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -199,7 +203,7 @@ extern "C" {
#endif
#include // NOTE(bill): malloc on linux
#include
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include
#endif
#include
@@ -235,6 +239,12 @@ extern "C" {
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include
+ #include
+ #define lseek64 lseek
+#endif
+
#if defined(GB_SYSTEM_UNIX)
#include
#endif
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_OPENBSD)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -1663,7 +1680,7 @@ GB_DEF gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, c
GB_DEF void gb_file_free_contents(gbFileContents *fc);
-// TODO(bill): Should these have different na,es as they do not take in a gbFile * ???
+// TODO(bill): Should these have different names as they do not take in a gbFile * ???
GB_DEF b32 gb_file_exists (char const *filepath);
GB_DEF gbFileTime gb_file_last_write_time(char const *filepath);
GB_DEF b32 gb_file_copy (char const *existing_filename, char const *new_filename, b32 fail_if_exists);
@@ -3355,6 +3372,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+ thread_id = gettid();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3676,6 +3695,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
return true;
}
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
+
+#elif defined(GB_SYSTEM_OPENBSD)
+#include
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
@@ -6023,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
#if defined(GB_SYSTEM_OSX)
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
-#else
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
isize size;
int existing_fd = open(existing_filename, O_RDONLY, 0);
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
@@ -6039,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
close(new_fd);
close(existing_fd);
+ return size == stat_existing.st_size;
+#else
+ int new_flags = O_WRONLY | O_CREAT;
+ if (fail_if_exists) {
+ new_flags |= O_EXCL;
+ }
+ int existing_fd = open(existing_filename, O_RDONLY, 0);
+ int new_fd = open(new_filename, new_flags, 0666);
+
+ struct stat stat_existing;
+ if (fstat(existing_fd, &stat_existing) == -1) {
+ return 0;
+ }
+
+ size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
+ char *buf = (char *)malloc(bsize);
+ if (buf == NULL) {
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+
+ isize size = 0;
+ ssize_t nread, nwrite, offset;
+ while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
+ for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
+ if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+ size += nwrite;
+ }
+ }
+
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+
+ if (nread == -1) {
+ return 0;
+ }
return size == stat_existing.st_size;
#endif
}
@@ -6091,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
}
void gb_file_free_contents(gbFileContents *fc) {
+ if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
@@ -6186,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
#else
char *p, *result, *fullpath = NULL;
isize len;
- p = realpath(path, NULL);
- fullpath = p;
- if (p == NULL) {
- // NOTE(bill): File does not exist
- fullpath = cast(char *)path;
+ fullpath = realpath(path, NULL);
+
+ if (fullpath == NULL) {
+ // NOTE(Jeroen): Path doesn't exist.
+ if (gb_strlen(path) > 0 && path[0] == '/') {
+ // But it is an absolute path, so return as-is.
+
+ fullpath = (char *)path;
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+
+ } else {
+ // Appears to be a relative path, so construct an absolute one relative to .
+ char cwd[4096];
+ getcwd(&cwd[0], 4096);
+
+ isize path_len = gb_strlen(path);
+ isize cwd_len = gb_strlen(cwd);
+ len = cwd_len + 1 + path_len + 1;
+ result = gb_alloc_array(a, char, len);
+
+ gb_memmove(result, (void *)cwd, cwd_len);
+ result[cwd_len] = '/';
+
+ gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
+ result[len] = 0;
+
+ }
+ } else {
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+ free(fullpath);
}
-
- len = gb_strlen(fullpath);
-
- result = gb_alloc_array(a, char, len + 1);
- gb_memmove(result, fullpath, len);
- result[len] = 0;
- free(p);
-
return result;
#endif
}
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index e18dc344b..60a07e531 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV {
bool is_register(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
+ i64 sz = lb_sizeof(type);
+ if (sz == 0) {
+ return false;
+ }
switch (kind) {
case LLVMIntegerTypeKind:
case LLVMHalfTypeKind:
@@ -965,6 +969,10 @@ namespace lbAbiArm64 {
}
return false;
}
+
+ unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
+ return (member_count_ <= 4);
+ }
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
LLVMTypeRef homo_base_type = {};
@@ -975,22 +983,31 @@ namespace lbAbiArm64 {
} else if (is_register(type)) {
return non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
- return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
+ return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ } else {
+ //TODO(Platin): do i need to create stuff that can handle the diffrent return type?
+ // else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
+
+ //LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
+ return lb_arg_type_indirect(type, attr);
+ }
} else {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size <= 1) {
- cast_type = LLVMIntTypeInContext(c, 8);
+ cast_type = LLVMInt8TypeInContext(c);
} else if (size <= 2) {
- cast_type = LLVMIntTypeInContext(c, 16);
+ cast_type = LLVMInt16TypeInContext(c);
} else if (size <= 4) {
- cast_type = LLVMIntTypeInContext(c, 32);
+ cast_type = LLVMInt32TypeInContext(c);
} else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, 64);
+ cast_type = LLVMInt64TypeInContext(c);
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+ cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
}
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
@@ -999,7 +1016,7 @@ namespace lbAbiArm64 {
}
}
}
-
+
Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make(heap_allocator(), arg_count);
@@ -1039,10 +1056,18 @@ namespace lbAbiArm64 {
}
}
-namespace lbAbiWasm32 {
+namespace lbAbiWasm {
+ /*
+ NOTE(bill): All of this is custom since there is not an "official"
+ ABI definition for WASM, especially for Odin.
+ The approach taken optimizes for passing things in multiple
+ registers/arguments if possible rather than by pointer.
+ */
Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+ enum {MAX_DIRECT_STRUCT_SIZE = 32};
+
LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
@@ -1070,7 +1095,7 @@ namespace lbAbiWasm32 {
return lb_arg_type_direct(type, nullptr, nullptr, attr);
}
- bool is_struct_valid_elem_type(LLVMTypeRef type) {
+ bool is_basic_register_type(LLVMTypeRef type) {
switch (LLVMGetTypeKind(type)) {
case LLVMHalfTypeKind:
case LLVMFloatTypeKind:
@@ -1082,7 +1107,33 @@ namespace lbAbiWasm32 {
}
return false;
}
-
+
+ bool type_can_be_direct(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ i64 sz = lb_sizeof(type);
+ if (sz == 0) {
+ return false;
+ }
+ if (sz <= MAX_DIRECT_STRUCT_SIZE) {
+ if (kind == LLVMArrayTypeKind) {
+ if (is_basic_register_type(LLVMGetElementType(type))) {
+ return true;
+ }
+ } else if (kind == LLVMStructTypeKind) {
+ unsigned count = LLVMCountStructElementTypes(type);
+ for (unsigned i = 0; i < count; i++) {
+ LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i);
+ if (!is_basic_register_type(elem)) {
+ return false;
+ }
+
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind);
@@ -1091,29 +1142,9 @@ namespace lbAbiWasm32 {
if (sz == 0) {
return lb_arg_type_ignore(type);
}
- if (sz <= 16) {
- if (kind == LLVMArrayTypeKind) {
- LLVMTypeRef elem = LLVMGetElementType(type);
- if (is_struct_valid_elem_type(elem)) {
- return lb_arg_type_direct(type);
- }
- } else if (kind == LLVMStructTypeKind) {
- bool can_be_direct = true;
- unsigned count = LLVMCountStructElementTypes(type);
- for (unsigned i = 0; i < count; i++) {
- LLVMTypeRef elem = LLVMStructGetTypeAtIndex(type, i);
- if (!is_struct_valid_elem_type(elem)) {
- can_be_direct = false;
- break;
- }
-
- }
- if (can_be_direct) {
- return lb_arg_type_direct(type);
- }
- }
+ if (type_can_be_direct(type)) {
+ return lb_arg_type_direct(type);
}
-
return lb_arg_type_indirect(type, nullptr);
}
@@ -1137,6 +1168,10 @@ namespace lbAbiWasm32 {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
+ if (type_can_be_direct(return_type)) {
+ return lb_arg_type_direct(return_type);
+ }
+
i64 sz = lb_sizeof(return_type);
switch (sz) {
case 1: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 8), nullptr, nullptr);
@@ -1151,6 +1186,88 @@ namespace lbAbiWasm32 {
}
}
+namespace lbAbiArm32 {
+ Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
+ ft->ret = compute_return_type(c, return_type, return_is_defined);
+ ft->calling_convention = calling_convention;
+ return ft;
+ }
+
+ bool is_register(LLVMTypeRef type, bool is_return) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMHalfTypeKind:
+ case LLVMFloatTypeKind:
+ case LLVMDoubleTypeKind:
+ return true;
+ case LLVMIntegerTypeKind:
+ return lb_sizeof(type) <= 8;
+ case LLVMFunctionTypeKind:
+ return true;
+ case LLVMPointerTypeKind:
+ return true;
+ case LLVMVectorTypeKind:
+ return true;
+ }
+ return false;
+ }
+
+ lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+ LLVMAttributeRef attr = nullptr;
+ LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+ if (type == i1) {
+ attr = lb_create_enum_attribute(c, "zeroext");
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attr);
+ }
+
+ Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
+ auto args = array_make(heap_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef t = arg_types[i];
+ if (is_register(t, false)) {
+ args[i] = non_struct(c, t, false);
+ } else {
+ i64 sz = lb_sizeof(t);
+ i64 a = lb_alignof(t);
+ if (is_calling_convention_odin(calling_convention) && sz > 8) {
+ // Minor change to improve performance using the Odin calling conventions
+ args[i] = lb_arg_type_indirect(t, nullptr);
+ } else if (a <= 4) {
+ unsigned n = cast(unsigned)((sz + 3) / 4);
+ args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n));
+ } else {
+ unsigned n = cast(unsigned)((sz + 7) / 8);
+ args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n));
+ }
+ }
+ }
+ return args;
+ }
+
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ } else if (!is_register(return_type, true)) {
+ switch (lb_sizeof(return_type)) {
+ case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr);
+ case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr);
+ case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr);
+ }
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+ return lb_arg_type_indirect(return_type, attr);
+ }
+ return non_struct(c, return_type, true);
+ }
+};
+
LB_ABI_INFO(lb_get_abi_info) {
switch (calling_convention) {
@@ -1171,27 +1288,32 @@ LB_ABI_INFO(lb_get_abi_info) {
ft->calling_convention = calling_convention;
return ft;
}
+ case ProcCC_Win64:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ case ProcCC_SysV:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- if (build_context.metrics.os == TargetOs_windows) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ } else if (build_context.metrics.abi == TargetABI_SysV) {
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
} else {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
- case TargetArch_386:
+ case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ case TargetArch_arm32:
+ return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_wasm32:
- // TODO(bill): implement wasm32's ABI correct
- // NOTE(bill): this ABI is only an issue for WASI compatibility
- return lbAbiWasm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_wasm64:
- // TODO(bill): implement wasm64's ABI correct
- // NOTE(bill): this ABI is only an issue for WASI compatibility
- return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ return lbAbiWasm::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
GB_PANIC("Unsupported ABI");
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index e468f3032..cf7389ec1 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -29,29 +29,53 @@ void lb_add_foreign_library_path(lbModule *m, Entity *e) {
GB_ASSERT(e->kind == Entity_LibraryName);
GB_ASSERT(e->flags & EntityFlag_Used);
- for_array(i, e->LibraryName.paths) {
- String library_path = e->LibraryName.paths[i];
- if (library_path.len == 0) {
- continue;
- }
+ mutex_lock(&m->gen->foreign_mutex);
+ if (!ptr_set_update(&m->gen->foreign_libraries_set, e)) {
+ array_add(&m->gen->foreign_libraries, e);
+ }
+ mutex_unlock(&m->gen->foreign_mutex);
+}
- bool ok = true;
- for_array(path_index, m->foreign_library_paths) {
- String path = m->foreign_library_paths[path_index];
- #if defined(GB_SYSTEM_WINDOWS)
- if (str_eq_ignore_case(path, library_path)) {
- #else
- if (str_eq(path, library_path)) {
- #endif
- ok = false;
- break;
- }
- }
+GB_COMPARE_PROC(foreign_library_cmp) {
+ int cmp = 0;
+ Entity *x = *(Entity **)a;
+ Entity *y = *(Entity **)b;
+ if (x == y) {
+ return 0;
+ }
+ GB_ASSERT(x->kind == Entity_LibraryName);
+ GB_ASSERT(y->kind == Entity_LibraryName);
- if (ok) {
- array_add(&m->foreign_library_paths, library_path);
+ cmp = i64_cmp(x->LibraryName.priority_index, y->LibraryName.priority_index);
+ if (cmp) {
+ return cmp;
+ }
+
+ if (x->pkg != y->pkg) {
+ isize order_x = x->pkg ? x->pkg->order : 0;
+ isize order_y = y->pkg ? y->pkg->order : 0;
+ cmp = isize_cmp(order_x, order_y);
+ if (cmp) {
+ return cmp;
}
}
+ if (x->file != y->file) {
+ String fullpath_x = x->file ? x->file->fullpath : (String{});
+ String fullpath_y = y->file ? y->file->fullpath : (String{});
+ String file_x = filename_from_path(fullpath_x);
+ String file_y = filename_from_path(fullpath_y);
+
+ cmp = string_compare(file_x, file_y);
+ if (cmp) {
+ return cmp;
+ }
+ }
+
+ cmp = u64_cmp(x->order_in_src, y->order_in_src);
+ if (cmp) {
+ return cmp;
+ }
+ return i32_cmp(x->token.pos.offset, y->token.pos.offset);
}
void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name) {
@@ -454,7 +478,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
token.kind = Token_Ident;
token.string = name;
Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
- e->file = expr->file;
+ e->file = expr->file();
e->decl_info = pl->decl;
e->code_gen_module = m;
e->flags |= EntityFlag_ProcBodyChecked;
@@ -624,6 +648,9 @@ struct lbGlobalVariable {
};
lbProcedure *lb_create_startup_type_info(lbModule *m) {
+ if (build_context.disallow_rtti) {
+ return nullptr;
+ }
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -652,7 +679,54 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
return p;
}
-lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime
+lbProcedure *lb_create_objc_names(lbModule *main_module) {
+ if (build_context.metrics.os != TargetOs_darwin) {
+ return nullptr;
+ }
+ Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+ lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
+ p->is_startup = true;
+ return p;
+}
+
+void lb_finalize_objc_names(lbProcedure *p) {
+ if (p == nullptr) {
+ return;
+ }
+ lbModule *m = p->module;
+
+ LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
+ lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
+ LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+
+
+ auto args = array_make(permanent_allocator(), 1);
+
+ LLVMSetLinkage(p->value, LLVMInternalLinkage);
+ lb_begin_procedure_body(p);
+ for_array(i, m->objc_classes.entries) {
+ auto const &entry = m->objc_classes.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ for_array(i, m->objc_selectors.entries) {
+ auto const &entry = m->objc_selectors.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ lb_end_procedure_body(p);
+
+ lb_run_function_pass_manager(default_function_pass_manager, p);
+
+}
+
+lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -664,7 +738,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_begin_procedure_body(p);
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ if (startup_type_info) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ }
+
+ if (objc_names) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
+ }
for_array(i, global_variables) {
auto *var = &global_variables[i];
@@ -783,7 +863,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false;
- } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_386 || build_context.no_crt)) {
+ } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
name = str_lit("mainCRTStartup");
} else if (is_arch_wasm()) {
name = str_lit("_start");
@@ -873,7 +953,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
- lb_emit_call(p, entry_point, {});
+ lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
}
}
@@ -911,7 +991,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
}
String lb_filepath_ll_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
@@ -922,7 +1007,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
String lb_filepath_obj_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
}
@@ -945,6 +1035,19 @@ String lb_filepath_obj_for_module(lbModule *m) {
case TargetOs_essence:
ext = STR_LIT(".o");
break;
+
+ case TargetOs_freestanding:
+ switch (build_context.metrics.abi) {
+ default:
+ case TargetABI_Default:
+ case TargetABI_SysV:
+ ext = STR_LIT(".o");
+ break;
+ case TargetABI_Win64:
+ ext = STR_LIT(".obj");
+ break;
+ }
+ break;
}
}
}
@@ -1140,7 +1243,7 @@ void lb_generate_code(lbGenerator *gen) {
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- case TargetArch_386:
+ case TargetArch_i386:
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
@@ -1197,6 +1300,8 @@ void lb_generate_code(lbGenerator *gen) {
LLVMCodeModel code_mode = LLVMCodeModelDefault;
if (is_arch_wasm()) {
code_mode = LLVMCodeModelJITDefault;
+ } else if (build_context.metrics.os == TargetOs_freestanding) {
+ code_mode = LLVMCodeModelKernel;
}
char const *host_cpu_name = LLVMGetHostCPUName();
@@ -1219,10 +1324,18 @@ void lb_generate_code(lbGenerator *gen) {
// x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
// x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
if (ODIN_LLVM_MINIMUM_VERSION_12) {
- llvm_cpu = "x86-64-v2";
+ if (build_context.metrics.os == TargetOs_freestanding) {
+ llvm_cpu = "x86-64";
+ } else {
+ llvm_cpu = "x86-64-v2";
+ }
}
}
+ if (build_context.target_features_set.entries.count != 0) {
+ llvm_features = target_features_set_to_cstring(permanent_allocator(), false);
+ }
+
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
@@ -1238,12 +1351,36 @@ void lb_generate_code(lbGenerator *gen) {
// NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe
auto target_machines = array_make(permanent_allocator(), gen->modules.entries.count);
+ // NOTE(dweiler): Dynamic libraries require position-independent code.
+ LLVMRelocMode reloc_mode = LLVMRelocDefault;
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ reloc_mode = LLVMRelocPIC;
+ }
+
+ switch (build_context.reloc_mode) {
+ case RelocMode_Default:
+ if (build_context.metrics.os == TargetOs_openbsd) {
+ // Always use PIC for OpenBSD: it defaults to PIE
+ reloc_mode = LLVMRelocPIC;
+ }
+ break;
+ case RelocMode_Static:
+ reloc_mode = LLVMRelocStatic;
+ break;
+ case RelocMode_PIC:
+ reloc_mode = LLVMRelocPIC;
+ break;
+ case RelocMode_DynamicNoPIC:
+ reloc_mode = LLVMRelocDynamicNoPic;
+ break;
+ }
+
for_array(i, gen->modules.entries) {
target_machines[i] = LLVMCreateTargetMachine(
target, target_triple, llvm_cpu,
llvm_features,
code_gen_level,
- LLVMRelocDefault,
+ reloc_mode,
code_mode);
LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i]));
}
@@ -1278,8 +1415,8 @@ void lb_generate_code(lbGenerator *gen) {
if (Entity *entry_point = m->info->entry_point) {
if (Ast *ident = entry_point->identifier.load()) {
- if (ident->file) {
- init_file = ident->file;
+ if (ident->file_id) {
+ init_file = ident->file();
}
}
}
@@ -1304,7 +1441,7 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Variables");
- {
+ if (!build_context.disallow_rtti) {
lbModule *m = default_module;
{ // Add type info data
@@ -1399,30 +1536,30 @@ void lb_generate_code(lbGenerator *gen) {
isize global_variable_max_count = 0;
- Entity *entry_point = info->entry_point;
- bool has_dll_main = false;
- bool has_win_main = false;
+ bool already_has_entry_point = false;
for_array(i, info->entities) {
Entity *e = info->entities[i];
String name = e->token.string;
- bool is_global = e->pkg != nullptr;
-
if (e->kind == Entity_Variable) {
global_variable_max_count++;
- } else if (e->kind == Entity_Procedure && !is_global) {
+ } else if (e->kind == Entity_Procedure) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
- GB_ASSERT(e == entry_point);
- // entry_point = e;
+ GB_ASSERT(e == info->entry_point);
}
- if (e->Procedure.is_export ||
- (e->Procedure.link_name.len > 0) ||
- ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) {
- if (!has_dll_main && name == "DllMain") {
- has_dll_main = true;
- } else if (!has_win_main && name == "WinMain") {
- has_win_main = true;
+ if (build_context.command_kind == Command_test &&
+ (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+ String link_name = e->Procedure.link_name;
+ if (e->pkg->kind == Package_Runtime) {
+ if (link_name == "main" ||
+ link_name == "DllMain" ||
+ link_name == "WinMain" ||
+ link_name == "wWinMain" ||
+ link_name == "mainCRTStartup" ||
+ link_name == "_start") {
+ already_has_entry_point = true;
+ }
}
}
}
@@ -1560,6 +1697,14 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ TIME_SECTION("LLVM Runtime Type Information Creation");
+ lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
+
+ lbProcedure *objc_names = lb_create_objc_names(default_module);
+
+ TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
+ lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
+ gb_unused(startup_runtime);
TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) {
@@ -1619,14 +1764,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- TIME_SECTION("LLVM Runtime Type Information Creation");
- lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
-
- TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
- lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables);
-
-
TIME_SECTION("LLVM Procedure Generation");
for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value;
@@ -1636,8 +1773,7 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
+ if (build_context.command_kind == Command_test && !already_has_entry_point) {
TIME_SECTION("LLVM main");
lb_create_main_procedure(default_module, startup_runtime);
}
@@ -1651,6 +1787,8 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ lb_finalize_objc_names(objc_names);
+
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
for_array(j, gen->modules.entries) {
@@ -1663,6 +1801,7 @@ void lb_generate_code(lbGenerator *gen) {
}
+
TIME_SECTION("LLVM Function Pass");
for_array(i, gen->modules.entries) {
lbModule *m = gen->modules.entries[i].value;
@@ -1807,4 +1946,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
}
+
+ gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index e70b1f84c..a09286d0b 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -135,7 +135,6 @@ struct lbModule {
u32 nested_type_name_guid;
Array procedures_to_generate;
- Array foreign_library_paths;
lbProcedure *curr_procedure;
@@ -144,6 +143,9 @@ struct lbModule {
PtrMap debug_values;
Array debug_incomplete_types;
+
+ StringMap | |