;` 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/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 aa1c1559c..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)
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 bd48011f1..97d2421d4 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -209,7 +209,7 @@ unmarshal_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)
}
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace {
- assert(expect_token(p, .Open_Brace) == nil)
+ unmarshal_expect_token(p, .Open_Brace)
}
v := v
@@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
if end_token == .Close_Brace {
- assert(expect_token(p, .Close_Brace) == nil)
+ unmarshal_expect_token(p, .Close_Brace)
}
return
}
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/fmt.odin b/core/fmt/fmt.odin
index 2cc192c12..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)
}
}
}
@@ -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
}
}
@@ -2099,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 5044d567a..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:]
}
@@ -130,9 +130,9 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
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
@@ -151,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> 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])
@@ -172,108 +172,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
return h1
}
+// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed")
-murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
- when size_of(int) == 8 {
- m :: 0xc6a4a7935bd1e995
- r :: 47
+murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
+ m :: 0xc6a4a7935bd1e995
+ r :: 47
- h: u64 = seed ~ (u64(len(data)) * m)
- data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
+ h: u64 = seed ~ (u64(len(data)) * m)
+ data64 := mem.slice_data_cast([]u64, data)
- 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")
diff --git a/core/hash/xxhash/streaming.odin b/core/hash/xxhash/streaming.odin
index d6df1089f..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
@@ -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 d72b770d5..baacd64d9 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -15,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`,
@@ -46,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.
@@ -88,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`
@@ -103,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,
@@ -131,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,
@@ -146,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,
@@ -157,9 +259,6 @@ PNG_Error :: enum {
Invalid_Chunk_Length,
}
-/*
- PNG-specific structs
-*/
PNG_Info :: struct {
header: PNG_IHDR,
chunks: [dynamic]PNG_Chunk,
@@ -222,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 {
@@ -250,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,
@@ -267,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
@@ -297,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)
@@ -325,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 f4eb5128e..17436c260 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -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 bff0afde3..35fdb58d8 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -18,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 */
@@ -64,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,
@@ -244,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
@@ -259,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
}
@@ -324,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,
}
/*
@@ -350,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
}
}
@@ -366,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
}
@@ -377,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
@@ -392,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
@@ -473,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.
@@ -540,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 {
@@ -594,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
@@ -635,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.
@@ -683,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
}
@@ -699,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
@@ -737,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
@@ -818,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[:])
@@ -1017,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[:])
@@ -1204,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
@@ -1526,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
@@ -1568,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{
@@ -1630,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 803b04d17..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,15 +173,127 @@ 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
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/common.odin b/core/math/big/common.odin
index 2b34a9163..74a641d83 100644
--- a/core/math/big/common.odin
+++ b/core/math/big/common.odin
@@ -172,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",
@@ -182,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",
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 c2bf5552a..24b7a90fc 100644
--- a/core/math/linalg/extended.odin
+++ b/core/math/linalg/extended.odin
@@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> 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/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 7bc68b964..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,
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 4391975ba..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,
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 cb007bd91..c4ecb194f 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -476,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,
diff --git a/core/math/math.odin b/core/math/math.odin
index b81598da9..88cba965a 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -396,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
}
@@ -428,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
}
@@ -460,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
}
@@ -473,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,
@@ -958,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
}
@@ -1027,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,
@@ -1203,7 +1206,7 @@ prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
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) {
@@ -1715,4 +1718,22 @@ F32_BIAS :: 0x7f
F64_MASK :: 0x7ff
F64_SHIFT :: 64 - 12
-F64_BIAS :: 0x3ff
\ No newline at end of file
+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/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 71a56e499..2f6fbdd01 100644
--- a/core/mem/virtual/virtual_linux.odin
+++ b/core/mem/virtual/virtual_linux.odin
@@ -37,9 +37,9 @@ 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) -> rawptr {
+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 rawptr(res)
+ return int(res)
}
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
@@ -58,16 +58,15 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
}
-_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
@@ -75,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 9db57541b..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,13 +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,
- is_no_nil: bool,
+ kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,
@@ -757,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 59eafdc09..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 :: 3
+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,6 +112,9 @@ Entity_Flag :: enum u32le {
Type_Alias = 20,
+ Builtin_Pkg_Builtin = 30,
+ Builtin_Pkg_Intrinsics = 31,
+
Var_Thread_Local = 40,
Var_Static = 41,
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index e8c2c848d..52ecb4781 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -183,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)
@@ -195,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")
}
}
@@ -428,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,
@@ -447,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
@@ -490,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
}
@@ -629,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")
}
}
@@ -710,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)
@@ -852,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
}
}
@@ -894,7 +907,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if is_range {
- assign_stmt := cond.derived.(ast.Assign_Stmt)
+ assign_stmt := cond.derived.(^ast.Assign_Stmt)
vals := assign_stmt.lhs[:]
rhs: ^ast.Expr
@@ -975,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
@@ -1062,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:
@@ -1083,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
}
@@ -1291,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)
@@ -1312,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 ||
@@ -1369,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
}
@@ -1401,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
@@ -1548,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")
}
@@ -1629,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
@@ -1794,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
@@ -1873,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
}
@@ -1891,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")
}
}
@@ -1967,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")
}
@@ -1994,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()
@@ -2125,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
}
@@ -2167,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")
}
@@ -2264,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)
@@ -2319,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)
@@ -2600,8 +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_no_nil: bool
+ is_maybe: bool
+ is_no_nil: bool
+ is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2633,12 +2665,34 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
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
@@ -2659,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) {
@@ -2669,14 +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.is_no_nil = is_no_nil
+ ut.kind = union_kind
return ut
@@ -2834,19 +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.Matrix_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
@@ -2968,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
@@ -3398,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 419f8bbc2..8019b0440 100644
--- a/core/os/file_windows.odin
+++ b/core/os/file_windows.odin
@@ -20,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
@@ -106,19 +106,23 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
BUF_SIZE :: 386
buf16: [BUF_SIZE]u16
buf8: [4*BUF_SIZE]u8
-
+
for n < len(b) && err == 0 {
- max_read := u32(min(BUF_SIZE, len(b)/4))
+ 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]
@@ -129,9 +133,16 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
b[n] = x
n += 1
}
- if ctrl_z || single_read_length < len(buf16) {
+ 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
@@ -309,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)
}
@@ -357,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) {
@@ -376,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
}
@@ -399,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':
@@ -455,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 83158be80..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)
}
@@ -139,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
}
@@ -206,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 b32453a5d..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,13 +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="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> 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 ---
@@ -305,13 +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="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 ---
@@ -324,18 +338,17 @@ get_last_error :: proc() -> int {
}
get_last_error_string :: proc() -> string {
- return cast(string)_darwin_string_error(cast(c.int)get_last_error());
+ 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)
+ 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" {
+when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
if mode != 0 {
err := fchmod(handle, cast(u16)mode)
if err != 0 {
@@ -348,12 +361,12 @@ when ODIN_OS == "darwin" && ODIN_ARCH == "arm64" {
return handle, 0
}
-fchmod :: proc(fildes: Handle, mode: u16) -> Errno {
- return cast(Errno)_unix_fchmod(fildes, mode)
+fchmod :: proc(fd: Handle, mode: u16) -> Errno {
+ return cast(Errno)_unix_fchmod(fd, mode)
}
-close :: proc(fd: Handle) {
- _unix_close(fd)
+close :: proc(fd: Handle) -> bool {
+ return _unix_close(fd) == 0
}
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
@@ -412,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) {
@@ -474,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
@@ -514,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())
}
@@ -553,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 {
@@ -593,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 1c796f1b8..de3a22187 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -11,6 +11,7 @@ import "core:intrinsics"
import "core:sys/unix"
Handle :: distinct i32
+Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
@@ -150,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
@@ -164,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
@@ -266,15 +266,28 @@ X_OK :: 1 // Test for execute permission
W_OK :: 2 // Test for write permission
R_OK :: 4 // Test for read permission
-AT_FDCWD :: -100
+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" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path), uintptr(flags), uintptr(mode))))
+ res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
}
return -1 if res < 0 else Handle(res)
}
@@ -292,7 +305,7 @@ _unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
}
_unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
- when ODIN_ARCH == "amd64" || ODIN_ARCH == "arm64" {
+ 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)
@@ -304,17 +317,17 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
}
_unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == "amd64" {
+ when ODIN_ARCH == .amd64 {
return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
- } else when ODIN_ARCH != "arm64" {
+ } 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), 0))
+ 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" {
+ 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)))
@@ -322,28 +335,28 @@ _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
}
_unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == "amd64" {
+ when ODIN_ARCH == .amd64 {
return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
- } else when ODIN_ARCH != "arm64" {
+ } 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
+ 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" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
+ 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" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mask)))
+ return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
}
}
@@ -356,34 +369,34 @@ _unix_chdir :: proc(path: cstring) -> int {
}
_unix_rename :: proc(old, new: cstring) -> int {
- when ODIN_ARCH != "arm64" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(old)), uintptr(rawptr(new))))
+ 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" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path), 0)))
+ return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
}
}
_unix_rmdir :: proc(path: cstring) -> int {
- when ODIN_ARCH != "arm64" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), AT_REMOVEDIR))
+ 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" {
+ 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, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode)))
+ return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
}
}
@@ -402,6 +415,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(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) -> ! ---
@@ -431,10 +445,25 @@ 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)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, flags, mode)
- defer delete(cstr)
if handle < 0 {
return INVALID_HANDLE, _get_errno(int(handle))
}
@@ -473,11 +502,13 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
}
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 {
@@ -549,6 +580,11 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
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
@@ -580,10 +616,10 @@ 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 < 0 {
return s, _get_errno(result)
@@ -593,10 +629,10 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@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 < 0 {
return s, _get_errno(result)
@@ -606,7 +642,8 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
@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 < 0 {
return s, _get_errno(result)
@@ -659,8 +696,7 @@ _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)
@@ -696,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 {
@@ -712,8 +747,7 @@ 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)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, mask)
if result < 0 {
return false, _get_errno(result)
@@ -727,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))
}
@@ -734,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 {
@@ -774,6 +833,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
}
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
_unix_exit(c.int(code))
}
@@ -782,15 +842,13 @@ current_thread_id :: proc "contextless" () -> int {
}
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..c932f202a 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,15 +261,17 @@ 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)
}
- d, derr := os.open(dir)
+ d, derr := os.open(dir, os.O_RDONLY)
if derr != 0 {
return
}
@@ -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/slashpath/path.odin b/core/path/slashpath/path.odin
index 8ac10e655..865f619bf 100644
--- a/core/path/slashpath/path.odin
+++ b/core/path/slashpath/path.odin
@@ -146,7 +146,7 @@ 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 != "" {
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 424650828..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,37 @@ 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 {
@@ -417,7 +466,7 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN)
// This is probably only useful for freestanding targets
foreign {
@(link_name="__$startup_runtime")
- _startup_runtime :: proc() ---
+ _startup_runtime :: proc "odin" () ---
}
@(link_name="__$cleanup_runtime")
@@ -425,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()
+}
+
/////////////////////////////
/////////////////////////////
@@ -474,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
@@ -523,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
@@ -539,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 3bafc0b1d..4f698a270 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
@@ -382,16 +413,17 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
@builtin
-insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
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
@@ -400,7 +432,7 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
}
@builtin
-insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
if array == nil {
return
}
@@ -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
@@ -423,27 +456,72 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
}
@builtin
-insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
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:], arg)
+ ok = true
+ }
+ return
+}
+
+@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
+
+
+
+@builtin
+assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if index < len(array) {
+ array[index] = arg
+ ok = true
+ } else if resize(array, index+1, loc) {
+ array[index] = arg
+ ok = true
+ }
+ return
+}
+
+
+@builtin
+assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if index+len(args) < len(array) {
+ copy(array[index:], args)
+ ok = true
+ } else if resize(array, index+1+len(args), loc) {
copy(array[index:], args)
ok = true
}
return
}
-@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}
+
+@builtin
+assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+ if len(args) == 0 {
+ ok = true
+ } else if index+len(args) < len(array) {
+ copy(array[index:], args)
+ ok = true
+ } else if resize(array, index+1+len(args), loc) {
+ copy(array[index:], args)
+ ok = true
+ }
+ return
+}
+
+@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
@@ -523,6 +601,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 +713,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}
@@ -618,13 +748,15 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) {
// to improve performance to make the CPU not
// execute speculatively, making it about an order of
// magnitude faster
- proc(message: string, loc: Source_Code_Location) {
+ @(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/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
index dd1e06625..9f7d219c3 100644
--- a/core/runtime/entry_unix.odin
+++ b/core/runtime/entry_unix.odin
@@ -1,5 +1,5 @@
//+private
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package runtime
import "core:intrinsics"
@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
#force_no_inline _cleanup_runtime()
return 0
}
-}
\ No newline at end of file
+}
diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin
index 35a6bb421..a315c1209 100644
--- a/core/runtime/entry_windows.odin
+++ b/core/runtime/entry_windows.odin
@@ -8,21 +8,25 @@ when ODIN_BUILD_MODE == .Dynamic {
@(link_name="DllMain", linkage="strong", require)
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
context = default_context()
- switch fdwReason {
- case 1: // DLL_PROCESS_ATTACH
+
+ // 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 0: // DLL_PROCESS_DETACH
+ case .Process_Detach:
#force_no_inline _cleanup_runtime()
- case 2: // DLL_THREAD_ATTACH
+ case .Thread_Attach:
break
- case 3: // DLL_THREAD_DETACH
+ case .Thread_Detach:
break
}
return true
}
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
- when ODIN_ARCH == "i386" || ODIN_NO_CRT {
+ when ODIN_ARCH == .i386 || ODIN_NO_CRT {
@(link_name="mainCRTStartup", linkage="strong", require)
mainCRTStartup :: proc "stdcall" () -> i32 {
context = default_context()
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 7b283a132..30798f623 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -3,7 +3,7 @@ package runtime
import "core:intrinsics"
@(private="file")
-IS_WASM :: ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64"
+IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64
@(private)
RUNTIME_LINKAGE :: "strong" when (
@@ -37,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
}
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 961f6376f..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 {
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/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 426829a22..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 {
@@ -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)
@@ -304,22 +366,22 @@ 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 {} }
+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
+ 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:]
- }
+ for l := len(s); l > 0; l -= 1 {
+ r = f(r, p[0])
+ q[0] = r
+ p = p[1:]
+ q = q[1:]
+ }
- return res
+ return res
}
@@ -344,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/strconv/strconv.odin b/core/strconv/strconv.odin
index 6ea8b39e6..65161a820 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -895,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 67046c669..603917698 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,16 +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), allocator, loc)
copy(c, s)
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)
@@ -18,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 {
@@ -46,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 {
@@ -54,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,
@@ -75,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)
@@ -82,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 {
@@ -96,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 != "" {
@@ -153,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 ""
@@ -181,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 ""
@@ -198,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 }
}
@@ -280,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
}
@@ -312,44 +533,68 @@ _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))
}
@@ -364,6 +609,14 @@ _trim_cr :: proc(s: string) -> string {
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)
@@ -373,6 +626,14 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string {
return lines
}
+/*
+ split the string `s` at every line break '\n' for `n` parts
+ return an allocated slice of strings
+
+ 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)
@@ -382,6 +643,14 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st
return lines
}
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ return an allocated slice of strings
+
+ 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)
@@ -391,6 +660,15 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string
return lines
}
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ only runs for `n` parts
+ return an allocated slice of strings
+
+ 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)
@@ -400,33 +678,45 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
return lines
}
+/*
+ split the string `s` at every line break '\n'
+ returns the current split string every iteration till the string is consumed
+
+ 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, -1) or_return
+ line = _split_iterator(s, sep, 0) or_return
return _trim_cr(line), true
}
-split_lines_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) {
- sep :: "\n"
- line = _split_iterator(s, sep, 0, n) 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), -1) or_return
+ line = _split_iterator(s, sep, len(sep)) or_return
return _trim_cr(line), true
}
-split_lines_after_n_iterator :: proc(s: ^string, n: int) -> (line: string, ok: bool) {
- sep :: "\n"
- line = _split_iterator(s, sep, len(sep), n) 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 {
@@ -436,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 {
@@ -447,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 {
@@ -500,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 {
@@ -548,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
@@ -581,6 +936,16 @@ index_any :: proc(s, chars: string) -> int {
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
@@ -630,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
@@ -665,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")
@@ -682,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
@@ -727,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)]
@@ -745,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 {
@@ -763,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 {
@@ -776,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 {
@@ -785,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; {
@@ -797,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; {
@@ -808,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 {
@@ -817,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 {
@@ -848,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 {
@@ -859,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 {
@@ -870,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
@@ -884,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
@@ -893,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
@@ -901,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):]
@@ -937,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)]
@@ -944,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
@@ -1114,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)
@@ -1130,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
@@ -1173,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 {
@@ -1200,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)
@@ -1223,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)
@@ -1245,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)
@@ -1362,3 +1852,97 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
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 13c870a3b..000000000
--- a/core/sync/barrier.odin
+++ /dev/null
@@ -1,80 +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 66%
rename from core/sync/sync2/extended.odin
rename to core/sync/extended.odin
index deb48a22d..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) {
@@ -119,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 {
@@ -146,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
}
@@ -160,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)
}
@@ -174,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 {
@@ -196,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)
}
}
@@ -227,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)
}
@@ -240,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
@@ -260,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)
}
@@ -289,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 94%
rename from core/sync/sync2/futex_linux.odin
rename to core/sync/futex_linux.odin
index fca28cace..c429a9d64 100644
--- a/core/sync/sync2/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -1,6 +1,6 @@
//+private
//+build linux
-package sync2
+package sync
import "core:c"
import "core:time"
@@ -14,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
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 97%
rename from core/sync/sync2/primitives.odin
rename to core/sync/primitives.odin
index 6d056d439..bfbdc6f9b 100644
--- a/core/sync/sync2/primitives.odin
+++ b/core/sync/primitives.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -195,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
}
@@ -204,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 71%
rename from core/sync/sync2/primitives_atomic.odin
rename to core/sync/primitives_atomic.odin
index 5fc6fba85..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
}
@@ -282,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.
@@ -401,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
+ }
}
}
@@ -432,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/sync2/primitives_linux.odin b/core/sync/primitives_linux.odin
similarity index 90%
rename from core/sync/sync2/primitives_linux.odin
rename to core/sync/primitives_linux.odin
index 89ed97985..1e75891df 100644
--- a/core/sync/sync2/primitives_linux.odin
+++ b/core/sync/primitives_linux.odin
@@ -1,6 +1,6 @@
//+build linux
//+private
-package sync2
+package sync
import "core:sys/unix"
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_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 f4027e908..000000000
--- a/core/sync/sync2/sema_internal.odin
+++ /dev/null
@@ -1,73 +0,0 @@
-//+private
-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 {
- return unix.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 99%
rename from core/sync/sync2/sync_util.odin
rename to core/sync/sync_util.odin
index 013bf511a..4add9064d 100644
--- a/core/sync/sync2/sync_util.odin
+++ b/core/sync/sync_util.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
/*
Example:
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 146822e61..000000000
--- a/core/sys/cpu/cpu_x86.odin
+++ /dev/null
@@ -1,67 +0,0 @@
-//+build i386, 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
index 3dc3d2c74..d611e33f0 100644
--- a/core/sys/unix/syscalls_linux.odin
+++ b/core/sys/unix/syscalls_linux.odin
@@ -15,7 +15,7 @@ import "core:intrinsics"
// 386: arch/x86/entry/syscalls/sycall_32.tbl
// arm: arch/arm/tools/syscall.tbl
-when ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .amd64 {
SYS_read : uintptr : 0
SYS_write : uintptr : 1
SYS_open : uintptr : 2
@@ -33,8 +33,8 @@ when ODIN_ARCH == "amd64" {
SYS_rt_sigprocmask : uintptr : 14
SYS_rt_sigreturn : uintptr : 15
SYS_ioctl : uintptr : 16
- SYS_pread : uintptr : 17
- SYS_pwrite : uintptr : 18
+ SYS_pread64 : uintptr : 17
+ SYS_pwrite64 : uintptr : 18
SYS_readv : uintptr : 19
SYS_writev : uintptr : 20
SYS_access : uintptr : 21
@@ -374,7 +374,7 @@ when ODIN_ARCH == "amd64" {
SYS_landlock_add_rule : uintptr : 445
SYS_landlock_restrict_self : uintptr : 446
SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm64" {
+} else when ODIN_ARCH == .arm64 {
SYS_io_setup : uintptr : 0
SYS_io_destroy : uintptr : 1
SYS_io_submit : uintptr : 2
@@ -675,7 +675,9 @@ when ODIN_ARCH == "amd64" {
SYS_landlock_create_ruleset : uintptr : 444
SYS_landlock_add_rule : uintptr : 445
SYS_landlock_restrict_self : uintptr : 446
-} else when ODIN_ARCH == "i386" {
+
+ SIGCHLD :: 17
+} else when ODIN_ARCH == .i386 {
SYS_restart_syscall : uintptr : 0
SYS_exit : uintptr : 1
SYS_fork : uintptr : 2
@@ -1112,7 +1114,7 @@ when ODIN_ARCH == "amd64" {
SYS_landlock_add_rule : uintptr : 445
SYS_landlock_restrict_self : uintptr : 446
SYS_memfd_secret : uintptr : 447
-} else when ODIN_ARCH == "arm" {
+} else when ODIN_ARCH == .arm32 { // TODO
SYS_restart_syscall : uintptr : 0
SYS_exit : uintptr : 1
SYS_fork : uintptr : 2
@@ -1516,10 +1518,337 @@ when ODIN_ARCH == "amd64" {
#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 {
+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/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin
index 2e2a99617..d4f0d19cf 100644
--- a/core/sys/wasm/wasi/wasi_api.odin
+++ b/core/sys/wasm/wasi/wasi_api.odin
@@ -1148,6 +1148,156 @@ foreign wasi {
*/
how: sdflags_t,
) -> errno_t ---
+
+
+ /**
+ * Return a description of the given preopened file descriptor.
+ */
+ fd_prestat_dir_name :: proc(
+ fd: fd_t,
+ /**
+ * A buffer into which to write the preopened directory name.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Create a directory.
+ * Note: This is similar to `mkdirat` in POSIX.
+ */
+ path_create_directory :: proc(
+ fd: fd_t,
+ /**
+ * The path at which to create the directory.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Adjust the timestamps of a file or directory.
+ * Note: This is similar to `utimensat` in POSIX.
+ */
+ path_filestat_set_times :: proc(
+ fd: fd_t,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ flags: lookupflags_t,
+ /**
+ * The path of the file or directory to operate on.
+ */
+ path: string,
+ /**
+ * The desired values of the data access timestamp.
+ */
+ atim: timestamp_t,
+ /**
+ * The desired values of the data modification timestamp.
+ */
+ mtim: timestamp_t,
+ /**
+ * A bitmask indicating which timestamps to adjust.
+ */
+ fst_flags: fstflags_t,
+ ) -> errno_t ---
+ /**
+ * Remove a directory.
+ * Return `errno::notempty` if the directory is not empty.
+ * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
+ */
+ path_remove_directory :: proc(
+ fd: fd_t,
+ /**
+ * The path to a directory to remove.
+ */
+ path: string,
+ ) -> errno_t ---
+ /**
+ * Create a hard link.
+ * Note: This is similar to `linkat` in POSIX.
+ */
+ path_link :: proc(
+ old_fd: fd_t,
+ /**
+ * Flags determining the method of how the path is resolved.
+ */
+ old_flags: lookupflags_t,
+ /**
+ * The source path from which to link.
+ */
+ old_path: string,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ new_fd: fd_t,
+ /**
+ * The destination path at which to create the hard link.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Rename a file or directory.
+ * Note: This is similar to `renameat` in POSIX.
+ */
+ path_rename :: proc(
+ fd: fd_t,
+ /**
+ * The source path of the file or directory to rename.
+ */
+ old_path: string,
+ /**
+ * The working directory at which the resolution of the new path starts.
+ */
+ new_fd: fd_t,
+ /**
+ * The destination path to which to rename the file or directory.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Create a symbolic link.
+ * Note: This is similar to `symlinkat` in POSIX.
+ */
+ path_symlink :: proc(
+ /**
+ * The contents of the symbolic link.
+ */
+ old_path: string,
+ fd: fd_t,
+ /**
+ * The destination path at which to create the symbolic link.
+ */
+ new_path: string,
+ ) -> errno_t ---
+
+ /**
+ * Unlink a file.
+ * Return `errno::isdir` if the path refers to a directory.
+ * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
+ */
+ path_unlink_file :: proc(
+ fd: fd_t,
+ /**
+ * The path to a file to unlink.
+ */
+ path: string,
+ ) -> errno_t ---
+
+ /**
+ * Write high-quality random data into a buffer.
+ * This function blocks when the implementation is unable to immediately
+ * provide sufficient high-quality random data.
+ * This function may execute slowly, so when large mounts of random data are
+ * required, it's advisable to use this function to seed a pseudo-random
+ * number generator, rather than to provide the random data directly.
+ */
+ random_get :: proc(
+ /**
+ * The buffer to fill with random data.
+ */
+ buf: []u8,
+ ) -> errno_t ---
+
}
/**
@@ -1250,7 +1400,7 @@ fd_pread :: proc "c" (
*/
offset: filesize_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n)
+ err = wasi_fd_pread(fd, iovs, offset, &n)
return
}
/**
@@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" (
*/
offset: filesize_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n)
+ err = wasi_fd_pwrite(fd, iovs, offset, &n)
return
}
/**
@@ -1297,7 +1447,7 @@ fd_read :: proc "c" (
*/
iovs: []iovec_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n)
+ err = wasi_fd_read(fd, iovs, &n)
return
}
/**
@@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" (
*/
cookie: dircookie_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n)
+ err = wasi_fd_readdir(fd, buf, cookie, &n)
return
}
/**
@@ -1370,7 +1520,7 @@ fd_write :: proc "c" (
*/
iovs: []ciovec_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n)
+ err = wasi_fd_write(fd, iovs, &n)
return
}
/**
@@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" (
*/
path: string,
) -> (offset: filestat_t, err: errno_t) {
- err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset)
+ err = wasi_path_filestat_get(fd, flags, path, &offset)
return
}
/**
@@ -1432,7 +1582,7 @@ path_open :: proc "c" (
fs_rights_inheriting: rights_t,
fdflags: fdflags_t,
) -> (file: fd_t, err: errno_t) {
- err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
+ err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
return
}
/**
@@ -1452,7 +1602,7 @@ path_readlink :: proc "c" (
*/
buf: []u8,
) -> (n: size_t, err: errno_t) {
- err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n)
+ err = wasi_path_readlink(fd, path, buf, &n)
return
}
/**
@@ -1495,7 +1645,7 @@ sock_recv :: proc "c" (
*/
ri_flags: riflags_t,
) -> (n: size_t, flags: roflags_t, err: errno_t) {
- err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags)
+ err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags)
return
}
/**
@@ -1516,172 +1666,11 @@ sock_send :: proc "c" (
*/
si_flags: siflags_t,
) -> (n: size_t, err: errno_t) {
- err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n)
+ err = wasi_sock_send(fd, si_data, si_flags, &n)
return
}
-/**
- * Return a description of the given preopened file descriptor.
- */
-fd_prestat_dir_name :: proc(
- fd: fd_t,
- /**
- * A buffer into which to write the preopened directory name.
- */
- path: string,
-) -> errno_t {
- return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path))
-}
-/**
- * Create a directory.
- * Note: This is similar to `mkdirat` in POSIX.
- */
-path_create_directory :: proc(
- fd: fd_t,
- /**
- * The path at which to create the directory.
- */
- path: string,
-) -> errno_t {
- return wasm_path_create_directory(fd, raw_data(path), len(path))
-}
-/**
- * Adjust the timestamps of a file or directory.
- * Note: This is similar to `utimensat` in POSIX.
- */
-path_filestat_set_times :: proc(
- fd: fd_t,
- /**
- * Flags determining the method of how the path is resolved.
- */
- flags: lookupflags_t,
- /**
- * The path of the file or directory to operate on.
- */
- path: string,
- /**
- * The desired values of the data access timestamp.
- */
- atim: timestamp_t,
- /**
- * The desired values of the data modification timestamp.
- */
- mtim: timestamp_t,
- /**
- * A bitmask indicating which timestamps to adjust.
- */
- fst_flags: fstflags_t,
-) -> errno_t {
- return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags)
-}
-/**
- * Remove a directory.
- * Return `errno::notempty` if the directory is not empty.
- * Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
- */
-path_remove_directory :: proc(
- fd: fd_t,
- /**
- * The path to a directory to remove.
- */
- path: string,
-) -> errno_t {
- return wasm_path_remove_directory(fd, raw_data(path), len(path))
-}
-/**
- * Create a hard link.
- * Note: This is similar to `linkat` in POSIX.
- */
-path_link :: proc(
- old_fd: fd_t,
- /**
- * Flags determining the method of how the path is resolved.
- */
- old_flags: lookupflags_t,
- /**
- * The source path from which to link.
- */
- old_path: string,
- /**
- * The working directory at which the resolution of the new path starts.
- */
- new_fd: fd_t,
- /**
- * The destination path at which to create the hard link.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
-}
-/**
- * Rename a file or directory.
- * Note: This is similar to `renameat` in POSIX.
- */
-path_rename :: proc(
- fd: fd_t,
- /**
- * The source path of the file or directory to rename.
- */
- old_path: string,
- /**
- * The working directory at which the resolution of the new path starts.
- */
- new_fd: fd_t,
- /**
- * The destination path to which to rename the file or directory.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
-}
-/**
- * Create a symbolic link.
- * Note: This is similar to `symlinkat` in POSIX.
- */
-path_symlink :: proc(
- /**
- * The contents of the symbolic link.
- */
- old_path: string,
- fd: fd_t,
- /**
- * The destination path at which to create the symbolic link.
- */
- new_path: string,
-) -> errno_t {
- return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path))
-}
-/**
- * Unlink a file.
- * Return `errno::isdir` if the path refers to a directory.
- * Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
- */
-path_unlink_file :: proc(
- fd: fd_t,
- /**
- * The path to a file to unlink.
- */
- path: string,
-) -> errno_t {
- return wasm_path_unlink_file(fd, raw_data(path), len(path))
-}
-/**
- * Write high-quality random data into a buffer.
- * This function blocks when the implementation is unable to immediately
- * provide sufficient high-quality random data.
- * This function may execute slowly, so when large mounts of random data are
- * required, it's advisable to use this function to seed a pseudo-random
- * number generator, rather than to provide the random data directly.
- */
-random_get :: proc(
- /**
- * The buffer to fill with random data.
- */
- buf: []u8,
-) -> errno_t {
- return wasm_random_get(raw_data(buf), len(buf))
-}
@@ -1722,8 +1711,7 @@ foreign wasi {
@(link_name="fd_pread")
wasi_fd_pread :: proc(
fd: fd_t,
- iovs: [^]iovec_t,
- iovs_len: size_t,
+ iovs: []iovec_t,
offset: filesize_t,
retptr0: ^size_t,
) -> errno_t ---
@@ -1735,23 +1723,20 @@ foreign wasi {
@(link_name="fd_pwrite")
wasi_fd_pwrite :: proc(
fd: fd_t,
- iovs: [^]ciovec_t,
- iovs_len: size_t,
+ iovs: []ciovec_t,
offset: filesize_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="fd_read")
wasi_fd_read :: proc(
fd: fd_t,
- iovs: [^]iovec_t,
- iovs_len: size_t,
+ iovs: []iovec_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="fd_readdir")
wasi_fd_readdir :: proc(
fd: fd_t,
- buf: [^]u8,
- buf_len: size_t,
+ buf: []u8,
cookie: dircookie_t,
retptr0: ^size_t,
) -> errno_t ---
@@ -1770,8 +1755,7 @@ foreign wasi {
@(link_name="fd_write")
wasi_fd_write :: proc(
fd: fd_t,
- iovs: [^]ciovec_t,
- iovs_len: size_t,
+ iovs: []ciovec_t,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="path_filestat_get")
@@ -1781,16 +1765,14 @@ foreign wasi {
/**
* The path of the file or directory to inspect.
*/
- path: [^]u8,
- path_len: size_t,
+ path: string,
retptr0: ^filestat_t,
) -> errno_t ---
@(link_name="path_open")
wasi_path_open :: proc(
fd: fd_t,
dirflags: lookupflags_t,
- path: [^]u8,
- path_len: size_t,
+ path: string,
oflags: oflags_t,
fs_rights_base: rights_t,
fs_rights_inheriting: rights_t,
@@ -1800,10 +1782,8 @@ foreign wasi {
@(link_name="path_readlink")
wasi_path_readlink :: proc(
fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- buf: [^]u8,
- buf_len: size_t,
+ path: string,
+ buf: []u8,
retptr0: ^size_t,
) -> errno_t ---
@(link_name="poll_oneoff")
@@ -1816,8 +1796,7 @@ foreign wasi {
@(link_name="sock_recv")
wasi_sock_recv :: proc(
fd: fd_t,
- ri_data: [^]iovec_t,
- ri_data_len: size_t,
+ ri_data: []iovec_t,
ri_flags: riflags_t,
retptr0: ^size_t,
retptr1: ^roflags_t,
@@ -1825,75 +1804,8 @@ foreign wasi {
@(link_name="sock_send")
wasi_sock_send :: proc(
fd: fd_t,
- si_data: [^]ciovec_t,
- si_data_len: size_t,
+ si_data: []ciovec_t,
si_flags: siflags_t,
retptr0: ^size_t,
) -> errno_t ---
- @(link_name="fd_prestat_dir_name")
- wasm_fd_prestat_dir_name :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_create_directory")
- wasm_path_create_directory :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_filestat_set_times")
- wasm_path_filestat_set_times :: proc(
- fd: fd_t,
- flags: lookupflags_t,
- path: [^]u8,
- path_len: size_t,
- atim: timestamp_t,
- mtim: timestamp_t,
- fst_flags: fstflags_t,
- ) -> errno_t ---
- @(link_name="path_remove_directory")
- wasm_path_remove_directory :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_link")
- wasm_path_link :: proc(
- old_fd: fd_t,
- old_flags: lookupflags_t,
- old_path: [^]u8,
- old_path_len: size_t,
- new_fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_rename")
- wasm_path_rename :: proc(
- fd: fd_t,
- old_path: [^]u8,
- old_path_len: size_t,
- new_fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_symlink")
- wasm_path_symlink :: proc(
- old_path: [^]u8,
- old_path_len: size_t,
- fd: fd_t,
- new_path: [^]u8,
- new_path_len: size_t,
- ) -> errno_t ---
- @(link_name="path_unlink_file")
- wasm_path_unlink_file :: proc(
- fd: fd_t,
- path: [^]u8,
- path_len: size_t,
- ) -> errno_t ---
- @(link_name="random_get")
- wasm_random_get :: proc(
- buf: [^]u8,
- buf_len: size_t,
- ) -> errno_t ---
}
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/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 c24238602..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 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
@@ -101,15 +194,19 @@ _ :: 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 f94e092af..7da2e501b 100644
--- a/examples/all/all_vendor.odin
+++ b/examples/all/all_vendor.odin
@@ -1,15 +1,15 @@
-//+build windows
package all
-
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 SDLNet "vendor:sdl2/net"
@@ -17,31 +17,28 @@ import IMG "vendor:sdl2/image"
import MIX "vendor:sdl2/mixer"
import TTF "vendor:sdl2/ttf"
-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"
-
import vk "vendor:vulkan"
+import NS "vendor:darwin/Foundation"
+import MTL "vendor:darwin/Metal"
+import CA "vendor:darwin/QuartzCore"
_ :: botan
_ :: ENet
+_ :: ggpo
_ :: gl
_ :: glfw
_ :: microui
_ :: miniaudio
_ :: PM
_ :: rl
+_ :: exr
_ :: SDL
_ :: SDLNet
_ :: IMG
_ :: MIX
_ :: TTF
-_ :: stb_easy_font
-_ :: stbi
-_ :: stbrp
-_ :: stbtt
-_ :: stb_vorbis
-_ :: vk
\ No newline at end of file
+_ :: vk
+_ :: 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 ac3727978..d08bd647f 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -89,7 +89,9 @@ template
void slice_init(Slice *s, gbAllocator const &allocator, isize count) {
GB_ASSERT(count >= 0);
s->data = gb_alloc_array(allocator, T, count);
- GB_ASSERT(s->data != nullptr);
+ 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 9a1cb2254..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`.
*/
@@ -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
@@ -473,11 +486,11 @@ void print_bug_report_help() {
#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 b4a934ec8..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
// #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_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"),
@@ -64,6 +76,7 @@ String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
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;
};
@@ -165,6 +186,36 @@ 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
@@ -175,7 +226,10 @@ struct BuildContext {
String ODIN_ROOT; // Odin ROOT
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_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,9 +244,13 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil
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;
@@ -243,6 +301,12 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil
bool copy_file_contents;
+ bool disallow_rtti;
+
+ RelocMode reloc_mode;
+ bool disable_red_zone;
+
+
u32 cmd_doc_flags;
Array extra_packages;
@@ -254,10 +318,13 @@ bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil
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) {
@@ -268,7 +335,7 @@ bool global_ignore_warnings(void) {
}
-gb_global TargetMetrics target_windows_386 = {
+gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
TargetArch_i386,
4,
@@ -284,7 +351,7 @@ 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_i386,
4,
@@ -306,7 +373,16 @@ gb_global TargetMetrics target_linux_arm64 = {
8,
16,
str_lit("aarch64-linux-elf"),
- str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+ 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 = {
@@ -327,7 +403,7 @@ 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_i386,
4,
@@ -344,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,
@@ -370,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,
@@ -389,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 {
@@ -400,17 +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("linux_arm64"), &target_linux_arm64 },
- { str_lit("windows_386"), &target_windows_386 },
+ { 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;
@@ -524,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:
@@ -533,7 +650,6 @@ bool allow_check_foreign_filepath(void) {
return true;
}
-
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -712,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) {
@@ -843,6 +987,21 @@ bool has_asm_extension(String const &path) {
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;
@@ -855,7 +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();
-
+
+ {
+ 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;
@@ -871,6 +1054,8 @@ 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
@@ -878,13 +1063,13 @@ void init_build_context(TargetMetrics *cross_target) {
#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
@@ -912,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
@@ -929,6 +1129,9 @@ 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_i386) {
switch (bc->metrics.os) {
@@ -946,6 +1149,15 @@ 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:
@@ -961,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);
@@ -981,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 a42741976..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,29 @@ 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;
@@ -548,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) {
@@ -558,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;
@@ -704,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;
@@ -725,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;
@@ -858,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);
@@ -870,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);
@@ -931,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);
@@ -943,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);
@@ -993,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);
@@ -1005,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;
}
@@ -1018,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;
}
@@ -1032,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);
@@ -1043,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);
@@ -1051,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;
}
@@ -1131,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;
}
@@ -1965,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;
}
}
@@ -2183,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);
@@ -2337,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;
@@ -2596,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);
@@ -2654,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);
@@ -2691,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;
@@ -2711,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:
{
@@ -2734,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);
@@ -2782,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);
@@ -2826,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);
@@ -2869,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);
@@ -2882,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);
@@ -2908,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)) {
@@ -2938,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)) {
@@ -3002,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)) {
@@ -3039,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:
@@ -3065,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;
}
@@ -3122,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);
@@ -3224,6 +4491,7 @@ 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_i386:
case TargetArch_amd64:
@@ -3310,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:
@@ -3320,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:
@@ -3372,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:
{
@@ -3691,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 = {};
@@ -3780,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 f9bc17ba4..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:
@@ -835,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) {
@@ -857,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);
}
@@ -975,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) {
@@ -1050,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) {
@@ -1142,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:
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 81f69055a..cf9f2f751 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -132,6 +132,62 @@ void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
}
}
+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();
@@ -144,6 +200,7 @@ void check_did_you_mean_type(String const &name, Array const &fields,
check_did_you_mean_print(&d, prefix);
}
+
void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") {
ERROR_BLOCK();
@@ -228,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) {
///////////////////////////////////////////////////////////////////////////////
@@ -421,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];
@@ -508,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;
@@ -673,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)) {
@@ -716,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);
@@ -725,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) {
@@ -782,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) {
@@ -1260,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;
}
@@ -1286,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;
@@ -1422,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:
@@ -1496,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:
@@ -1515,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:
@@ -1566,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;
@@ -1583,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:
@@ -2416,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);
@@ -2626,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;
}
@@ -2715,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);
@@ -2773,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;
}
@@ -2842,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)) {
@@ -2904,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;
@@ -3132,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);
@@ -3419,7 +3548,6 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
if (operand->value.kind == ExactValue_String) {
String key = operand->value.value_string;
if (is_type_string(operand->type) && is_type_enum(target_type)) {
- gb_printf_err("HERE!\n");
Type *et = base_type(target_type);
check_did_you_mean_type(key, et->Enum.fields, ".");
}
@@ -4044,7 +4172,11 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
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;
@@ -4065,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);
@@ -4113,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;
@@ -4214,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;
@@ -4315,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);
@@ -4351,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);
@@ -4376,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);
@@ -4389,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);
@@ -4712,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) {
@@ -4770,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;
@@ -5442,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;
@@ -5472,6 +5740,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
+
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -5756,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;
}
@@ -5770,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);
@@ -5778,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;
@@ -5790,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;
}
@@ -6290,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;
}
}
@@ -6861,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);
@@ -6876,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;
}
@@ -6883,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:
@@ -6955,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);
@@ -7049,1110 +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");
- }
- }
-
- 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;
- }
-
- 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);
@@ -8172,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);
@@ -8362,7 +9302,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
check_unary_expr(c, o, ue->op, node);
}
o->expr = node;
- return kind;
+ return Expr_Expr;
case_end;
@@ -8380,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);
@@ -8861,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;
@@ -8941,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) {
@@ -8956,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;
}
@@ -9342,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 ");
@@ -9433,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];
@@ -9512,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);
@@ -9591,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 94b7561c7..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;
}
@@ -689,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);
@@ -961,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));
@@ -1001,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;
}
@@ -1024,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
@@ -1107,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);
}
}
}
@@ -1143,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));
@@ -1459,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) {
@@ -1483,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;
}
@@ -2194,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;
@@ -2230,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;
@@ -2260,6 +2226,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
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 != "") {
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 2a7479d68..dea523599 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -144,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];
@@ -158,6 +159,9 @@ 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;
@@ -194,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);
@@ -323,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);
@@ -653,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) {
@@ -732,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) {
@@ -803,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));
@@ -1202,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);
@@ -1221,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);
@@ -1313,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 {
@@ -1582,6 +1616,10 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
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;
@@ -1658,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");
@@ -1679,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);
@@ -1711,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);
@@ -1905,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;
@@ -1918,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 != "";
@@ -1989,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;
}
}
}
@@ -2128,7 +2189,7 @@ void init_map_entry_type(Type *type) {
/*
struct {
- hash: runtime.Map_Hash,
+ hash: uintptr,
next: int,
key: Key,
value: Value,
@@ -2644,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;
@@ -2712,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;
@@ -2753,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);
@@ -2984,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 55a3892e5..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;
@@ -225,8 +242,8 @@ bool decl_info_has_init(DeclInfo *d) {
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
s->parent = parent;
- string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
- ptr_set_init(&s->imported, permanent_allocator(), 0);
+ string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
+ ptr_set_init(&s->imported, heap_allocator(), 0);
mutex_init(&s->mutex);
if (parent != nullptr && parent != builtin_pkg->scope) {
@@ -504,6 +521,7 @@ enum VettedEntityKind {
VettedEntity_Unused,
VettedEntity_Shadowed,
+ VettedEntity_Shadowed_And_Unused,
};
struct VettedEntity {
VettedEntityKind kind;
@@ -526,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) {
@@ -576,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;
}
}
@@ -625,12 +662,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
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);
}
}
@@ -642,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:
@@ -724,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;
@@ -790,15 +848,16 @@ struct GlobalEnumValue {
i64 value;
};
-Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count) {
+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 *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+ 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, e);
+ 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++) {
@@ -819,6 +878,9 @@ Slice add_global_enum_type(String const &type_name, GlobalEnumValue *v
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) {
@@ -832,6 +894,17 @@ void add_global_enum_constant(Slice const &fields, char const *name, i
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;
@@ -842,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]);
@@ -861,11 +935,44 @@ 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_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]);
+ }
{
GlobalEnumValue values[BuildMode_COUNT] = {
@@ -880,7 +987,6 @@ void init_universal(void) {
add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode);
}
- add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
{
GlobalEnumValue values[TargetEndian_COUNT] = {
{"Unknown", TargetEndian_Invalid},
@@ -891,6 +997,32 @@ void init_universal(void) {
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);
}
@@ -902,6 +1034,8 @@ void init_universal(void) {
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
@@ -966,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);
+ }
}
@@ -1022,6 +1167,9 @@ void init_checker_info(CheckerInfo *i) {
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) {
@@ -1054,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) {
@@ -1577,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);
@@ -1618,9 +1773,6 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
// NOTE(bill): map entries grow linearly and in order
ti_index = c->info->type_info_types.count;
array_add(&c->info->type_info_types, t);
- if (t->kind == Type_Named && t->Named.name == "A") {
- gb_printf_err("HERE!\n");
- }
}
map_set(&c->checker->info.type_info_map, t, ti_index);
@@ -2092,21 +2244,25 @@ 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"),
@@ -2139,35 +2295,36 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
// WASM Specific
str_lit("__ashlti3"),
str_lit("__multi3"),
- };
- 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]);
- }
+ );
- 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];
@@ -2289,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) {
@@ -2537,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) {
@@ -2696,6 +2864,14 @@ 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"
@@ -2995,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;
}
@@ -3145,6 +3373,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
} 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;
}
@@ -3460,9 +3696,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (entity_visibility_kind == EntityVisiblity_Public &&
(c->scope->flags&ScopeFlag_File) &&
- c->scope->file &&
- (c->scope->file->flags & AstFile_IsPrivate)) {
- entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ 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)) {
@@ -3553,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");
@@ -3658,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;
@@ -3729,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
@@ -4082,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;
@@ -4109,38 +4395,11 @@ 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;
}
@@ -4159,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;
}
@@ -4215,6 +4482,9 @@ 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 ||
@@ -4374,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;
}
@@ -4647,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);
@@ -4868,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;
@@ -4875,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);
}
@@ -5288,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");
}
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 9a8753efd..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;
@@ -340,7 +361,8 @@ struct CheckerInfo {
MPMCQueue intrinsics_entry_point_usage;
-
+ BlockingMutex objc_types_mutex;
+ PtrMap objc_msgSend_types;
};
struct CheckerContext {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index e8f5174c0..3ea6fcdd5 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,
@@ -252,6 +276,19 @@ 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] = {
@@ -299,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},
@@ -321,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},
@@ -335,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},
@@ -420,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"), 1, 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},
@@ -457,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},
@@ -466,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},
@@ -479,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},
@@ -495,6 +553,8 @@ 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},
@@ -504,4 +564,18 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{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 ab2a46118..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;
@@ -675,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;
@@ -1021,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 096c35b5c..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) {
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 39f2e307c..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 3
+#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 {
@@ -154,6 +155,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
+ OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -170,6 +172,9 @@ 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,
@@ -201,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 825ca113f..2f531a45c 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -619,9 +619,10 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
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));
@@ -683,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;
@@ -811,14 +779,17 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
- if (!comment && e->kind == Entity_Variable) {
- comment = e->Variable.comment;
- }
- if (!docs && e->kind == Entity_Variable) {
- docs = e->Variable.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;
u64 flags = 0;
@@ -833,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) {
@@ -862,6 +834,23 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
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) {
@@ -897,8 +886,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
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);
@@ -975,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 {};
}
@@ -983,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;
@@ -1001,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);
}
@@ -1096,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 0f8bfa456..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;
@@ -161,6 +199,8 @@ struct Entity {
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
@@ -184,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;
@@ -192,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;
@@ -211,6 +255,7 @@ struct Entity {
struct {
Slice paths;
String name;
+ i64 priority_index;
} LibraryName;
i32 Nil;
struct {
@@ -243,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
index b08ff99df..faf4d11fb 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -33,6 +33,10 @@ void init_global_error_collector(void) {
}
+// 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);
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 d9bf09436..d09c7618b 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
@@ -86,6 +90,10 @@ extern "C" {
#error This operating system is not supported
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+#include
+#endif
+
#if defined(_MSC_VER)
#define GB_COMPILER_MSVC 1
#elif defined(__GNUC__)
@@ -199,7 +207,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 +243,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 +797,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 +1684,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);
@@ -3678,6 +3699,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;
@@ -6025,7 +6070,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);
@@ -6041,6 +6086,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
}
@@ -6093,6 +6181,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;
@@ -6188,20 +6277,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 310df6639..b22a839b3 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -233,7 +233,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
i64 elem_size = lb_sizeof(elem);
i64 count = LLVMGetVectorSize(type);
i64 size = count * elem_size;
- return gb_clamp(next_pow2(size), 1, build_context.max_align);
+ return next_pow2(size);
}
}
@@ -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:
@@ -797,16 +801,23 @@ namespace lbAbiAmd64SysV {
i64 elem_sz = lb_sizeof(elem);
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
RegClass reg = RegClass_NoClass;
+ unsigned elem_width = LLVMGetIntTypeWidth(elem);
switch (elem_kind) {
case LLVMIntegerTypeKind:
case LLVMHalfTypeKind:
- switch (LLVMGetIntTypeWidth(elem)) {
- case 8: reg = RegClass_SSEInt8;
- case 16: reg = RegClass_SSEInt16;
- case 32: reg = RegClass_SSEInt32;
- case 64: reg = RegClass_SSEInt64;
+ switch (elem_width) {
+ case 8: reg = RegClass_SSEInt8; break;
+ case 16: reg = RegClass_SSEInt16; break;
+ case 32: reg = RegClass_SSEInt32; break;
+ case 64: reg = RegClass_SSEInt64; break;
default:
- GB_PANIC("Unhandled integer width for vector type");
+ if (elem_width > 64) {
+ for (i64 i = 0; i < len; i++) {
+ classify_with(elem, cls, ix, off + i*elem_sz);
+ }
+ break;
+ }
+ GB_PANIC("Unhandled integer width for vector type %u", elem_width);
}
break;
case LLVMFloatTypeKind:
@@ -1052,10 +1063,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;
@@ -1083,7 +1102,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:
@@ -1095,7 +1114,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);
@@ -1104,29 +1149,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);
}
@@ -1150,6 +1175,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);
@@ -1164,6 +1193,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) {
@@ -1184,27 +1295,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_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 304effb7f..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) {
@@ -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];
@@ -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;
}
}
}
@@ -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;
@@ -1244,6 +1357,24 @@ void lb_generate_code(lbGenerator *gen) {
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,
@@ -1310,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
@@ -1417,9 +1548,8 @@ void lb_generate_code(lbGenerator *gen) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
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 (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" ||
@@ -1570,8 +1700,10 @@ 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, 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");
@@ -1641,6 +1773,11 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ if (build_context.command_kind == Command_test && !already_has_entry_point) {
+ TIME_SECTION("LLVM main");
+ lb_create_main_procedure(default_module, startup_runtime);
+ }
+
for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value;
for_array(i, m->missing_procedures_to_check) {
@@ -1650,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) {
@@ -1662,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;
@@ -1806,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 49f675a49..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 objc_classes;
+ StringMap objc_selectors;
};
struct lbGenerator {
@@ -159,6 +161,10 @@ struct lbGenerator {
PtrMap anonymous_proc_lits;
+ BlockingMutex foreign_mutex;
+ PtrSet foreign_libraries_set;
+ Array foreign_libraries;
+
std::atomic global_array_index;
std::atomic global_generated_index;
};
@@ -232,6 +238,7 @@ struct lbTargetList {
enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+ lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
struct lbCopyElisionHint {
@@ -285,6 +292,9 @@ struct lbProcedure {
LLVMMetadataRef debug_info;
lbCopyElisionHint copy_elision_hint;
+
+ PtrMap selector_values;
+ PtrMap selector_addr;
};
@@ -292,7 +302,6 @@ struct lbProcedure {
bool lb_init_generator(lbGenerator *gen, Checker *c);
-void lb_generate_module(lbGenerator *gen);
String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
@@ -365,7 +374,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
void lb_add_foreign_library_path(lbModule *m, Entity *e);
@@ -455,6 +464,8 @@ void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module,
lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t);
bool lb_is_expr_untyped_const(Ast *expr);
+LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name = "");
+
void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment);
void lb_emit_init_context(lbProcedure *p, lbAddr addr);
@@ -471,7 +482,11 @@ LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRe
void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
+LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile);
+i64 lb_max_zero_init_size(void) {
+ return cast(i64)(4*build_context.word_size);
+}
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
@@ -545,6 +560,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
lbCallingConvention_C, // ProcCC_None,
lbCallingConvention_C, // ProcCC_Naked,
lbCallingConvention_C, // ProcCC_InlineAsm,
+
+ lbCallingConvention_Win64, // ProcCC_Win64,
+ lbCallingConvention_X86_64_SysV, // ProcCC_SysV,
+
};
enum : LLVMDWARFTypeEncoding {
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 5862a7add..201932ad9 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
- GB_ASSERT(is_type_pointer(value.type));
- GB_ASSERT(is_type_pointer(t));
+ GB_ASSERT(is_type_internally_pointer_like(value.type));
+ GB_ASSERT(is_type_internally_pointer_like(t));
GB_ASSERT(lb_is_const(value));
lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
}
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
- GB_ASSERT(is_type_pointer(data.type));
+ GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
GB_ASSERT(are_types_identical(len.type, t_int));
LLVMValueRef vals[2] = {
data.value,
@@ -410,12 +410,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
// NOTE(bill, 2020-06-08): This is a bit of a hack but a "constant" slice needs
// its backing data on the stack
lbProcedure *p = m->curr_procedure;
- LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
-
LLVMTypeRef llvm_type = lb_type(m, t);
- array_data = LLVMBuildAlloca(p->builder, llvm_type, "");
- LLVMSetAlignment(array_data, 16); // TODO(bill): Make this configurable
- LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+
+ array_data = llvm_alloca(p, llvm_type, 16);
+
LLVMBuildStore(p->builder, backing_array.value, array_data);
{
@@ -495,9 +493,9 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = data;
return res;
} else if (is_type_array(type) &&
- value.kind != ExactValue_Invalid &&
- value.kind != ExactValue_String &&
- value.kind != ExactValue_Compound) {
+ value.kind != ExactValue_Invalid &&
+ value.kind != ExactValue_String &&
+ value.kind != ExactValue_Compound) {
i64 count = type->Array.count;
Type *elem = type->Array.elem;
@@ -513,8 +511,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = llvm_const_array(lb_type(m, elem), elems, cast(unsigned)count);
return res;
} else if (is_type_matrix(type) &&
- value.kind != ExactValue_Invalid &&
- value.kind != ExactValue_Compound) {
+ value.kind != ExactValue_Invalid &&
+ value.kind != ExactValue_Compound) {
i64 row = type->Matrix.row_count;
i64 column = type->Matrix.column_count;
GB_ASSERT(row == column);
@@ -537,6 +535,22 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
res.value = LLVMConstArray(lb_type(m, elem), elems, cast(unsigned)total_elem_count);
return res;
+ } else if (is_type_simd_vector(type) &&
+ value.kind != ExactValue_Invalid &&
+ value.kind != ExactValue_Compound) {
+ i64 count = type->SimdVector.count;
+ Type *elem = type->SimdVector.elem;
+
+ lbValue single_elem = lb_const_value(m, elem, value, allow_local);
+ single_elem.value = llvm_const_cast(single_elem.value, lb_type(m, elem));
+
+ LLVMValueRef *elems = gb_alloc_array(permanent_allocator(), LLVMValueRef, count);
+ for (i64 i = 0; i < count; i++) {
+ elems[i] = single_elem.value;
+ }
+
+ res.value = LLVMConstVector(elems, cast(unsigned)count);
+ return res;
}
switch (value.kind) {
@@ -568,7 +582,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
case ExactValue_Integer:
- if (is_type_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
@@ -819,26 +833,81 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
return lb_const_nil(m, original_type);
}
GB_ASSERT(elem_type_can_be_constant(elem_type));
-
isize total_elem_count = cast(isize)type->SimdVector.count;
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, total_elem_count);
- for (isize i = 0; i < elem_count; i++) {
- TypeAndValue tav = cl->elems[i]->tav;
- GB_ASSERT(tav.mode != Addressing_Invalid);
- values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
- }
- LLVMTypeRef et = lb_type(m, elem_type);
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand
+ isize value_index = 0;
+ for (i64 i = 0; i < total_elem_count; i++) {
+ bool found = false;
- for (isize i = elem_count; i < type->SimdVector.count; i++) {
- values[i] = LLVMConstNull(et);
- }
- for (isize i = 0; i < total_elem_count; i++) {
- values[i] = llvm_const_cast(values[i], et);
- }
+ for (isize j = 0; j < elem_count; j++) {
+ Ast *elem = cl->elems[j];
+ ast_node(fv, FieldValue, elem);
+ if (is_ast_range(fv->field)) {
+ ast_node(ie, BinaryExpr, fv->field);
+ TypeAndValue lo_tav = ie->left->tav;
+ TypeAndValue hi_tav = ie->right->tav;
+ GB_ASSERT(lo_tav.mode == Addressing_Constant);
+ GB_ASSERT(hi_tav.mode == Addressing_Constant);
- res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
- return res;
+ TokenKind op = ie->op.kind;
+ i64 lo = exact_value_to_i64(lo_tav.value);
+ i64 hi = exact_value_to_i64(hi_tav.value);
+ if (op != Token_RangeHalf) {
+ hi += 1;
+ }
+ if (lo == i) {
+ TypeAndValue tav = fv->value->tav;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ for (i64 k = lo; k < hi; k++) {
+ values[value_index++] = val;
+ }
+
+ found = true;
+ i += (hi-lo-1);
+ break;
+ }
+ } else {
+ TypeAndValue index_tav = fv->field->tav;
+ GB_ASSERT(index_tav.mode == Addressing_Constant);
+ i64 index = exact_value_to_i64(index_tav.value);
+ if (index == i) {
+ TypeAndValue tav = fv->value->tav;
+ LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ values[value_index++] = val;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found) {
+ values[value_index++] = LLVMConstNull(lb_type(m, elem_type));
+ }
+ }
+
+ res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
+ return res;
+ } else {
+ for (isize i = 0; i < elem_count; i++) {
+ TypeAndValue tav = cl->elems[i]->tav;
+ GB_ASSERT(tav.mode != Addressing_Invalid);
+ values[i] = lb_const_value(m, elem_type, tav.value, allow_local).value;
+ }
+ LLVMTypeRef et = lb_type(m, elem_type);
+
+ for (isize i = elem_count; i < total_elem_count; i++) {
+ values[i] = LLVMConstNull(et);
+ }
+ for (isize i = 0; i < total_elem_count; i++) {
+ values[i] = llvm_const_cast(values[i], et);
+ }
+
+ res.value = LLVMConstVector(values, cast(unsigned)total_elem_count);
+ return res;
+ }
} else if (is_type_struct(type)) {
ast_node(cl, CompoundLit, value.value_compound);
@@ -956,7 +1025,10 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
return lb_const_nil(m, original_type);
}
- u64 bits = 0;
+ 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);
@@ -968,18 +1040,13 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
GB_ASSERT(tav.value.kind == ExactValue_Integer);
i64 v = big_int_to_i64(&tav.value.value_integer);
i64 lower = type->BitSet.lower;
- bits |= 1ull<debug_builder, cast(unsigned)type->SimdVector.count, 8*cast(unsigned)type_align_of(type), lb_debug_type(m, type->SimdVector.elem), nullptr, 0);
+ {
+ LLVMMetadataRef elem = lb_debug_type(m, type->SimdVector.elem);
+ LLVMMetadataRef subscripts[1] = {};
+ subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+ 0ll,
+ type->SimdVector.count
+ );
+ return LLVMDIBuilderCreateVectorType(
+ m->debug_builder,
+ 8*cast(unsigned)type_size_of(type), 8*cast(unsigned)type_align_of(type),
+ elem, subscripts, gb_count_of(subscripts));
+ }
case Type_RelativePointer: {
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
@@ -958,13 +973,79 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
);
LLVMValueRef storage = ptr;
- LLVMValueRef instr = ptr;
+ LLVMBasicBlockRef block = p->curr_block->block;
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
- LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+ LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
}
+
+void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ if (type == nullptr) {
+ return;
+ }
+ if (type == t_invalid) {
+ return;
+ }
+ if (p->body == nullptr) {
+ return;
+ }
+
+ lbModule *m = p->module;
+ String const &name = token.string;
+ if (name == "" || name == "_") {
+ return;
+ }
+
+ if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+ // Already been set
+ return;
+ }
+
+
+ AstFile *file = p->body->file();
+
+ LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ GB_ASSERT(llvm_scope != nullptr);
+ if (llvm_file == nullptr) {
+ llvm_file = LLVMDIScopeGetFile(llvm_scope);
+ }
+
+ if (llvm_file == nullptr) {
+ return;
+ }
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ LLVMBool always_preserve = build_context.optimization_level == 0;
+
+ LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+ LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
+ m->debug_builder, llvm_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ arg_number,
+ llvm_file, token.pos.line,
+ debug_type,
+ always_preserve, flags
+ );
+
+ LLVMValueRef storage = ptr;
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+ LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+ lb_set_llvm_metadata(m, ptr, llvm_expr);
+
+ // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
+ // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
+ // instruction "before" it.
+ LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
+}
+
+
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
if (!p->debug_info || !p->body) {
return;
@@ -984,5 +1065,10 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
token.string = str_lit("context");
token.pos = pos;
- lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
+ LLVMValueRef ptr = ctx.addr.value;
+ while (LLVMIsABitCastInst(ptr)) {
+ ptr = LLVMGetOperand(ptr, 0);
+ }
+
+ lb_add_debug_local_variable(p, ptr, t_context, token);
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 1f0ed6434..b28470770 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -1,3 +1,4 @@
+lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false);
lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
lbModule *m = p->module;
@@ -258,7 +259,18 @@ lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, Type *type)
LLVMBuildStore(p->builder, v2, LLVMBuildStructGEP(p->builder, addr.addr.value, 2, ""));
LLVMBuildStore(p->builder, v3, LLVMBuildStructGEP(p->builder, addr.addr.value, 3, ""));
return lb_addr_load(p, addr);
-
+ } else if (is_type_simd_vector(x.type)) {
+ Type *elem = base_array_type(x.type);
+ if (is_type_float(elem)) {
+ res.value = LLVMBuildFNeg(p->builder, x.value, "");
+ } else {
+ res.value = LLVMBuildNeg(p->builder, x.value, "");
+ }
+ } else if (is_type_matrix(x.type)) {
+ lbValue zero = {};
+ zero.value = LLVMConstNull(lb_type(p->module, type));
+ zero.type = type;
+ return lb_emit_arith_matrix(p, Token_Sub, zero, x, type, true);
} else {
GB_PANIC("Unhandled type %s", type_to_string(x.type));
}
@@ -580,6 +592,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
if (is_type_array(m.type)) {
+ i32 rank = type_math_rank(m.type);
+ if (rank == 2) {
+ lbAddr addr = lb_add_local_generated(p, type, false);
+ lbValue dst = addr.addr;
+ lbValue src = m;
+ i32 n = cast(i32)get_array_type_count(m.type);
+ i32 m = cast(i32)get_array_type_count(type);
+ // m.type == [n][m]T
+ // type == [m][n]T
+
+ for (i32 j = 0; j < m; j++) {
+ lbValue dst_col = lb_emit_struct_ep(p, dst, j);
+ for (i32 i = 0; i < n; i++) {
+ lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
+ lbValue src_col = lb_emit_struct_ev(p, src, i);
+ lbValue src_row = lb_emit_struct_ev(p, src_col, j);
+ lb_emit_store(p, dst_row, src_row);
+ }
+ }
+ return lb_addr_load(p, addr);
+ }
// no-op
m.type = type;
return m;
@@ -949,7 +982,7 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
-lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise=false) {
+lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise) {
GB_ASSERT(is_type_matrix(lhs.type) || is_type_matrix(rhs.type));
@@ -1398,10 +1431,13 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
Type *it = bit_set_to_int(rt);
left = lb_emit_conv(p, left, it);
+ if (is_type_different_to_arch_endianness(it)) {
+ left = lb_emit_byte_swap(p, left, integer_endian_type_to_platform_type(it));
+ }
- lbValue lower = lb_const_value(p->module, it, exact_value_i64(rt->BitSet.lower));
- lbValue key = lb_emit_arith(p, Token_Sub, left, lower, it);
- lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, it, 1), key, it);
+ lbValue lower = lb_const_value(p->module, left.type, exact_value_i64(rt->BitSet.lower));
+ lbValue key = lb_emit_arith(p, Token_Sub, left, lower, left.type);
+ lbValue bit = lb_emit_arith(p, Token_Shl, lb_const_int(p->module, left.type, 1), key, left.type);
bit = lb_emit_conv(p, bit, it);
lbValue old_value = lb_emit_transmute(p, right, it);
@@ -1799,6 +1835,59 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
+ if (is_type_simd_vector(dst)) {
+ Type *et = base_array_type(dst);
+ if (is_type_simd_vector(src)) {
+ Type *src_elem = core_array_type(src);
+ Type *dst_elem = core_array_type(dst);
+
+ GB_ASSERT(src->SimdVector.count == dst->SimdVector.count);
+
+ lbValue res = {};
+ res.type = t;
+ if (are_types_identical(src_elem, dst_elem)) {
+ res.value = value.value;
+ } else if (is_type_float(src_elem) && is_type_integer(dst_elem)) {
+ if (is_type_unsigned(dst_elem)) {
+ res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
+ } else {
+ res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
+ }
+ } else if (is_type_integer(src_elem) && is_type_float(dst_elem)) {
+ if (is_type_unsigned(src_elem)) {
+ res.value = LLVMBuildUIToFP(p->builder, value.value, lb_type(m, t), "");
+ } else {
+ res.value = LLVMBuildSIToFP(p->builder, value.value, lb_type(m, t), "");
+ }
+ } else if ((is_type_integer(src_elem) || is_type_boolean(src_elem)) && is_type_integer(dst_elem)) {
+ res.value = LLVMBuildIntCast2(p->builder, value.value, lb_type(m, t), !is_type_unsigned(src_elem), "");
+ } else if (is_type_float(src_elem) && is_type_float(dst_elem)) {
+ res.value = LLVMBuildFPCast(p->builder, value.value, lb_type(m, t), "");
+ } else if (is_type_integer(src_elem) && is_type_boolean(dst_elem)) {
+ LLVMValueRef i1vector = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(LLVMTypeOf(value.value)), "");
+ res.value = LLVMBuildIntCast2(p->builder, i1vector, lb_type(m, t), !is_type_unsigned(src_elem), "");
+ } else {
+ GB_PANIC("Unhandled simd vector conversion: %s -> %s", type_to_string(src), type_to_string(dst));
+ }
+ return res;
+ } else {
+ i64 count = get_array_type_count(dst);
+ LLVMTypeRef vt = lb_type(m, t);
+ LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
+ LLVMValueRef elem = lb_emit_conv(p, value, et).value;
+ LLVMValueRef vector = LLVMConstNull(vt);
+ for (i64 i = 0; i < count; i++) {
+ LLVMValueRef idx = LLVMConstInt(llvm_u32, i, false);
+ vector = LLVMBuildInsertElement(p->builder, vector, elem, idx, "");
+ }
+ lbValue res = {};
+ res.type = t;
+ res.value = vector;
+ return res;
+ }
+ }
+
+
// Pointer <-> uintptr
if (is_type_pointer(src) && is_type_uintptr(dst)) {
lbValue res = {};
@@ -1834,6 +1923,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_addr_load(p, parent);
}
}
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ if (internal_check_is_assignable_to(src, vt)) {
+ value = lb_emit_conv(p, value, vt);
+ lbAddr parent = lb_add_local_generated(p, t, true);
+ lb_emit_store_union_variant(p, parent.addr, value, vt);
+ return lb_addr_load(p, parent);
+ }
+ }
}
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2172,6 +2270,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
+ if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+ Type *tl = base_type(a);
+ lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+ lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+ // TODO(bill): Test to see if this is actually faster!!!!
+ auto args = array_make(permanent_allocator(), 3);
+ args[0] = lb_emit_conv(p, lhs, t_rawptr);
+ args[1] = lb_emit_conv(p, rhs, t_rawptr);
+ args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+ lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+ lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+ return lb_emit_conv(p, res, t_bool);
+ }
if (is_type_array(a) || is_type_enumerated_array(a)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
@@ -2461,6 +2574,57 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
case Token_NotEq: pred = LLVMIntNE; break;
}
res.value = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+ } else if (is_type_simd_vector(a)) {
+ LLVMValueRef mask = nullptr;
+ Type *elem = base_array_type(a);
+ if (is_type_float(elem)) {
+ LLVMRealPredicate pred = {};
+ switch (op_kind) {
+ case Token_CmpEq: pred = LLVMRealOEQ; break;
+ case Token_NotEq: pred = LLVMRealONE; break;
+ }
+ mask = LLVMBuildFCmp(p->builder, pred, left.value, right.value, "");
+ } else {
+ LLVMIntPredicate pred = {};
+ switch (op_kind) {
+ case Token_CmpEq: pred = LLVMIntEQ; break;
+ case Token_NotEq: pred = LLVMIntNE; break;
+ }
+ mask = LLVMBuildICmp(p->builder, pred, left.value, right.value, "");
+ }
+ GB_ASSERT_MSG(mask != nullptr, "Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
+
+ /* NOTE(bill, 2022-05-28):
+ Thanks to Per Vognsen, sign extending to
+ a vector of the same width as the input vector, bit casting to an integer,
+ and then comparing against zero is the better option
+ See: https://lists.llvm.org/pipermail/llvm-dev/2012-September/053046.html
+
+ // Example assuming 128-bit vector
+
+ %1 = <4 x float> ...
+ %2 = <4 x float> ...
+ %3 = fcmp oeq <4 x float> %1, %2
+ %4 = sext <4 x i1> %3 to <4 x i32>
+ %5 = bitcast <4 x i32> %4 to i128
+ %6 = icmp ne i128 %5, 0
+ br i1 %6, label %true1, label %false2
+
+ This will result in 1 cmpps + 1 ptest + 1 br
+ (even without SSE4.1, contrary to what the mail list states, because of pmovmskb)
+
+ */
+
+ unsigned count = cast(unsigned)get_array_type_count(a);
+ unsigned elem_sz = cast(unsigned)(type_size_of(elem)*8);
+ LLVMTypeRef mask_type = LLVMVectorType(LLVMIntTypeInContext(p->module->ctx, elem_sz), count);
+ mask = LLVMBuildSExtOrBitCast(p->builder, mask, mask_type, "");
+
+ LLVMTypeRef mask_int_type = LLVMIntTypeInContext(p->module->ctx, cast(unsigned)(8*type_size_of(a)));
+ LLVMValueRef mask_int = LLVMBuildBitCast(p->builder, mask, mask_int_type, "");
+ res.value = LLVMBuildICmp(p->builder, LLVMIntNE, mask_int, LLVMConstNull(LLVMTypeOf(mask_int)), "");
+ return res;
+
} else {
GB_PANIC("Unhandled comparison kind %s (%s) %.*s %s (%s)", type_to_string(left.type), type_to_string(base_type(left.type)), LIT(token_strings[op_kind]), type_to_string(right.type), type_to_string(base_type(right.type)));
}
@@ -2768,28 +2932,39 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *src_type = type_deref(v.type);
Type *dst_type = type;
- lbValue src_tag = {};
- lbValue dst_tag = {};
- if (is_type_union_maybe_pointer(src_type)) {
- src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
- dst_tag = lb_const_bool(p->module, t_bool, true);
- } else {
- src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
- dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ lbValue src_tag = {};
+ lbValue dst_tag = {};
+ if (is_type_union_maybe_pointer(src_type)) {
+ src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+ dst_tag = lb_const_bool(p->module, t_bool, true);
+ } else {
+ src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+ dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+ }
+
+
+ isize arg_count = 6;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+ auto args = array_make(permanent_allocator(), arg_count);
+ args[0] = ok;
+
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+ if (!build_context.disallow_rtti) {
+ args[4] = lb_typeid(p->module, src_type);
+ args[5] = lb_typeid(p->module, dst_type);
+ }
+ lb_emit_runtime_call(p, "type_assertion_check", args);
}
- lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
- auto args = array_make(permanent_allocator(), 6);
- args[0] = ok;
-
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
-
- args[4] = lb_typeid(p->module, src_type);
- args[5] = lb_typeid(p->module, dst_type);
- lb_emit_runtime_call(p, "type_assertion_check", args);
-
lbValue data_ptr = v;
return lb_emit_conv(p, data_ptr, tv.type);
} else if (is_type_any(t)) {
@@ -2797,23 +2972,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
if (is_type_pointer(v.type)) {
v = lb_emit_load(p, v);
}
-
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue id = lb_typeid(p->module, type);
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ GB_ASSERT(!build_context.disallow_rtti);
+ lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make(permanent_allocator(), 6);
- args[0] = ok;
+ lbValue id = lb_typeid(p->module, type);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+ auto args = array_make(permanent_allocator(), 6);
+ args[0] = ok;
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
- args[4] = any_id;
- args[5] = id;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[4] = any_id;
+ args[5] = id;
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
return lb_emit_conv(p, data_ptr, tv.type);
} else {
@@ -2825,9 +3002,8 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
return lb_build_addr_ptr(p, ue->expr);
}
+lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr);
lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
- lbModule *m = p->module;
-
u16 prev_state_flags = p->state_flags;
defer (p->state_flags = prev_state_flags);
@@ -2843,9 +3019,49 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ } else if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ }
+
p->state_flags = out;
}
+
+ // IMPORTANT NOTE(bill):
+ // Selector Call Expressions (foo->bar(...))
+ // must only evaluate `foo` once as it gets transformed into
+ // `foo.bar(foo, ...)`
+ // And if `foo` is a procedure call or something more complex, storing the value
+ // once is a very good idea
+ // If a stored value is found, it must be removed from the cache
+ if (expr->state_flags & StateFlag_SelectorCallExpr) {
+ lbValue *pp = map_get(&p->selector_values, expr);
+ if (pp != nullptr) {
+ lbValue res = *pp;
+ map_remove(&p->selector_values, expr);
+ return res;
+ }
+ lbAddr *pa = map_get(&p->selector_addr, expr);
+ if (pa != nullptr) {
+ lbAddr res = *pa;
+ map_remove(&p->selector_addr, expr);
+ return lb_addr_load(p, res);
+ }
+ }
+ lbValue res = lb_build_expr_internal(p, expr);
+ if (expr->state_flags & StateFlag_SelectorCallExpr) {
+ map_set(&p->selector_values, expr, res);
+ }
+ return res;
+}
+
+lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
+ lbModule *m = p->module;
+
expr = unparen_expr(expr);
TokenPos expr_pos = ast_token(expr).pos;
@@ -2864,17 +3080,6 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
return lb_const_value(p->module, type, tv.value);
}
- #if 0
- LLVMMetadataRef prev_debug_location = nullptr;
- if (p->debug_info != nullptr) {
- prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder);
- LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, expr));
- }
- defer (if (prev_debug_location != nullptr) {
- LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location);
- });
- #endif
-
switch (expr->kind) {
case_ast_node(bl, BasicLit, expr);
TokenPos pos = bl->token.pos;
@@ -2943,14 +3148,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
case_ast_node(se, SelectorCallExpr, expr);
GB_ASSERT(se->modified_call);
- TypeAndValue tav = type_and_value_of_expr(expr);
- GB_ASSERT(tav.mode != Addressing_Invalid);
- lbValue res = lb_build_call_expr(p, se->call);
-
- ast_node(ce, CallExpr, se->call);
- ce->sce_temp_data = gb_alloc_copy(permanent_allocator(), &res, gb_size_of(res));
-
- return res;
+ return lb_build_call_expr(p, se->call);
case_end;
case_ast_node(te, TernaryIfExpr, expr);
@@ -2962,23 +3160,31 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
+ LLVMTypeRef llvm_type = lb_type(p->module, type);
incoming_values[0] = lb_emit_conv(p, lb_build_expr(p, te->x), type).value;
+ if (is_type_internally_pointer_like(type)) {
+ incoming_values[0] = LLVMBuildBitCast(p->builder, incoming_values[0], llvm_type, "");
+ }
lb_emit_jump(p, done);
lb_start_block(p, else_);
incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, te->y), type).value;
+ if (is_type_internally_pointer_like(type)) {
+ incoming_values[1] = LLVMBuildBitCast(p->builder, incoming_values[1], llvm_type, "");
+ }
+
lb_emit_jump(p, done);
lb_start_block(p, done);
lbValue res = {};
- res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
+ res.value = LLVMBuildPhi(p->builder, llvm_type, "");
res.type = type;
GB_ASSERT(p->curr_block->preds.count >= 2);
@@ -3236,9 +3442,34 @@ lbAddr lb_build_array_swizzle_addr(lbProcedure *p, AstCallExpr *ce, TypeAndValue
}
+lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr);
lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
expr = unparen_expr(expr);
+ // IMPORTANT NOTE(bill):
+ // Selector Call Expressions (foo->bar(...))
+ // must only evaluate `foo` once as it gets transformed into
+ // `foo.bar(foo, ...)`
+ // And if `foo` is a procedure call or something more complex, storing the value
+ // once is a very good idea
+ // If a stored value is found, it must be removed from the cache
+ if (expr->state_flags & StateFlag_SelectorCallExpr) {
+ lbAddr *pp = map_get(&p->selector_addr, expr);
+ if (pp != nullptr) {
+ lbAddr res = *pp;
+ map_remove(&p->selector_addr, expr);
+ return res;
+ }
+ }
+ lbAddr addr = lb_build_addr_internal(p, expr);
+ if (expr->state_flags & StateFlag_SelectorCallExpr) {
+ map_set(&p->selector_addr, expr, addr);
+ }
+ return addr;
+}
+
+
+lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
case_ast_node(i, Implicit, expr);
lbAddr v = {};
@@ -3263,9 +3494,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorExpr, expr);
- Ast *sel = unparen_expr(se->selector);
- if (sel->kind == Ast_Ident) {
- String selector = sel->Ident.token.string;
+ Ast *sel_node = unparen_expr(se->selector);
+ if (sel_node->kind == Ast_Ident) {
+ String selector = sel_node->Ident.token.string;
TypeAndValue tav = type_and_value_of_expr(se->expr);
if (tav.mode == Addressing_Invalid) {
@@ -3280,7 +3511,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
- GB_PANIC("Unreachable");
+ Selection sel = lookup_field(tav.type, selector, true);
+ if (sel.pseudo_field) {
+ GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
+ }
+ GB_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
@@ -3307,6 +3543,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr);
+ if (sel.pseudo_field) {
+ GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ Entity *e = entity_of_node(sel_node);
+ return lb_addr(lb_find_value_from_entity(p->module, e));
+ }
{
lbAddr addr = lb_build_addr(p, se->expr);
@@ -3370,9 +3611,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorCallExpr, expr);
- GB_ASSERT(se->modified_call);
- TypeAndValue tav = type_and_value_of_expr(expr);
- GB_ASSERT(tav.mode != Addressing_Invalid);
lbValue e = lb_build_expr(p, expr);
return lb_addr(lb_address_from_load_or_generate_local(p, e));
case_end;
@@ -3460,7 +3698,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
- lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+ lbAddr map_addr = lb_build_addr(p, ie->expr);
+ lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
@@ -3469,7 +3708,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
- return lb_addr_map(map_val, key, t, result_type);
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+ return lb_addr_map(map_ptr, key, t, result_type);
}
switch (t->kind) {
@@ -4531,6 +4771,102 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
break;
}
+ case Type_SimdVector: {
+ if (cl->elems.count > 0) {
+ lbValue vector_value = lb_const_value(p->module, type, exact_value_compound(expr));
+ defer (lb_addr_store(p, v, vector_value));
+
+ auto temp_data = array_make(temporary_allocator(), 0, cl->elems.count);
+
+ // NOTE(bill): Separate value, store into their own chunks
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind == Ast_FieldValue) {
+ ast_node(fv, FieldValue, elem);
+ if (lb_is_elem_const(fv->value, et)) {
+ continue;
+ }
+ if (is_ast_range(fv->field)) {
+ ast_node(ie, BinaryExpr, fv->field);
+ TypeAndValue lo_tav = ie->left->tav;
+ TypeAndValue hi_tav = ie->right->tav;
+ GB_ASSERT(lo_tav.mode == Addressing_Constant);
+ GB_ASSERT(hi_tav.mode == Addressing_Constant);
+
+ TokenKind op = ie->op.kind;
+ i64 lo = exact_value_to_i64(lo_tav.value);
+ i64 hi = exact_value_to_i64(hi_tav.value);
+ if (op != Token_RangeHalf) {
+ hi += 1;
+ }
+
+ lbValue value = lb_build_expr(p, fv->value);
+
+ for (i64 k = lo; k < hi; k++) {
+ lbCompoundLitElemTempData data = {};
+ data.value = value;
+ data.elem_index = cast(i32)k;
+ array_add(&temp_data, data);
+ }
+
+ } else {
+ auto tav = fv->field->tav;
+ GB_ASSERT(tav.mode == Addressing_Constant);
+ i64 index = exact_value_to_i64(tav.value);
+
+ lbValue value = lb_build_expr(p, fv->value);
+ lbCompoundLitElemTempData data = {};
+ data.value = lb_emit_conv(p, value, et);
+ data.expr = fv->value;
+ data.elem_index = cast(i32)index;
+ array_add(&temp_data, data);
+ }
+
+ } else {
+ if (lb_is_elem_const(elem, et)) {
+ continue;
+ }
+ lbCompoundLitElemTempData data = {};
+ data.expr = elem;
+ data.elem_index = cast(i32)i;
+ array_add(&temp_data, data);
+ }
+ }
+
+
+ for_array(i, temp_data) {
+ lbValue field_expr = temp_data[i].value;
+ Ast *expr = temp_data[i].expr;
+
+ auto prev_hint = lb_set_copy_elision_hint(p, lb_addr(temp_data[i].gep), expr);
+
+ if (field_expr.value == nullptr) {
+ field_expr = lb_build_expr(p, expr);
+ }
+ Type *t = field_expr.type;
+ GB_ASSERT(t->kind != Type_Tuple);
+ lbValue ev = lb_emit_conv(p, field_expr, et);
+
+ if (!p->copy_elision_hint.used) {
+ temp_data[i].value = ev;
+ }
+
+ lb_reset_copy_elision_hint(p, prev_hint);
+ }
+
+
+ // TODO(bill): reduce the need for individual `insertelement` if a `shufflevector`
+ // might be a better option
+
+ for_array(i, temp_data) {
+ if (temp_data[i].value.value != nullptr) {
+ LLVMValueRef index = lb_const_int(p->module, t_u32, temp_data[i].elem_index).value;
+ vector_value.value = LLVMBuildInsertElement(p->builder, vector_value.value, temp_data[i].value.value, index, "");
+ }
+ }
+ }
+ break;
+ }
}
return v;
@@ -4568,7 +4904,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 17eeb0bea..b61439238 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -67,11 +67,12 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->equal_procs, a);
map_init(&m->hasher_procs, a);
array_init(&m->procedures_to_generate, a, 0, 1024);
- array_init(&m->foreign_library_paths, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16);
-
map_init(&m->debug_values, a);
array_init(&m->debug_incomplete_types, a, 0, 1024);
+
+ string_map_init(&m->objc_classes, a);
+ string_map_init(&m->objc_selectors, a);
}
bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -84,7 +85,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
-
String init_fullpath = c->parser->init_fullpath;
if (build_context.out_filepath.len == 0) {
@@ -124,6 +124,11 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2);
map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024);
+
+ mutex_init(&gen->foreign_mutex);
+ array_init(&gen->foreign_libraries, heap_allocator(), 0, 1024);
+ ptr_set_init(&gen->foreign_libraries_set, heap_allocator(), 1024);
+
if (USE_SEPARATE_MODULES) {
for_array(i, gen->info->packages.entries) {
AstPackage *pkg = gen->info->packages.entries[i].value;
@@ -216,6 +221,17 @@ LLVMValueRef llvm_one(lbModule *m) {
return LLVMConstInt(lb_type(m, t_i32), 1, false);
}
+LLVMValueRef llvm_alloca(lbProcedure *p, LLVMTypeRef llvm_type, isize alignment, char const *name) {
+ LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
+
+ LLVMValueRef val = LLVMBuildAlloca(p->builder, llvm_type, name);
+ LLVMSetAlignment(val, cast(unsigned int)alignment);
+
+ LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+
+ return val;
+}
+
lbValue lb_zero(lbModule *m, Type *t) {
lbValue v = {};
v.value = LLVMConstInt(lb_type(m, t), 0, false);
@@ -271,6 +287,10 @@ lbAddr lb_addr(lbValue addr) {
lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT(is_type_map(mt));
+
lbAddr v = {lbAddr_Map, addr};
v.map.key = map_key;
v.map.type = map_type;
@@ -832,6 +852,16 @@ bool lb_is_type_proc_recursive(Type *t) {
void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
GB_ASSERT(value.value != nullptr);
Type *a = type_deref(ptr.type);
+
+ if (LLVMIsNull(value.value)) {
+ LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value));
+ if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
+ LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
+ } else {
+ lb_mem_zero_ptr(p, ptr.value, a, 1);
+ }
+ return;
+ }
if (is_type_boolean(a)) {
// NOTE(bill): There are multiple sized booleans, thus force a conversion (if necessarily)
value = lb_emit_conv(p, value, a);
@@ -841,6 +871,20 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
GB_ASSERT_MSG(are_types_identical(ca, core_type(value.type)), "%s != %s", type_to_string(a), type_to_string(value.type));
}
+ enum {MAX_STORE_SIZE = 64};
+
+ if (LLVMIsALoadInst(value.value) && lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
+ LLVMValueRef dst_ptr = ptr.value;
+ LLVMValueRef src_ptr = LLVMGetOperand(value.value, 0);
+ src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
+
+ LLVMBuildMemMove(p->builder,
+ dst_ptr, 1,
+ src_ptr, 1,
+ LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
+ return;
+ }
+
if (lb_is_type_proc_recursive(a)) {
// NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be
// stored as regular pointer with no procedure information
@@ -1169,10 +1213,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
- lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ Type *pt = base_type(type_deref(parent.type));
+ GB_ASSERT(pt->kind == Type_Union);
+ if (pt->Union.kind == UnionType_shared_nil) {
+ lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
+ lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
+ lbBlock *done = lb_create_block(p, "shared_nil.done");
- lb_emit_store(p, underlying, variant);
- lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
+ lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
+
+ lb_start_block(p, if_nil);
+ lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, if_not_nil);
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, done);
+
+
+ } else {
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ }
}
@@ -1598,8 +1667,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return llvm_type;
}
llvm_type = LLVMStructCreateNamed(ctx, name);
+ LLVMTypeRef found_val = *found;
map_set(&m->types, type, llvm_type);
- lb_clone_struct_type(llvm_type, *found);
+ lb_clone_struct_type(llvm_type, found_val);
return llvm_type;
}
}
@@ -1892,11 +1962,12 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
if (e->flags & EntityFlag_CVarArg) {
continue;
}
-
Type *e_type = reduce_tuple_to_single_type(e->type);
LLVMTypeRef param_type = nullptr;
- if (is_type_boolean(e_type) &&
+ if (e->flags & EntityFlag_ByPtr) {
+ param_type = lb_type(m, alloc_type_pointer(e_type));
+ } else if (is_type_boolean(e_type) &&
type_size_of(e_type) <= 1) {
param_type = LLVMInt1TypeInContext(m->ctx);
} else {
@@ -2265,13 +2336,11 @@ general_end:;
return loaded_val;
} else {
GB_ASSERT(p->decl_block != p->curr_block);
- LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
- LLVMValueRef ptr = LLVMBuildAlloca(p->builder, dst_type, "");
- LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 4);
- LLVMSetAlignment(ptr, cast(unsigned)max_align);
+
+ LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), "");
LLVMBuildStore(p->builder, val, nptr);
@@ -2304,7 +2373,10 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
+ LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
+ LLVMSetAlignment(global_data, 1);
+ LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2346,7 +2418,10 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
+ LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
+ LLVMSetAlignment(global_data, 1);
+ LLVMSetGlobalConstant(global_data, true);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2445,7 +2520,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
return {};
}
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);
@@ -2471,6 +2546,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
+
+ if (entity_) *entity_ = e;
+
return lb_addr(g);
}
@@ -2579,7 +2657,8 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g.value, LLVMInternalLinkage);
+ LLVMSetLinkage(g.value, LLVMPrivateLinkage);
+ LLVMSetUnnamedAddress(g.value, LLVMGlobalUnnamedAddr);
string_map_set(&m->members, s, g);
return g;
}
@@ -2591,6 +2670,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
+ // Use to signal not to do compile time short circuit for consts
+ lbValue no_comptime_short_circuit = {};
+
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2598,7 +2680,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
- return lb_build_cond(p, ue->expr, false_block, true_block);
+ lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
+ if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+ return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+ }
+ return no_comptime_short_circuit;
}
case_end;
@@ -2607,12 +2693,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
}
case_end;
}
@@ -2642,34 +2730,29 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p
}
LLVMTypeRef llvm_type = lb_type(p->module, type);
- LLVMValueRef ptr = LLVMBuildAlloca(p->builder, llvm_type, name);
unsigned alignment = cast(unsigned)gb_max(type_align_of(type), lb_alignof(llvm_type));
if (is_type_matrix(type)) {
alignment *= 2; // NOTE(bill): Just in case
}
- LLVMSetAlignment(ptr, alignment);
-
- LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
+ LLVMValueRef ptr = llvm_alloca(p, llvm_type, alignment, name);
if (!zero_init && !force_no_init) {
// If there is any padding of any kind, just zero init regardless of zero_init parameter
LLVMTypeKind kind = LLVMGetTypeKind(llvm_type);
+ if (kind == LLVMArrayTypeKind) {
+ kind = LLVMGetTypeKind(lb_type(p->module, core_array_type(type)));
+ }
+
if (kind == LLVMStructTypeKind) {
i64 sz = type_size_of(type);
if (type_size_of_struct_pretend_is_packed(type) != sz) {
zero_init = true;
}
- } else if (kind == LLVMArrayTypeKind) {
- zero_init = true;
}
}
- if (zero_init) {
- lb_mem_zero_ptr(p, ptr, type, alignment);
- }
-
lbValue val = {};
val.value = ptr;
val.type = alloc_type_pointer(type);
@@ -2679,6 +2762,10 @@ lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero_init, i32 p
lb_add_debug_local_variable(p, ptr, type, e->token);
}
+ if (zero_init) {
+ lb_mem_zero_ptr(p, ptr, type, alignment);
+ }
+
return lb_addr(val);
}
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 8f1c7ad59..6b80b21d6 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -56,7 +56,7 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
#endif
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
- if (optimization_level == 0 && build_context.ODIN_DEBUG) {
+ if (false && optimization_level == 0 && build_context.ODIN_DEBUG) {
LLVMAddMergedLoadStoreMotionPass(fpm);
} else {
LLVMAddPromoteMemoryToRegisterPass(fpm);
@@ -380,6 +380,43 @@ void llvm_delete_function(LLVMValueRef func) {
LLVMDeleteFunction(func);
}
+void lb_append_to_compiler_used(lbModule *m, LLVMValueRef func) {
+ LLVMValueRef global = LLVMGetNamedGlobal(m->mod, "llvm.compiler.used");
+
+ LLVMValueRef *constants;
+ int operands = 1;
+
+ if (global != NULL) {
+ GB_ASSERT(LLVMIsAGlobalVariable(global));
+ LLVMValueRef initializer = LLVMGetInitializer(global);
+
+ GB_ASSERT(LLVMIsAConstantArray(initializer));
+ operands = LLVMGetNumOperands(initializer) + 1;
+ constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, operands);
+
+ for (int i = 0; i < operands - 1; i++) {
+ LLVMValueRef operand = LLVMGetOperand(initializer, i);
+ GB_ASSERT(LLVMIsAConstant(operand));
+ constants[i] = operand;
+ }
+
+ LLVMDeleteGlobal(global);
+ } else {
+ constants = gb_alloc_array(temporary_allocator(), LLVMValueRef, 1);
+ }
+
+ LLVMTypeRef Int8PtrTy = LLVMPointerType(LLVMInt8TypeInContext(m->ctx), 0);
+ LLVMTypeRef ATy = LLVMArrayType(Int8PtrTy, operands);
+
+ constants[operands - 1] = LLVMConstBitCast(func, Int8PtrTy);
+ LLVMValueRef initializer = LLVMConstArray(Int8PtrTy, constants, operands);
+
+ global = LLVMAddGlobal(m->mod, ATy, "llvm.compiler.used");
+ LLVMSetLinkage(global, LLVMAppendingLinkage);
+ LLVMSetSection(global, "llvm.metadata");
+ LLVMSetInitializer(global, initializer);
+}
+
void lb_run_remove_unused_function_pass(lbModule *m) {
isize removal_count = 0;
isize pass_count = 0;
@@ -415,6 +452,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
Entity *e = *found;
bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require;
if (is_required) {
+ lb_append_to_compiler_used(m, curr_func);
continue;
}
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index b35c6c304..0b0c0794b 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -57,11 +57,12 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
+
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
if (!entity->Procedure.is_foreign) {
- GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked);
+ GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
}
String link_name = {};
@@ -112,6 +113,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
p->branch_blocks.allocator = a;
p->context_stack.allocator = a;
p->scope_stack.allocator = a;
+ map_init(&p->selector_values, a, 0);
+ map_init(&p->selector_addr, a, 0);
if (p->is_foreign) {
lb_add_foreign_library_path(p->module, entity->Procedure.foreign_library);
@@ -134,43 +137,57 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lb_add_attribute_to_proc(m, p->value, "naked");
}
- switch (p->inlining) {
- case ProcInlining_inline:
- lb_add_attribute_to_proc(m, p->value, "alwaysinline");
- break;
- case ProcInlining_no_inline:
+ if (!entity->Procedure.is_foreign && build_context.disable_red_zone) {
+ lb_add_attribute_to_proc(m, p->value, "noredzone");
+ }
+
+ if (build_context.optimization_level == 0 && build_context.ODIN_DEBUG) {
lb_add_attribute_to_proc(m, p->value, "noinline");
- break;
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ } else {
+ switch (p->inlining) {
+ case ProcInlining_inline:
+ lb_add_attribute_to_proc(m, p->value, "alwaysinline");
+ break;
+ case ProcInlining_no_inline:
+ lb_add_attribute_to_proc(m, p->value, "noinline");
+ break;
+ }
+
+ switch (entity->Procedure.optimization_mode) {
+ case ProcedureOptimizationMode_None:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Minimal:
+ lb_add_attribute_to_proc(m, p->value, "optnone");
+ break;
+ case ProcedureOptimizationMode_Size:
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
+ case ProcedureOptimizationMode_Speed:
+ // TODO(bill): handle this correctly
+ lb_add_attribute_to_proc(m, p->value, "optsize");
+ break;
+ }
+ }
+
+ if (!entity->Procedure.target_feature_disabled &&
+ entity->Procedure.target_feature.len != 0) {
+ auto features = split_by_comma(entity->Procedure.target_feature);
+ for_array(i, features) {
+ String feature = features[i];
+ LLVMAttributeRef ref = LLVMCreateStringAttribute(
+ m->ctx,
+ cast(char const *)feature.text, cast(unsigned)feature.len,
+ "", 0);
+ LLVMAddAttributeAtIndex(p->value, LLVMAttributeIndex_FunctionIndex, ref);
+ }
}
if (entity->flags & EntityFlag_Cold) {
lb_add_attribute_to_proc(m, p->value, "cold");
}
- switch (entity->Procedure.optimization_mode) {
- case ProcedureOptimizationMode_None:
- lb_add_attribute_to_proc(m, p->value, "optnone");
- break;
- case ProcedureOptimizationMode_Minimal:
- lb_add_attribute_to_proc(m, p->value, "optnone");
- break;
- case ProcedureOptimizationMode_Size:
- lb_add_attribute_to_proc(m, p->value, "optsize");
- break;
- case ProcedureOptimizationMode_Speed:
- // TODO(bill): handle this correctly
- lb_add_attribute_to_proc(m, p->value, "optsize");
- break;
- }
-
-
-
- // lbCallingConventionKind cc_kind = lbCallingConvention_C;
- // // TODO(bill): Clean up this logic
- // if (build_context.metrics.os != TargetOs_js) {
- // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
- // }
- // LLVMSetFunctionCallConv(p->value, cc_kind);
lbValue proc_value = {p->value, p->type};
lb_add_entity(m, entity, proc_value);
lb_add_member(m, p->name, proc_value);
@@ -192,7 +209,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
GB_ASSERT(entity->kind == Entity_Procedure);
String link_name = entity->Procedure.link_name;
if (entity->flags & EntityFlag_CustomLinkName &&
- link_name != "") {
+ link_name != "") {
if (string_starts_with(link_name, str_lit("__"))) {
LLVMSetLinkage(p->value, LLVMExternalLinkage);
} else {
@@ -203,12 +220,12 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
}
}
lb_set_linkage_from_entity_flags(p->module, p->value, entity->flags);
-
-
+
+
if (p->is_foreign) {
lb_set_wasm_import_attributes(p->value, entity, p->name);
}
-
+
// NOTE(bill): offset==0 is the return value
isize offset = 1;
@@ -283,7 +300,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
if (p->body != nullptr) {
// String debug_name = entity->token.string.text;
String debug_name = p->name;
-
+
p->debug_info = LLVMDIBuilderCreateFunction(m->debug_builder, scope,
cast(char const *)debug_name.text, debug_name.len,
cast(char const *)p->name.text, p->name.len,
@@ -418,6 +435,40 @@ void lb_start_block(lbProcedure *p, lbBlock *b) {
p->curr_block = b;
}
+void lb_set_debug_position_to_procedure_begin(lbProcedure *p) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ TokenPos pos = {};
+ if (p->body != nullptr) {
+ pos = ast_token(p->body).pos;
+ } else if (p->type_expr != nullptr) {
+ pos = ast_token(p->type_expr).pos;
+ } else if (p->entity != nullptr) {
+ pos = p->entity->token.pos;
+ }
+ if (pos.file_id != 0) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos));
+ }
+}
+
+void lb_set_debug_position_to_procedure_end(lbProcedure *p) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ TokenPos pos = {};
+ if (p->body != nullptr) {
+ pos = ast_end_token(p->body).pos;
+ } else if (p->type_expr != nullptr) {
+ pos = ast_end_token(p->type_expr).pos;
+ } else if (p->entity != nullptr) {
+ pos = p->entity->token.pos;
+ }
+ if (pos.file_id != 0) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos));
+ }
+}
+
void lb_begin_procedure_body(lbProcedure *p) {
DeclInfo *decl = decl_info_of_entity(p->entity);
if (decl != nullptr) {
@@ -450,7 +501,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
- e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+ e->flags |= EntityFlag_NoAlias;
return_ptr_value.value = LLVMGetParam(p->value, 0);
LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len);
@@ -473,34 +524,43 @@ void lb_begin_procedure_body(lbProcedure *p) {
}
lbArgType *arg_type = &ft->args[param_index];
+ defer (param_index += 1);
+
if (arg_type->kind == lbArg_Ignore) {
continue;
} else if (arg_type->kind == lbArg_Direct) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
LLVMTypeRef param_type = lb_type(p->module, e->type);
- LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index);
-
- value = OdinLLVMBuildTransmute(p, value, param_type);
+ LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index);
+ LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type);
lbValue param = {};
param.value = value;
param.type = e->type;
lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+ GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
lb_add_entity(p->module, e, ptr);
- // lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
+
+ lbBlock *block = p->decl_block;
+ if (original_value != value) {
+ block = p->curr_block;
+ }
+ LLVMValueRef debug_storage_value = value;
+ if (original_value != value && LLVMIsALoadInst(value)) {
+ debug_storage_value = LLVMGetOperand(value, 0);
+ }
+ lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block);
}
} else if (arg_type->kind == lbArg_Indirect) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
lbValue ptr = {};
ptr.value = LLVMGetParam(p->value, param_offset+param_index);
ptr.type = alloc_type_pointer(e->type);
-
lb_add_entity(p->module, e, ptr);
- // lb_add_debug_local_variable(p, ptr.value, e->type, e->token);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block);
}
}
- param_index += 1;
}
}
@@ -540,29 +600,21 @@ void lb_begin_procedure_body(lbProcedure *p) {
lb_push_context_onto_stack_from_implicit_parameter(p);
}
- lb_start_block(p, p->entry_block);
-
+ lb_set_debug_position_to_procedure_begin(p);
if (p->debug_info != nullptr) {
- TokenPos pos = {};
- if (p->body != nullptr) {
- pos = ast_token(p->body).pos;
- } else if (p->type_expr != nullptr) {
- pos = ast_token(p->type_expr).pos;
- } else if (p->entity != nullptr) {
- pos = p->entity->token.pos;
- }
- if (pos.file_id != 0) {
- LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_token_pos(p, pos));
- }
-
if (p->context_stack.count != 0) {
+ p->curr_block = p->decl_block;
lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p));
}
}
+
+ lb_start_block(p, p->entry_block);
}
void lb_end_procedure_body(lbProcedure *p) {
+ lb_set_debug_position_to_procedure_begin(p);
+
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
LLVMBuildBr(p->builder, p->entry_block->block);
LLVMPositionBuilderAtEnd(p->builder, p->curr_block->block);
@@ -574,6 +626,7 @@ void lb_end_procedure_body(lbProcedure *p) {
instr = LLVMGetLastInstruction(p->curr_block->block);
if (!lb_is_instr_terminating(instr)) {
lb_emit_defer_stmts(p, lbDeferExit_Return, nullptr);
+ lb_set_debug_position_to_procedure_end(p);
LLVMBuildRetVoid(p->builder);
}
}
@@ -817,12 +870,6 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args,
GB_ASSERT(pt->kind == Type_Proc);
Type *results = pt->Proc.results;
- if (p->entity != nullptr) {
- if (p->entity->flags & EntityFlag_Disabled) {
- return {};
- }
- }
-
lbAddr context_ptr = {};
if (pt->Proc.calling_convention == ProcCC_Odin) {
context_ptr = lb_find_or_generate_context_ptr(p);
@@ -976,10 +1023,466 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array const &args,
return result;
}
+LLVMValueRef llvm_splat_float(i64 count, LLVMTypeRef type, f64 value) {
+ LLVMValueRef v = LLVMConstReal(type, value);
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ for (i64 i = 0; i < count; i++) {
+ values[i] = v;
+ }
+ return LLVMConstVector(values, cast(unsigned)count);
+}
+LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) {
+ LLVMValueRef v = LLVMConstInt(type, value, is_signed);
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ for (i64 i = 0; i < count; i++) {
+ values[i] = v;
+ }
+ return LLVMConstVector(values, cast(unsigned)count);
+}
+
+
+lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId builtin_id) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+
+ lbValue res = {};
+ res.type = tv.type;
+
+ lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);
+ lbValue arg1 = {}; if (ce->args.count > 1) arg1 = lb_build_expr(p, ce->args[1]);
+ lbValue arg2 = {}; if (ce->args.count > 2) arg2 = lb_build_expr(p, ce->args[2]);
+
+ Type *elem = base_array_type(arg0.type);
+
+ bool is_float = is_type_float(elem);
+ bool is_signed = !is_type_unsigned(elem);
+
+ LLVMOpcode op_code = cast(LLVMOpcode)0;
+
+ switch (builtin_id) {
+ case BuiltinProc_simd_add:
+ case BuiltinProc_simd_sub:
+ case BuiltinProc_simd_mul:
+ case BuiltinProc_simd_div:
+ case BuiltinProc_simd_rem:
+ if (is_float) {
+ switch (builtin_id) {
+ case BuiltinProc_simd_add: op_code = LLVMFAdd; break;
+ case BuiltinProc_simd_sub: op_code = LLVMFSub; break;
+ case BuiltinProc_simd_mul: op_code = LLVMFMul; break;
+ case BuiltinProc_simd_div: op_code = LLVMFDiv; break;
+ }
+ } else {
+ switch (builtin_id) {
+ case BuiltinProc_simd_add: op_code = LLVMAdd; break;
+ case BuiltinProc_simd_sub: op_code = LLVMSub; break;
+ case BuiltinProc_simd_mul: op_code = LLVMMul; break;
+ case BuiltinProc_simd_div:
+ if (is_signed) {
+ op_code = LLVMSDiv;
+ } else {
+ op_code = LLVMUDiv;
+ }
+ break;
+ case BuiltinProc_simd_rem:
+ if (is_signed) {
+ op_code = LLVMSRem;
+ } else {
+ op_code = LLVMURem;
+ }
+ break;
+ }
+ }
+ if (op_code) {
+ res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
+ return res;
+ }
+ break;
+ case BuiltinProc_simd_shl: // Odin logic
+ case BuiltinProc_simd_shr: // Odin logic
+ case BuiltinProc_simd_shl_masked: // C logic
+ case BuiltinProc_simd_shr_masked: // C logic
+ {
+ i64 sz = type_size_of(elem);
+ GB_ASSERT(arg0.type->kind == Type_SimdVector);
+
+ i64 count = arg0.type->SimdVector.count;
+ Type *elem1 = base_array_type(arg1.type);
+
+ bool is_masked = false;
+ switch (builtin_id) {
+ case BuiltinProc_simd_shl: op_code = LLVMShl; is_masked = false; break;
+ case BuiltinProc_simd_shr: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = false; break;
+ case BuiltinProc_simd_shl_masked: op_code = LLVMShl; is_masked = true; break;
+ case BuiltinProc_simd_shr_masked: op_code = is_signed ? LLVMAShr : LLVMLShr; is_masked = true; break;
+ }
+ if (op_code) {
+ LLVMValueRef bits = llvm_splat_int(count, lb_type(m, elem1), sz*8 - 1);
+ if (is_masked) {
+ // C logic
+ LLVMValueRef shift = LLVMBuildAnd(p->builder, arg1.value, bits, "");
+ res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, shift, "");
+ } else {
+ // Odin logic
+ LLVMValueRef zero = lb_const_nil(m, arg1.type).value;
+ LLVMValueRef mask = LLVMBuildICmp(p->builder, LLVMIntULE, arg1.value, bits, "");
+ LLVMValueRef shift = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSelect(p->builder, mask, shift, zero, "");
+ }
+ return res;
+ }
+ }
+ break;
+ case BuiltinProc_simd_and:
+ case BuiltinProc_simd_or:
+ case BuiltinProc_simd_xor:
+ case BuiltinProc_simd_and_not:
+ switch (builtin_id) {
+ case BuiltinProc_simd_and: op_code = LLVMAnd; break;
+ case BuiltinProc_simd_or: op_code = LLVMOr; break;
+ case BuiltinProc_simd_xor: op_code = LLVMXor; break;
+ case BuiltinProc_simd_and_not:
+ op_code = LLVMAnd;
+ arg1.value = LLVMBuildNot(p->builder, arg1.value, "");
+ break;
+ }
+ if (op_code) {
+ res.value = LLVMBuildBinOp(p->builder, op_code, arg0.value, arg1.value, "");
+ return res;
+ }
+ break;
+ case BuiltinProc_simd_neg:
+ if (is_float) {
+ res.value = LLVMBuildFNeg(p->builder, arg0.value, "");
+ } else {
+ res.value = LLVMBuildNeg(p->builder, arg0.value, "");
+ }
+ return res;
+ case BuiltinProc_simd_abs:
+ if (is_float) {
+ LLVMValueRef pos = arg0.value;
+ LLVMValueRef neg = LLVMBuildFNeg(p->builder, pos, "");
+ LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, pos, neg, "");
+ res.value = LLVMBuildSelect(p->builder, cond, pos, neg, "");
+ } else {
+ LLVMValueRef pos = arg0.value;
+ LLVMValueRef neg = LLVMBuildNeg(p->builder, pos, "");
+ LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, pos, neg, "");
+ res.value = LLVMBuildSelect(p->builder, cond, pos, neg, "");
+ }
+ return res;
+ case BuiltinProc_simd_min:
+ if (is_float) {
+ LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOLT, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ } else {
+ LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSLT : LLVMIntULT, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ }
+ return res;
+ case BuiltinProc_simd_max:
+ if (is_float) {
+ LLVMValueRef cond = LLVMBuildFCmp(p->builder, LLVMRealOGT, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ } else {
+ LLVMValueRef cond = LLVMBuildICmp(p->builder, is_signed ? LLVMIntSGT : LLVMIntUGT, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSelect(p->builder, cond, arg0.value, arg1.value, "");
+ }
+ return res;
+ 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:
+ if (is_float) {
+ LLVMRealPredicate pred = cast(LLVMRealPredicate)0;
+ switch (builtin_id) {
+ case BuiltinProc_simd_lanes_eq: pred = LLVMRealOEQ; break;
+ case BuiltinProc_simd_lanes_ne: pred = LLVMRealONE; break;
+ case BuiltinProc_simd_lanes_lt: pred = LLVMRealOLT; break;
+ case BuiltinProc_simd_lanes_le: pred = LLVMRealOLE; break;
+ case BuiltinProc_simd_lanes_gt: pred = LLVMRealOGT; break;
+ case BuiltinProc_simd_lanes_ge: pred = LLVMRealOGE; break;
+ }
+ if (pred) {
+ res.value = LLVMBuildFCmp(p->builder, pred, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), "");
+ return res;
+ }
+ } else {
+ LLVMIntPredicate pred = cast(LLVMIntPredicate)0;
+ switch (builtin_id) {
+ case BuiltinProc_simd_lanes_eq: pred = LLVMIntEQ; break;
+ case BuiltinProc_simd_lanes_ne: pred = LLVMIntNE; break;
+ case BuiltinProc_simd_lanes_lt: pred = is_signed ? LLVMIntSLT :LLVMIntULT; break;
+ case BuiltinProc_simd_lanes_le: pred = is_signed ? LLVMIntSLE :LLVMIntULE; break;
+ case BuiltinProc_simd_lanes_gt: pred = is_signed ? LLVMIntSGT :LLVMIntUGT; break;
+ case BuiltinProc_simd_lanes_ge: pred = is_signed ? LLVMIntSGE :LLVMIntUGE; break;
+ }
+ if (pred) {
+ res.value = LLVMBuildICmp(p->builder, pred, arg0.value, arg1.value, "");
+ res.value = LLVMBuildSExtOrBitCast(p->builder, res.value, lb_type(m, tv.type), "");
+ return res;
+ }
+ }
+ break;
+
+ case BuiltinProc_simd_extract:
+ res.value = LLVMBuildExtractElement(p->builder, arg0.value, arg1.value, "");
+ return res;
+ case BuiltinProc_simd_replace:
+ res.value = LLVMBuildInsertElement(p->builder, arg0.value, arg2.value, arg1.value, "");
+ return res;
+
+ case BuiltinProc_simd_reduce_add_ordered:
+ case BuiltinProc_simd_reduce_mul_ordered:
+ {
+ LLVMTypeRef llvm_elem = lb_type(m, elem);
+ LLVMValueRef args[2] = {};
+ isize args_count = 0;
+
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_reduce_add_ordered:
+ if (is_float) {
+ name = "llvm.vector.reduce.fadd";
+ args[args_count++] = LLVMConstReal(llvm_elem, 0.0);
+ } else {
+ name = "llvm.vector.reduce.add";
+ }
+ break;
+ case BuiltinProc_simd_reduce_mul_ordered:
+ if (is_float) {
+ name = "llvm.vector.reduce.fmul";
+ args[args_count++] = LLVMConstReal(llvm_elem, 1.0);
+ } else {
+ name = "llvm.vector.reduce.mul";
+ }
+ break;
+ }
+ args[args_count++] = arg0.value;
+
+
+ LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ res.value = LLVMBuildCall(p->builder, ip, args, cast(unsigned)args_count, "");
+ return res;
+ }
+ case BuiltinProc_simd_reduce_min:
+ case BuiltinProc_simd_reduce_max:
+ case BuiltinProc_simd_reduce_and:
+ case BuiltinProc_simd_reduce_or:
+ case BuiltinProc_simd_reduce_xor:
+ {
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_reduce_min:
+ if (is_float) {
+ name = "llvm.vector.reduce.fmin";
+ } else if (is_signed) {
+ name = "llvm.vector.reduce.smin";
+ } else {
+ name = "llvm.vector.reduce.umin";
+ }
+ break;
+ case BuiltinProc_simd_reduce_max:
+ if (is_float) {
+ name = "llvm.vector.reduce.fmax";
+ } else if (is_signed) {
+ name = "llvm.vector.reduce.smax";
+ } else {
+ name = "llvm.vector.reduce.umax";
+ }
+ break;
+ case BuiltinProc_simd_reduce_and: name = "llvm.vector.reduce.and"; break;
+ case BuiltinProc_simd_reduce_or: name = "llvm.vector.reduce.or"; break;
+ case BuiltinProc_simd_reduce_xor: name = "llvm.vector.reduce.xor"; break;
+ }
+ LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = arg0.value;
+
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+ case BuiltinProc_simd_shuffle:
+ {
+ Type *vt = arg0.type;
+ GB_ASSERT(vt->kind == Type_SimdVector);
+
+ i64 indices_count = ce->args.count-2;
+ i64 max_count = vt->SimdVector.count*2;
+ GB_ASSERT(indices_count <= max_count);
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, indices_count);
+ for (isize i = 0; i < indices_count; i++) {
+ lbValue idx = lb_build_expr(p, ce->args[i+2]);
+ GB_ASSERT(LLVMIsConstant(idx.value));
+ values[i] = idx.value;
+ }
+ LLVMValueRef indices = LLVMConstVector(values, cast(unsigned)indices_count);
+
+ res.value = LLVMBuildShuffleVector(p->builder, arg0.value, arg1.value, indices, "");
+ return res;
+ }
+
+ case BuiltinProc_simd_select:
+ {
+ LLVMValueRef cond = arg0.value;
+ LLVMValueRef x = lb_build_expr(p, ce->args[1]).value;
+ LLVMValueRef y = lb_build_expr(p, ce->args[2]).value;
+
+ cond = LLVMBuildICmp(p->builder, LLVMIntNE, cond, LLVMConstNull(LLVMTypeOf(cond)), "");
+ res.value = LLVMBuildSelect(p->builder, cond, x, y, "");
+ return res;
+ }
+
+ case BuiltinProc_simd_ceil:
+ case BuiltinProc_simd_floor:
+ case BuiltinProc_simd_trunc:
+ case BuiltinProc_simd_nearest:
+ {
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_ceil: name = "llvm.ceil"; break;
+ case BuiltinProc_simd_floor: name = "llvm.floor"; break;
+ case BuiltinProc_simd_trunc: name = "llvm.trunc"; break;
+ case BuiltinProc_simd_nearest: name = "llvm.nearbyint"; break;
+ }
+
+ LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = arg0.value;
+
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+ case BuiltinProc_simd_lanes_reverse:
+ {
+ i64 count = get_array_type_count(arg0.type);
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
+ for (i64 i = 0; i < count; i++) {
+ values[i] = LLVMConstInt(llvm_u32, count-1-i, false);
+ }
+ LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count);
+
+ LLVMValueRef v = arg0.value;
+ res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, "");
+ return res;
+ }
+
+ case BuiltinProc_simd_lanes_rotate_left:
+ case BuiltinProc_simd_lanes_rotate_right:
+ {
+
+ i64 count = get_array_type_count(arg0.type);
+ GB_ASSERT(is_power_of_two(count));
+ BigInt bi_count = {};
+ big_int_from_i64(&bi_count, count);
+
+ TypeAndValue const &tv = ce->args[1]->tav;
+ ExactValue val = exact_value_to_integer(tv.value);
+ GB_ASSERT(val.kind == ExactValue_Integer);
+ BigInt *bi = &val.value_integer;
+ if (builtin_id == BuiltinProc_simd_lanes_rotate_right) {
+ big_int_neg(bi, bi);
+ }
+ big_int_rem(bi, bi, &bi_count);
+ big_int_dealloc(&bi_count);
+
+ i64 left = big_int_to_i64(bi);
+
+ LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
+ LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
+ for (i64 i = 0; i < count; i++) {
+ u64 idx = cast(u64)(i+left) & cast(u64)(count-1);
+ values[i] = LLVMConstInt(llvm_u32, idx, false);
+ }
+ LLVMValueRef mask = LLVMConstVector(values, cast(unsigned)count);
+
+ LLVMValueRef v = arg0.value;
+ res.value = LLVMBuildShuffleVector(p->builder, v, v, mask, "");
+ return res;
+ }
+
+
+ case BuiltinProc_simd_add_sat:
+ case BuiltinProc_simd_sub_sat:
+ {
+ char const *name = nullptr;
+ switch (builtin_id) {
+ case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break;
+ case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break;
+ }
+
+ LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = arg0.value;
+ args[1] = arg1.value;
+
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+ case BuiltinProc_simd_clamp:
+ {
+ LLVMValueRef v = arg0.value;
+ LLVMValueRef min = arg1.value;
+ LLVMValueRef max = arg2.value;
+
+ if (is_float) {
+ v = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOLT, v, min, ""), min, v, "");
+ res.value = LLVMBuildSelect(p->builder, LLVMBuildFCmp(p->builder, LLVMRealOGT, v, max, ""), max, v, "");
+ } else if (is_signed) {
+ v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSLT, v, min, ""), min, v, "");
+ res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntSGT, v, max, ""), max, v, "");
+ } else {
+ v = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntULT, v, min, ""), min, v, "");
+ res.value = LLVMBuildSelect(p->builder, LLVMBuildICmp(p->builder, LLVMIntUGT, v, max, ""), max, v, "");
+ }
+ return res;
+ }
+
+ case BuiltinProc_simd_to_bits:
+ {
+ res.value = LLVMBuildBitCast(p->builder, arg0.value, lb_type(m, tv.type), "");
+ return res;
+ }
+
+ }
+ GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name));
+
+ return {};
+}
+
lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, BuiltinProcId id) {
ast_node(ce, CallExpr, expr);
+ if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
+ return lb_build_builtin_simd_proc(p, expr, tv, id);
+ }
+
switch (id) {
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
@@ -1040,7 +1543,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return lb_string_len(p, v);
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_len(p, v);
@@ -1066,7 +1569,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_PANIC("Unreachable");
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_cap(p, v);
@@ -1309,22 +1812,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_clamp:
return lb_emit_clamp(p, type_of_expr(expr),
- lb_build_expr(p, ce->args[0]),
- lb_build_expr(p, ce->args[1]),
- lb_build_expr(p, ce->args[2]));
+ lb_build_expr(p, ce->args[0]),
+ lb_build_expr(p, ce->args[1]),
+ lb_build_expr(p, ce->args[2]));
case BuiltinProc_soa_zip:
return lb_soa_zip(p, ce, tv);
case BuiltinProc_soa_unzip:
return lb_soa_unzip(p, ce, tv);
-
+
case BuiltinProc_transpose:
{
lbValue m = lb_build_expr(p, ce->args[0]);
return lb_emit_matrix_tranpose(p, m, tv.type);
}
-
+
case BuiltinProc_outer_product:
{
lbValue a = lb_build_expr(p, ce->args[0]);
@@ -1341,13 +1844,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_ASSERT(is_type_matrix(tv.type));
return lb_emit_arith_matrix(p, Token_Mul, a, b, tv.type, true);
}
-
+
case BuiltinProc_matrix_flatten:
{
lbValue m = lb_build_expr(p, ce->args[0]);
return lb_emit_matrix_flatten(p, m, tv.type);
}
-
+
// "Intrinsics"
case BuiltinProc_alloca:
@@ -1364,14 +1867,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_cpu_relax:
if (build_context.metrics.arch == TargetArch_i386 ||
- build_context.metrics.arch == TargetArch_amd64) {
+ build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {});
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {});
+ // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield`
+ // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true);
+ GB_ASSERT(the_asm != nullptr);
+ LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ // NOTE: default to something to prevent optimization
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
@@ -1400,14 +1911,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_read_cycle_counter:
{
- char const *name = "llvm.readcyclecounter";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
res.type = tv.type;
+
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+ bool has_side_effects = false;
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects);
+ GB_ASSERT(the_asm != nullptr);
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ char const *name = "llvm.readcyclecounter";
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+ res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ }
return res;
}
@@ -1510,12 +2030,37 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
+ case BuiltinProc_fused_mul_add:
+ {
+ Type *type = tv.type;
+ lbValue x = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), type);
+ lbValue y = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), type);
+ lbValue z = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), type);
+
+
+ char const *name = "llvm.fma";
+ LLVMTypeRef types[1] = {lb_type(p->module, type)};
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[3] = {};
+ args[0] = x.value;
+ args[1] = y.value;
+ args[2] = z.value;
+
+ lbValue res = {};
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ res.type = type;
+ return res;
+ }
+
case BuiltinProc_mem_copy:
{
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue src = lb_build_expr(p, ce->args[1]);
lbValue len = lb_build_expr(p, ce->args[2]);
-
+
lb_mem_copy_overlapping(p, dst, src, len, false);
return {};
}
@@ -1524,7 +2069,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue src = lb_build_expr(p, ce->args[1]);
lbValue len = lb_build_expr(p, ce->args[2]);
-
+
lb_mem_copy_non_overlapping(p, dst, src, len, false);
return {};
}
@@ -1583,36 +2128,34 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
-
- case BuiltinProc_atomic_fence:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
+ // TODO(bill): Which is correct?
+ case BuiltinProc_atomic_thread_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, "");
return {};
- case BuiltinProc_atomic_fence_acq:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
- return {};
- case BuiltinProc_atomic_fence_rel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
- return {};
- case BuiltinProc_atomic_fence_acqrel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
+ case BuiltinProc_atomic_signal_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, "");
return {};
case BuiltinProc_volatile_store:
+ case BuiltinProc_non_temporal_store:
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered: {
+ case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
- case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break;
- case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_non_temporal_store:
+ {
+ unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11);
+ LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false));
+ LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node));
+ }
+ break;
+ case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1621,19 +2164,24 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_volatile_load:
+ case BuiltinProc_non_temporal_load:
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered: {
+ case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
- case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break;
- case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_non_temporal_load:
+ {
+ unsigned kind_id = LLVMGetMDKindIDInContext(p->module->ctx, "nontemporal", 11);
+ LLVMMetadataRef node = LLVMValueAsMetadata(LLVMConstInt(lb_type(p->module, t_u32), 1, false));
+ LLVMSetMetadata(instr, kind_id, LLVMMetadataAsValue(p->module->ctx, node));
+ }
+ break;
+ break;
+ case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1642,7 +2190,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = type_deref(dst.type);
return res;
}
-
+
case BuiltinProc_unaligned_store:
{
lbValue dst = lb_build_expr(p, ce->args[0]);
@@ -1652,7 +2200,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_int, type_size_of(t)), false);
return {};
}
-
+
case BuiltinProc_unaligned_load:
{
lbValue src = lb_build_expr(p, ce->args[0]);
@@ -1663,40 +2211,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
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_exchange:
+ 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: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
@@ -1705,41 +2232,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMAtomicOrdering ordering = {};
switch (id) {
- case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
+ case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
}
lbValue res = {};
@@ -1748,24 +2254,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
- 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_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: {
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: {
lbValue address = lb_build_expr(p, ce->args[0]);
Type *elem = type_deref(address.type);
lbValue old_value = lb_build_expr(p, ce->args[1]);
@@ -1778,28 +2270,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMBool weak = false;
switch (id) {
- case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
}
// TODO(bill): Figure out how to make it weak
- LLVMBool single_threaded = weak;
+ LLVMBool single_threaded = false;
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
p->builder, address.value,
@@ -1808,6 +2286,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
failure_ordering,
single_threaded
);
+ LLVMSetWeak(value, weak);
if (tv.type->kind == Type_Tuple) {
Type *fix_typed = alloc_type_tuple();
@@ -1903,7 +2382,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = t;
return lb_emit_conv(p, res, t);
}
-
+
case BuiltinProc_prefetch_read_instruction:
case BuiltinProc_prefetch_read_data:
case BuiltinProc_prefetch_write_instruction:
@@ -1931,27 +2410,27 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
cache = 1;
break;
}
-
+
char const *name = "llvm.prefetch";
-
+
LLVMTypeRef types[1] = {lb_type(p->module, t_rawptr)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
+
LLVMTypeRef llvm_i32 = lb_type(p->module, t_i32);
LLVMValueRef args[4] = {};
args[0] = ptr.value;
args[1] = LLVMConstInt(llvm_i32, rw, false);
args[2] = LLVMConstInt(llvm_i32, locality, false);
args[3] = LLVMConstInt(llvm_i32, cache, false);
-
+
lbValue res = {};
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
res.type = nullptr;
return res;
}
-
+
case BuiltinProc___entry_point:
if (p->module->info->entry_point) {
lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point);
@@ -1969,22 +2448,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
arg = lb_emit_conv(p, arg, t_uintptr);
args[i] = arg.value;
}
-
+
LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr);
LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count);
for (unsigned i = 0; i < arg_count; i++) {
llvm_arg_types[i] = llvm_uintptr;
}
-
+
LLVMTypeRef func_type = LLVMFunctionType(llvm_uintptr, llvm_arg_types, arg_count, false);
-
+
LLVMValueRef inline_asm = nullptr;
-
+
switch (build_context.metrics.arch) {
case TargetArch_amd64:
{
GB_ASSERT(arg_count <= 7);
-
+
char asm_string[] = "syscall";
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
for (unsigned i = 0; i < arg_count; i++) {
@@ -2023,11 +2502,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case TargetArch_i386:
{
GB_ASSERT(arg_count <= 7);
-
+
char asm_string_default[] = "int $0x80";
char *asm_string = asm_string_default;
gbString constraints = gb_string_make(heap_allocator(), "={eax}");
-
+
for (unsigned i = 0; i < gb_min(arg_count, 6); i++) {
constraints = gb_string_appendc(constraints, ",{");
static char const *regs[] = {
@@ -2044,56 +2523,81 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
if (arg_count == 7) {
char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp";
asm_string = asm_string7;
-
+
constraints = gb_string_appendc(constraints, ",rm");
}
-
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
case TargetArch_arm64:
{
- GB_ASSERT(arg_count <= 7);
-
- if(build_context.metrics.os == TargetOs_darwin) {
- char asm_string[] = "svc #0x80";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x16",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
+ GB_ASSERT(arg_count <= 7);
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
- } else {
- char asm_string[] = "svc #0";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x8",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
+ if(build_context.metrics.os == TargetOs_darwin) {
+ char asm_string[] = "svc #0x80";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x16",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
- }
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ } else {
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x8",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
+ }
+ break;
+ case TargetArch_arm32:
+ {
+ // TODO(bill): Check this is correct
+ GB_ASSERT(arg_count <= 7);
+
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={r0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "r8",
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
default:
@@ -2105,6 +2609,210 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = t_uintptr;
return res;
}
+
+ case BuiltinProc_objc_send:
+ return lb_handle_objc_send(p, expr);
+
+ case BuiltinProc_objc_find_selector: return lb_handle_objc_find_selector(p, expr);
+ case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
+ case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
+ case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
+
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) {
+ if (r < 0x10000 || r > 0x10ffff) {
+ *r1 = 0xfffd;
+ *r2 = 0xfffd;
+ } else {
+ r -= 0x10000;
+ *r1 = 0xd800 + ((r>>10)&0x3ff);
+ *r2 = 0xdc00 + (r&0x3ff);
+ }
+ };
+
+ lbModule *m = p->module;
+
+ auto tav = type_and_value_of_expr(ce->args[0]);
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String value = tav.value.value_string;
+
+ LLVMTypeRef llvm_u16 = lb_type(m, t_u16);
+
+ isize max_len = value.len*2 + 1;
+ LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len);
+ isize n = 0;
+ while (value.len > 0) {
+ Rune r = 0;
+ isize w = gb_utf8_decode(value.text, value.len, &r);
+ value.text += w;
+ value.len -= w;
+ if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) {
+ buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false);
+ } else if (0x10000 <= r && r <= 0x10ffff) {
+ u16 r1, r2;
+ encode_surrogate_pair(r, &r1, &r2);
+ buffer[n++] = LLVMConstInt(llvm_u16, r1, false);
+ buffer[n++] = LLVMConstInt(llvm_u16, r2, false);
+ } else {
+ buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false);
+ }
+ }
+
+ buffer[n++] = LLVMConstInt(llvm_u16, 0, false);
+
+ LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n);
+
+ char *name = nullptr;
+ {
+ isize max_len = 7+8+1;
+ name = gb_alloc_array(permanent_allocator(), char, max_len);
+ u32 id = m->gen->global_array_index.fetch_add(1);
+ isize len = gb_snprintf(name, max_len, "csbs$%x", id);
+ len -= 1;
+ }
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
+ LLVMSetInitializer(global_data, array);
+ LLVMSetLinkage(global_data, LLVMInternalLinkage);
+
+
+
+ LLVMValueRef indices[] = {
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ };
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
+ return res;
+
+ }
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ char const *name = "llvm.wasm.memory.grow";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+ case BuiltinProc_wasm_memory_size:
+ {
+ char const *name = "llvm.wasm.memory.size";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+ case BuiltinProc_wasm_memory_atomic_wait32:
+ {
+ char const *name = "llvm.wasm.memory.atomic.wait32";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_u32),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types));
+
+ Type *t_u32_ptr = alloc_type_pointer(t_u32);
+
+ LLVMValueRef args[3] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
+ args[2] = lb_emit_conv(p, lb_build_expr(p, ce->args[2]), t_i64).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+ case BuiltinProc_wasm_memory_atomic_notify32:
+ {
+ char const *name = "llvm.wasm.memory.atomic.notify";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_u32),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0); // types, gb_count_of(types));
+
+ Type *t_u32_ptr = alloc_type_pointer(t_u32);
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32_ptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
+
+ case BuiltinProc_x86_cpuid:
+ {
+ Type *param_types[2] = {t_u32, t_u32};
+ Type *type = alloc_type_proc_from_types(param_types, gb_count_of(param_types), tv.type, false, ProcCC_None);
+ LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+ LLVMValueRef the_asm = llvm_get_inline_asm(
+ func_type,
+ str_lit("cpuid"),
+ str_lit("={ax},={bx},={cx},={dx},{ax},{cx}"),
+ true
+ );
+ GB_ASSERT(the_asm != nullptr);
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_u32).value;
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
+ return res;
+ }
+ case BuiltinProc_x86_xgetbv:
+ {
+ Type *type = alloc_type_proc_from_types(&t_u32, 1, tv.type, false, ProcCC_None);
+ LLVMTypeRef func_type = LLVMGetElementType(lb_type(p->module, type));
+ LLVMValueRef the_asm = llvm_get_inline_asm(
+ func_type,
+ str_lit("xgetbv"),
+ str_lit("={ax},={dx},{cx}"),
+ true
+ );
+ GB_ASSERT(the_asm != nullptr);
+
+ LLVMValueRef args[1] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_u32).value;
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, args, gb_count_of(args), "");
+ return res;
+ }
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
@@ -2153,10 +2861,6 @@ lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
expr = unparen_expr(expr);
ast_node(ce, CallExpr, expr);
- if (ce->sce_temp_data) {
- return *(lbValue *)ce->sce_temp_data;
- }
-
lbValue res = lb_build_call_expr_internal(p, expr);
if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
@@ -2197,6 +2901,15 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
// NOTE(bill): Regular call
lbValue value = {};
Ast *proc_expr = unparen_expr(ce->proc);
+
+ Entity *proc_entity = entity_of_node(proc_expr);
+ if (proc_entity != nullptr) {
+ if (proc_entity->flags & EntityFlag_Disabled) {
+ GB_ASSERT(tv.type == nullptr);
+ return {};
+ }
+ }
+
if (proc_expr->tav.mode == Addressing_Constant) {
ExactValue v = proc_expr->tav.value;
switch (v.kind) {
@@ -2223,13 +2936,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- Entity *proc_entity = entity_of_node(proc_expr);
- if (proc_entity != nullptr) {
- if (proc_entity->flags & EntityFlag_Disabled) {
- return {};
- }
- }
-
if (value.value == nullptr) {
value = lb_build_expr(p, proc_expr);
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 3375ceda9..f131bb3db 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1210,8 +1210,8 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
}
lb_emit_jump(p, done);
- lb_close_scope(p, lbDeferExit_Default, done);
lb_start_block(p, done);
+ lb_close_scope(p, lbDeferExit_Default, done);
}
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value) {
@@ -1253,7 +1253,6 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l
ast_node(cc, CaseClause, clause);
lb_push_target_list(p, label, done, nullptr, nullptr);
- lb_open_scope(p, body->scope);
lb_build_stmt_list(p, cc->stmts);
lb_close_scope(p, lbDeferExit_Default, body);
lb_pop_target_list(p);
@@ -1263,6 +1262,7 @@ void lb_type_case_body(lbProcedure *p, Ast *label, Ast *clause, lbBlock *body, l
void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
lbModule *m = p->module;
+ lb_open_scope(p, ss->scope);
ast_node(as, AssignStmt, ss->tag);
GB_ASSERT(as->lhs.count == 1);
@@ -1321,6 +1321,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
for_array(i, body->stmts) {
Ast *clause = body->stmts[i];
ast_node(cc, CaseClause, clause);
+ lb_open_scope(p, cc->scope);
if (cc->list.count == 0) {
lb_start_block(p, default_block);
lb_store_type_case_implicit(p, clause, parent_value);
@@ -1329,6 +1330,9 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
}
lbBlock *body = lb_create_block(p, "typeswitch.body");
+ if (p->debug_info != nullptr) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause));
+ }
Type *case_type = nullptr;
for_array(type_index, cc->list) {
case_type = type_of_expr(cc->list[type_index]);
@@ -1375,6 +1379,7 @@ void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss) {
lb_emit_jump(p, done);
lb_start_block(p, done);
+ lb_close_scope(p, lbDeferExit_Default, done);
}
@@ -1652,13 +1657,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
+ // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
+ // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
+ // will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
- if (LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
@@ -1718,6 +1726,9 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
ast_node(fs, ForStmt, node);
lb_open_scope(p, fs->scope); // Open Scope here
+ if (p->debug_info != nullptr) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
+ }
if (fs->init != nullptr) {
#if 1
@@ -1738,11 +1749,14 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
post = lb_create_block(p, "for.post");
}
-
lb_emit_jump(p, loop);
lb_start_block(p, loop);
if (loop != body) {
+ // right now the condition (all expressions) will not set it's debug location, so we will do it here
+ if (p->debug_info != nullptr) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, fs->cond));
+ }
lb_build_cond(p, fs->cond, body, done);
lb_start_block(p, body);
}
@@ -1750,10 +1764,12 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_push_target_list(p, fs->label, done, post, nullptr);
lb_build_stmt(p, fs->body);
- lb_close_scope(p, lbDeferExit_Default, nullptr);
lb_pop_target_list(p);
+ if (p->debug_info != nullptr) {
+ LLVMSetCurrentDebugLocation2(p->builder, lb_debug_end_location_from_ast(p, fs->body));
+ }
lb_emit_jump(p, post);
if (fs->post != nullptr) {
@@ -1763,6 +1779,7 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
}
lb_start_block(p, done);
+ lb_close_scope(p, lbDeferExit_Default, nullptr);
}
void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
@@ -1968,14 +1985,9 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
}
}
- LLVMMetadataRef prev_debug_location = nullptr;
if (p->debug_info != nullptr) {
- prev_debug_location = LLVMGetCurrentDebugLocation2(p->builder);
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
}
- defer (if (prev_debug_location != nullptr) {
- LLVMSetCurrentDebugLocation2(p->builder, prev_debug_location);
- });
u16 prev_state_flags = p->state_flags;
defer (p->state_flags = prev_state_flags);
@@ -1991,6 +2003,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
out |= StateFlag_no_bounds_check;
out &= ~StateFlag_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;
+ }
p->state_flags = out;
}
@@ -2063,7 +2082,8 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lbAddr lval = {};
if (!is_blank_ident(name)) {
Entity *e = entity_of_node(name);
- bool zero_init = true; // Always do it
+ // bool zero_init = true; // Always do it
+ bool zero_init = vd->values.count == 0;
lval = lb_add_local(p, e->type, e, zero_init);
}
array_add(&lvals, lval);
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index e1332c6f3..2e7b2788a 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -14,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
}
lbValue lb_typeid(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
@@ -88,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) {
}
lbValue lb_type_info(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
@@ -106,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) {
}
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
i32 index = cast(i32)lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
// gb_printf_err("%d %s\n", index, type_to_string(type));
@@ -155,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
lbModule *m = p->module;
CheckerInfo *info = m->info;
@@ -454,7 +464,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_EnumeratedArray: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
- LLVMValueRef vals[6] = {
+ LLVMValueRef vals[7] = {
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -463,6 +473,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// Unions
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+
+ lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
};
lbValue res = {};
@@ -663,8 +675,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
+ vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
+ vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
for (isize i = 0; i < gb_count_of(vals); i++) {
if (vals[i] == nullptr) {
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 5b1b11b44..88ec2f22c 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -1,3 +1,5 @@
+lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
+
bool lb_is_type_aggregate(Type *t) {
t = base_type(t);
switch (t->kind) {
@@ -48,18 +50,19 @@ lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
return value;
}
-void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
+LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len, unsigned alignment, bool is_volatile) {
bool is_inlinable = false;
i64 const_len = 0;
if (LLVMIsConstant(len)) {
const_len = cast(i64)LLVMConstIntGetSExtValue(len);
// TODO(bill): Determine when it is better to do the `*.inline` versions
- if (const_len <= 4*build_context.word_size) {
+ if (const_len <= lb_max_zero_init_size()) {
is_inlinable = true;
}
}
+
char const *name = "llvm.memset";
if (is_inlinable) {
name = "llvm.memset.inline";
@@ -69,17 +72,29 @@ void lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, LLVMValueRef len
lb_type(p->module, t_rawptr),
lb_type(p->module, t_int)
};
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]));
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+ if (true || is_inlinable) {
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s.%s.%s", name, LLVMPrintTypeToString(types[0]), LLVMPrintTypeToString(types[1]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
- LLVMValueRef args[4] = {};
- args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
- args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
- args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
- args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
+ LLVMValueRef args[4] = {};
+ args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
+ args[1] = LLVMConstInt(LLVMInt8TypeInContext(p->module->ctx), 0, false);
+ args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
+ args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), is_volatile, false);
+
+ return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ } else {
+ LLVMValueRef ip = lb_lookup_runtime_procedure(p->module, str_lit("memset")).value;
+
+ LLVMValueRef args[3] = {};
+ args[0] = LLVMBuildPointerCast(p->builder, ptr, types[0], "");
+ args[1] = LLVMConstInt(LLVMInt32TypeInContext(p->module->ctx), 0, false);
+ args[2] = LLVMBuildIntCast2(p->builder, len, types[1], /*signed*/false, "");
+
+ return LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ }
- LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, unsigned alignment) {
@@ -201,6 +216,11 @@ lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
return res;
}
+ if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
+ res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
+ return res;
+ }
+
if (lb_is_type_aggregate(src) || lb_is_type_aggregate(dst)) {
lbValue s = lb_address_from_load_or_generate_local(p, value);
lbValue d = lb_emit_transmute(p, s, alloc_type_pointer(t));
@@ -480,8 +500,10 @@ lbValue lb_emit_count_ones(lbProcedure *p, lbValue x, Type *type) {
}
lbValue lb_emit_count_zeros(lbProcedure *p, lbValue x, Type *type) {
- i64 sz = 8*type_size_of(type);
- lbValue size = lb_const_int(p->module, type, cast(u64)sz);
+ Type *elem = base_array_type(type);
+ i64 sz = 8*type_size_of(elem);
+ lbValue size = lb_const_int(p->module, elem, cast(u64)sz);
+ size = lb_emit_conv(p, size, type);
lbValue count = lb_emit_count_ones(p, x, type);
return lb_emit_arith(p, Token_Sub, size, count, type);
}
@@ -626,6 +648,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lbValue value_ = lb_address_from_load_or_generate_local(p, value);
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
+ return lb_emit_load(p, ptr);
+ }
+
lbValue tag = {};
lbValue dst_tag = {};
lbValue cond = {};
@@ -666,23 +694,29 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lb_start_block(p, end_block);
if (!is_tuple) {
- {
- // NOTE(bill): Panic on invalid conversion
- Type *dst_type = tuple->Tuple.variables[0]->type;
+ GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+ // NOTE(bill): Panic on invalid conversion
+ Type *dst_type = tuple->Tuple.variables[0]->type;
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make(permanent_allocator(), 7);
- args[0] = ok;
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ auto args = array_make(permanent_allocator(), arg_count);
+ args[0] = ok;
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
+
+ if (!build_context.disallow_rtti) {
args[4] = lb_typeid(m, src_type);
args[5] = lb_typeid(m, dst_type);
args[6] = lb_emit_conv(p, value_, t_rawptr);
- lb_emit_runtime_call(p, "type_assertion_check2", args);
}
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
}
@@ -706,6 +740,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
}
Type *dst_type = tuple->Tuple.variables[0]->type;
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
+ return lb_addr(ptr);
+ }
+
lbAddr v = lb_add_local_generated(p, tuple, true);
lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -731,18 +772,24 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
-
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make(permanent_allocator(), 7);
+
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+ auto args = array_make(permanent_allocator(), arg_count);
args[0] = ok;
args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
args[2] = lb_const_int(m, t_i32, pos.line);
args[3] = lb_const_int(m, t_i32, pos.column);
- args[4] = any_typeid;
- args[5] = dst_typeid;
- args[6] = lb_emit_struct_ev(p, value, 0);;
+ if (!build_context.disallow_rtti) {
+ args[4] = any_typeid;
+ args[5] = dst_typeid;
+ args[6] = lb_emit_struct_ev(p, value, 0);
+ }
lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
@@ -1362,7 +1409,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
return lb_emit_struct_ev(p, slice, 0);
}
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
- GB_ASSERT(is_type_slice(slice.type));
+ GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
return lb_emit_struct_ev(p, slice, 1);
}
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
@@ -1768,7 +1815,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
return LLVMGetInlineAsm(func_type,
cast(char *)str.text, cast(size_t)str.len,
cast(char *)clobbers.text, cast(size_t)clobbers.len,
- /*HasSideEffects*/true, /*IsAlignStack*/false,
+ has_side_effects, is_align_stack,
dialect
#if LLVM_VERSION_MAJOR >= 13
, /*CanThrow*/false
@@ -1808,3 +1855,195 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
LLVMSetVisibility(value, LLVMDefaultVisibility);
LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name));
}
+
+
+
+lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_selectors, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_selectors, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_selectors, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name));
+}
+
+lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_selector(p, name);
+
+ auto args = array_make(permanent_allocator(), 1);
+ 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, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
+}
+
+lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
+
+ auto args = array_make(permanent_allocator(), 3);
+ args[0] = lb_const_nil(m, t_objc_Class);
+ args[1] = lb_const_nil(m, t_objc_Class);
+ args[2] = lb_const_int(m, t_uint, 0);
+ lbValue ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+ lb_addr_store(p, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+
+lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
+ TypeAndValue const &tav = type_and_value_of_expr(expr);
+ if (tav.mode == Addressing_Type) {
+ Type *type = tav.type;
+ GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type));
+ Entity *e = type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+ String name = e->TypeName.objc_class_name;
+
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return lb_addr_load(p, *found);
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return lb_addr_load(p, local_addr);
+ }
+ }
+
+ return lb_build_expr(p, expr);
+}
+
+lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+ GB_ASSERT(data.proc_type != nullptr);
+
+ GB_ASSERT(ce->args.count >= 3);
+ auto args = array_make(permanent_allocator(), 0, ce->args.count-1);
+
+ lbValue id = lb_handle_objc_id(p, ce->args[1]);
+ Ast *sel_expr = ce->args[2];
+ GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String);
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string));
+
+ array_add(&args, id);
+ array_add(&args, sel);
+ for (isize i = 3; i < ce->args.count; i++) {
+ lbValue arg = lb_build_expr(p, ce->args[i]);
+ array_add(&args, arg);
+ }
+
+
+ lbValue the_proc = {};
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
+
+
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
+ GB_ASSERT(value.kind == ExactValue_Integer);
+ i64 v = exact_value_to_i64(value);
+ switch (v) {
+ case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
+ case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
+ case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
+ case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
+ case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
+ }
+ GB_PANIC("Unknown atomic ordering");
+ return LLVMAtomicOrderingSequentiallyConsistent;
+}
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
+ ExactValue value = type_and_value_of_expr(expr).value;
+ return llvm_atomic_ordering_from_odin(value);
+}
diff --git a/src/main.cpp b/src/main.cpp
index fe56d451f..beefec702 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
#include "checker.cpp"
#include "docs.cpp"
-
#include "llvm_backend.cpp"
#if defined(GB_SYSTEM_OSX)
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
#endif
#include "query_data.cpp"
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
-#include "microsoft_craziness.h"
-#endif
-
#include "bug_report.cpp"
-
// NOTE(bill): 'name' is used in debugging and profiling modes
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
@@ -117,6 +108,9 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
+ if (WIFEXITED(exit_code)) {
+ exit_code = WEXITSTATUS(exit_code);
+ }
#endif
if (exit_code) {
@@ -127,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
}
-
-
i32 linker_stage(lbGenerator *gen) {
i32 result = 0;
Timings *timings = &global_timings;
- String output_base = gen->output_base;
+ String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ debugf("Linking %.*s\n", LIT(output_filename));
+
+ // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
- "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
+ "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
- "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#ifdef GB_SYSTEM_UNIX
+#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
@@ -169,359 +164,367 @@ i32 linker_stage(lbGenerator *gen) {
build_context.keep_object_files = true;
} else {
#if defined(GB_SYSTEM_WINDOWS)
- String section_name = str_lit("msvc-link");
- if (build_context.use_lld) {
- section_name = str_lit("lld-link");
- }
- timings_start_section(timings, section_name);
-
- gbString lib_str = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(lib_str));
-
- char const *output_ext = "exe";
- gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
- defer (gb_string_free(link_settings));
+ bool is_windows = true;
+ #else
+ bool is_windows = false;
+ #endif
+ #if defined(GB_SYSTEM_OSX)
+ bool is_osx = true;
+ #else
+ bool is_osx = false;
+ #endif
- // 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();
+ if (is_windows) {
+ String section_name = str_lit("msvc-link");
+ if (build_context.use_lld) {
+ section_name = str_lit("lld-link");
+ }
+ timings_start_section(timings, section_name);
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- exit(1);
- }
+ gbString lib_str = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(lib_str));
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
+ gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
+ defer (gb_string_free(link_settings));
- // Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
-
- String path = {};
- auto add_path = [&](String path) {
- if (path[path.len-1] == '\\') {
- path.len -= 1;
- }
- link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
- };
- add_path(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
- }
+ // Add library search paths.
+ if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
+ String path = {};
+ auto add_path = [&](String path) {
+ if (path[path.len-1] == '\\') {
+ path.len -= 1;
+ }
+ link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
+ };
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
+ }
- StringSet libs = {};
- string_set_init(&libs, heap_allocator(), 64);
- defer (string_set_destroy(&libs));
+ StringSet libs = {};
+ string_set_init(&libs, heap_allocator(), 64);
+ defer (string_set_destroy(&libs));
- StringSet asm_files = {};
- string_set_init(&asm_files, heap_allocator(), 64);
- defer (string_set_destroy(&asm_files));
+ StringSet asm_files = {};
+ string_set_init(&asm_files, heap_allocator(), 64);
+ defer (string_set_destroy(&asm_files));
- for_array(j, gen->modules.entries) {
- lbModule *m = gen->modules.entries[j].value;
- for_array(i, m->foreign_library_paths) {
- String lib = m->foreign_library_paths[i];
- if (has_asm_extension(lib)) {
- string_set_add(&asm_files, lib);
- } else {
- string_set_add(&libs, lib);
+ for_array(j, gen->foreign_libraries) {
+ Entity *e = gen->foreign_libraries[j];
+ GB_ASSERT(e->kind == Entity_LibraryName);
+ for_array(i, e->LibraryName.paths) {
+ String lib = string_trim_whitespace(e->LibraryName.paths[i]);
+ // IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
+ // we will never uses these strings afterwards
+ string_to_lower(&lib);
+ if (lib.len == 0) {
+ continue;
+ }
+
+ if (has_asm_extension(lib)) {
+ if (!string_set_update(&asm_files, lib)) {
+ String asm_file = asm_files.entries[i].value;
+ String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
+
+ result = system_exec_command_line_app("nasm",
+ "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
+ "-f win64 "
+ "-o \"%.*s\" "
+ "%.*s "
+ "",
+ LIT(build_context.ODIN_ROOT), LIT(asm_file),
+ LIT(obj_file),
+ LIT(build_context.extra_assembler_flags)
+ );
+
+ if (result) {
+ return result;
+ }
+ array_add(&gen->output_object_paths, obj_file);
+ }
+ } else {
+ if (!string_set_update(&libs, lib)) {
+ lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
+ }
+ }
}
}
- }
- for_array(i, gen->default_module.foreign_library_paths) {
- String lib = gen->default_module.foreign_library_paths[i];
- if (has_asm_extension(lib)) {
- string_set_add(&asm_files, lib);
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
- string_set_add(&libs, lib);
+ link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
}
- }
- for_array(i, libs.entries) {
- String lib = libs.entries[i].value;
- lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
- }
-
-
- if (build_context.build_mode == BuildMode_DynamicLibrary) {
- output_ext = "dll";
- link_settings = gb_string_append_fmt(link_settings, " /DLL");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
- }
-
- if (build_context.pdb_filepath != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
- }
-
- if (build_context.no_crt) {
- link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
- } else {
- link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
- }
-
- if (build_context.ODIN_DEBUG) {
- link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
- }
-
- for_array(i, asm_files.entries) {
- String asm_file = asm_files.entries[i].value;
- String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
-
- result = system_exec_command_line_app("nasm",
- "\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
- "-f win64 "
- "-o \"%.*s\" "
- "%.*s "
- "",
- LIT(build_context.ODIN_ROOT), LIT(asm_file),
- LIT(obj_file),
- LIT(build_context.extra_assembler_flags)
- );
-
- if (result) {
- return result;
+ if (build_context.pdb_filepath != "") {
+ String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
- array_add(&gen->output_object_paths, obj_file);
- }
- gbString object_files = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(object_files));
- for_array(i, gen->output_object_paths) {
- String object_path = gen->output_object_paths[i];
- object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
- }
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
+ } else {
+ link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
+ }
- char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
- if (!build_context.use_lld) { // msvc
- if (build_context.has_resource) {
- result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
- );
+ if (build_context.ODIN_DEBUG) {
+ link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+ }
+
+ gbString object_files = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(object_files));
+ for_array(i, gen->output_object_paths) {
+ String object_path = gen->output_object_paths[i];
+ object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+ }
+
+ String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
+ defer (gb_free(heap_allocator(), vs_exe_path.text));
+
+ char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
+ if (!build_context.use_lld) { // msvc
+ if (build_context.has_resource) {
+ String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+ String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+ defer (gb_free(heap_allocator(), rc_path.text));
+ defer (gb_free(heap_allocator(), res_path.text));
+
+ result = system_exec_command_line_app("msvc-link",
+ "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ LIT(res_path),
+ LIT(rc_path)
+ );
+
+ if (result) {
+ return result;
+ }
+
+ result = system_exec_command_line_app("msvc-link",
+ "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
+ " %.*s "
+ " %.*s "
+ " %s "
+ "",
+ LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
+ LIT(build_context.extra_linker_flags),
+ lib_str
+ );
+ } else {
+ result = system_exec_command_line_app("msvc-link",
+ "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
+ "/nologo /incremental:no /opt:ref /subsystem:%s "
+ " %.*s "
+ " %.*s "
+ " %s "
+ "",
+ LIT(vs_exe_path), object_files, LIT(output_filename),
+ link_settings,
+ subsystem_str,
+ LIT(build_context.link_flags),
+ LIT(build_context.extra_linker_flags),
+ lib_str
+ );
+ }
if (result) {
return result;
}
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+ } else { // lld
+ result = system_exec_command_line_app("msvc-lld-link",
+ "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
- } else {
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
+ LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
- }
- if (result) {
- return result;
- }
-
- } else { // lld
- result = system_exec_command_line_app("msvc-lld-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
- link_settings,
- subsystem_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- lib_str
- );
-
- if (result) {
- return result;
- }
- }
- #else
- timings_start_section(timings, str_lit("ld-link"));
-
- // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
- char cwd[256];
- getcwd(&cwd[0], 256);
- //printf("%s\n", cwd);
-
- // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
- // files can be passed with -l:
- gbString lib_str = gb_string_make(heap_allocator(), "-L/");
- defer (gb_string_free(lib_str));
-
- for_array(i, gen->default_module.foreign_library_paths) {
- String lib = gen->default_module.foreign_library_paths[i];
-
- // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
- // This allows you to specify '-f' in a #foreign_system_library,
- // without having to implement any new syntax specifically for MacOS.
- #if defined(GB_SYSTEM_OSX)
- if (string_ends_with(lib, str_lit(".framework"))) {
- // framework thingie
- String lib_name = lib;
- lib_name = remove_extension_from_path(lib_name);
- lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
- } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
- // For:
- // object
- // dynamic lib
- // static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
- } else {
- // dynamic or static system lib, just link regularly searching system library paths
- lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+ if (result) {
+ return result;
}
- #else
- // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
- // since those are statically linked to at link time. shared libraries (.so) has to be
- // available at runtime wherever the executable is run, so we make require those to be
- // local to the executable (unless the system collection is used, in which case we search
- // the system library paths for the library file).
- if (string_ends_with(lib, str_lit(".a"))) {
- // static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".so"))) {
- // dynamic lib, relative path to executable
- // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtimeto the executable
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
- } else {
- // dynamic or static system lib, just link regularly searching system library paths
- lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
- }
- #endif
- }
-
- gbString object_files = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(object_files));
- for_array(i, gen->output_object_paths) {
- String object_path = gen->output_object_paths[i];
- object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
- }
-
- // Unlike the Win32 linker code, the output_ext includes the dot, because
- // typically executable files on *NIX systems don't have extensions.
- String output_ext = {};
- gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
-
- // NOTE(dweiler): We use clang as a frontend for the linker as there are
- // other runtime and compiler support libraries that need to be linked in
- // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
- // These are not always typically inside /lib, /lib64, or /usr versions
- // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
- // the distribution of Linux even. The gcc or clang specs is the only
- // reliable way to query this information to call ld directly.
- if (build_context.build_mode == BuildMode_DynamicLibrary) {
- // NOTE(dweiler): Let the frontend know we're building a shared library
- // so it doesn't generate symbols which cannot be relocated.
- link_settings = gb_string_appendc(link_settings, "-shared ");
-
- // NOTE(dweiler): _odin_entry_point must be called at initialization
- // time of the shared object, similarly, _odin_exit_point must be called
- // at deinitialization. We can pass both -init and -fini to the linker by
- // using a comma separated list of arguments to -Wl.
- //
- // This previously used ld but ld cannot actually build a shared library
- // correctly this way since all the other dependencies provided implicitly
- // by the compiler frontend are still needed and most of the command
- // line arguments prepared previously are incompatible with ld.
- //
- // Shared libraries are .dylib on MacOS and .so on Linux.
- #if defined(GB_SYSTEM_OSX)
- output_ext = STR_LIT(".dylib");
- #else
- output_ext = STR_LIT(".so");
- #endif
- link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
- link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
+ }
} else {
- link_settings = gb_string_appendc(link_settings, "-no-pie ");
- }
- if (build_context.out_filepath.len > 0) {
- //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
- isize pos = string_extension_position(build_context.out_filepath);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
- }
- }
+ timings_start_section(timings, str_lit("ld-link"));
- result = system_exec_command_line_app("ld-link",
- "clang -Wno-unused-command-line-argument %s -o \"%.*s%.*s\" %s "
- " %s "
- " %.*s "
- " %.*s "
- " %s "
- #if defined(GB_SYSTEM_OSX)
+ // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
+ char cwd[256];
+ #if !defined(GB_SYSTEM_WINDOWS)
+ getcwd(&cwd[0], 256);
+ #endif
+ //printf("%s\n", cwd);
+
+ // NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
+ // files can be passed with -l:
+ gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+ defer (gb_string_free(lib_str));
+
+ StringSet libs = {};
+ string_set_init(&libs, heap_allocator(), 64);
+ defer (string_set_destroy(&libs));
+
+ for_array(j, gen->foreign_libraries) {
+ Entity *e = gen->foreign_libraries[j];
+ GB_ASSERT(e->kind == Entity_LibraryName);
+ for_array(i, e->LibraryName.paths) {
+ String lib = string_trim_whitespace(e->LibraryName.paths[i]);
+ if (lib.len == 0) {
+ continue;
+ }
+ if (string_set_update(&libs, lib)) {
+ continue;
+ }
+
+ // NOTE(zangent): Sometimes, you have to use -framework on MacOS.
+ // This allows you to specify '-f' in a #foreign_system_library,
+ // without having to implement any new syntax specifically for MacOS.
+ if (build_context.metrics.os == TargetOs_darwin) {
+ if (string_ends_with(lib, str_lit(".framework"))) {
+ // framework thingie
+ String lib_name = lib;
+ lib_name = remove_extension_from_path(lib_name);
+ lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
+ } else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
+ // For:
+ // object
+ // dynamic lib
+ // static libs, absolute full path relative to the file in which the lib was imported from
+ lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+ } else {
+ // dynamic or static system lib, just link regularly searching system library paths
+ lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+ }
+ } else {
+ // NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
+ // since those are statically linked to at link time. shared libraries (.so) has to be
+ // available at runtime wherever the executable is run, so we make require those to be
+ // local to the executable (unless the system collection is used, in which case we search
+ // the system library paths for the library file).
+ if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
+ // static libs and object files, absolute full path relative to the file in which the lib was imported from
+ lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
+ } else if (string_ends_with(lib, str_lit(".so"))) {
+ // dynamic lib, relative path to executable
+ // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
+ // at runtime to the executable
+ lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
+ } else {
+ // dynamic or static system lib, just link regularly searching system library paths
+ lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
+ }
+ }
+ }
+ }
+
+
+ gbString object_files = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(object_files));
+ for_array(i, gen->output_object_paths) {
+ String object_path = gen->output_object_paths[i];
+ object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
+ }
+
+ gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
+
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+ }
+
+ // NOTE(dweiler): We use clang as a frontend for the linker as there are
+ // other runtime and compiler support libraries that need to be linked in
+ // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
+ // These are not always typically inside /lib, /lib64, or /usr versions
+ // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
+ // the distribution of Linux even. The gcc or clang specs is the only
+ // reliable way to query this information to call ld directly.
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // NOTE(dweiler): Let the frontend know we're building a shared library
+ // so it doesn't generate symbols which cannot be relocated.
+ link_settings = gb_string_appendc(link_settings, "-shared ");
+
+ // NOTE(dweiler): _odin_entry_point must be called at initialization
+ // time of the shared object, similarly, _odin_exit_point must be called
+ // at deinitialization. We can pass both -init and -fini to the linker by
+ // using a comma separated list of arguments to -Wl.
+ //
+ // This previously used ld but ld cannot actually build a shared library
+ // correctly this way since all the other dependencies provided implicitly
+ // by the compiler frontend are still needed and most of the command
+ // line arguments prepared previously are incompatible with ld.
+ if (build_context.metrics.os == TargetOs_darwin) {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
+ // NOTE(weshardee): __odin_exit_point should also be added, but -fini
+ // does not exist on MacOS
+ } else {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
+ link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
+ }
+
+ } else if (build_context.metrics.os != TargetOs_openbsd) {
+ // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
+ link_settings = gb_string_appendc(link_settings, "-no-pie ");
+ }
+
+ gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(platform_lib_str));
+ if (build_context.metrics.os == TargetOs_darwin) {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+ } else {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
+ }
+
+ if (build_context.metrics.os == TargetOs_darwin) {
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the 'mtriple' param passed to 'opt'
- #if defined(GB_CPU_ARM)
- " -mmacosx-version-min=11.0.0 "
- #else
- " -mmacosx-version-min=10.8.0 "
- #endif
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
+ } else {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
+ }
// This points the linker to where the entry point is
- " -e _main "
- #endif
- , object_files, LIT(output_base), LIT(output_ext),
- #if defined(GB_SYSTEM_OSX)
- "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
- #else
- "-lc -lm",
- #endif
- lib_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- link_settings);
+ link_settings = gb_string_appendc(link_settings, " -e _main ");
+ }
- if (result) {
- return result;
- }
+ gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+ defer (gb_string_free(link_command_line));
- #if defined(GB_SYSTEM_OSX)
- if (build_context.ODIN_DEBUG) {
- // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
- // to the symbols in the object file
- result = system_exec_command_line_app("dsymutil",
- "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
- );
+ link_command_line = gb_string_appendc(link_command_line, object_files);
+ link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
+ link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+ result = system_exec_command_line_app("ld-link", link_command_line);
if (result) {
return result;
}
- }
- #endif
- #endif
+ if (is_osx && build_context.ODIN_DEBUG) {
+ // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
+ // to the symbols in the object file
+ result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
+
+ if (result) {
+ return result;
+ }
+ }
+ }
}
return result;
@@ -572,54 +575,26 @@ void usage(String argv0) {
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
print_usage_line(0, "Commands:");
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
- print_usage_line(1, "check parse and type check .odin file");
- print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
- print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
- print_usage_line(1, "version print version");
- print_usage_line(1, "report print information useful to reporting a bug");
+ print_usage_line(1, "build compile directory of .odin files, as an executable.");
+ print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
+ print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(1, "check parse, and type check a directory of .odin files");
+ print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
+ print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
+ print_usage_line(1, "test build and runs procedures with the attribute @(test) in the initial package");
+ print_usage_line(1, "doc generate documentation on a directory of .odin files");
+ print_usage_line(1, "version print version");
+ print_usage_line(1, "report print information useful to reporting a bug");
print_usage_line(0, "");
print_usage_line(0, "For further details on a command, use -help after the command name");
print_usage_line(1, "e.g. odin build -help");
}
-
-bool string_is_valid_identifier(String str) {
- if (str.len <= 0) return false;
-
- isize rune_count = 0;
-
- isize w = 0;
- isize offset = 0;
- while (offset < str.len) {
- Rune r = 0;
- w = utf8_decode(str.text, str.len, &r);
- if (r == GB_RUNE_INVALID) {
- return false;
- }
-
- if (rune_count == 0) {
- if (!rune_is_letter(r)) {
- return false;
- }
- } else {
- if (!rune_is_letter(r) && !rune_is_digit(r)) {
- return false;
- }
- }
- rune_count += 1;
- offset += w;
- }
-
- return true;
-}
-
enum BuildFlagKind {
BuildFlag_Invalid,
BuildFlag_Help,
+ BuildFlag_SingleFile,
BuildFlag_OutFile,
BuildFlag_OptimizationLevel,
@@ -655,6 +630,10 @@ enum BuildFlagKind {
BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags,
BuildFlag_Microarch,
+ BuildFlag_TargetFeatures,
+
+ BuildFlag_RelocMode,
+ BuildFlag_DisableRedZone,
BuildFlag_TestName,
@@ -663,6 +642,8 @@ enum BuildFlagKind {
BuildFlag_InsertSemicolon,
BuildFlag_StrictStyle,
BuildFlag_StrictStyleInitOnly,
+ BuildFlag_ForeignErrorProcedures,
+ BuildFlag_DisallowRTTI,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -675,6 +656,7 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
+ BuildFlag_ErrorPosStyle,
// internal use only
BuildFlag_InternalIgnoreLazy,
@@ -772,69 +754,81 @@ ExactValue build_param_to_exact_value(String name, String param) {
bool parse_build_flags(Array args) {
auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT);
- add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
- add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
- add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build|Command_strip_semicolon);
- add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
- add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
- add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
- add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
+ add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build | Command_strip_semicolon);
+ add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
+ add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
+ add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
+ add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
+ add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
- add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
+
+ add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+
+
+ add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
+
+ add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all);
+
+ add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
#endif
@@ -857,11 +851,19 @@ bool parse_build_flags(Array args) {
String name = substring(flag, 1, flag.len);
isize end = 0;
+ bool have_equals = false;
for (; end < name.len; end++) {
if (name[end] == ':') break;
- if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!!
+ if (name[end] == '=') {
+ have_equals = true;
+ break;
+ }
}
name = substring(name, 0, end);
+ if (have_equals && name != "opt") {
+ gb_printf_err("`flag=value` has been deprecated and will be removed next release. Use `%.*s:` instead.\n", LIT(name));
+ }
+
String param = {};
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
@@ -935,35 +937,35 @@ bool parse_build_flags(Array args) {
switch (bf.param_kind) {
case BuildFlagParam_None:
if (value.kind != ExactValue_Invalid) {
- gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected no value, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Boolean:
if (value.kind != ExactValue_Bool) {
- gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a boolean, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Integer:
if (value.kind != ExactValue_Integer) {
- gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected an integer, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Float:
if (value.kind != ExactValue_Float) {
- gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a floating pointer number, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_String:
if (value.kind != ExactValue_String) {
- gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a string, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
@@ -993,7 +995,20 @@ bool parse_build_flags(Array args) {
bad_flags = true;
break;
}
+
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
+ if (build_context.optimization_level < 0 || build_context.optimization_level > 3) {
+ gb_printf_err("Invalid optimization level for -o:, got %d\n", build_context.optimization_level);
+ gb_printf_err("Valid optimization levels:\n");
+ gb_printf_err("\t0\n");
+ gb_printf_err("\t1\n");
+ gb_printf_err("\t2\n");
+ gb_printf_err("\t3\n");
+ bad_flags = true;
+ }
+
+ // Deprecation warning.
+ gb_printf_err("`-opt` has been deprecated and will be removed next release. Use `-o:minimal`, etc.\n");
break;
}
case BuildFlag_OptimizationMode: {
@@ -1366,6 +1381,37 @@ bool parse_build_flags(Array args) {
string_to_lower(&build_context.microarch);
break;
}
+ case BuildFlag_TargetFeatures: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.target_features_string = value.value_string;
+ string_to_lower(&build_context.target_features_string);
+ break;
+ }
+ case BuildFlag_RelocMode: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String v = value.value_string;
+ if (v == "default") {
+ build_context.reloc_mode = RelocMode_Default;
+ } else if (v == "static") {
+ build_context.reloc_mode = RelocMode_Static;
+ } else if (v == "pic") {
+ build_context.reloc_mode = RelocMode_PIC;
+ } else if (v == "dynamic-no-pic") {
+ build_context.reloc_mode = RelocMode_DynamicNoPIC;
+ } else {
+ gb_printf_err("-reloc-mode flag expected one of the following\n");
+ gb_printf_err("\tdefault\n");
+ gb_printf_err("\tstatic\n");
+ gb_printf_err("\tpic\n");
+ gb_printf_err("\tdynamic-no-pic\n");
+ bad_flags = true;
+ }
+
+ break;
+ }
+ case BuildFlag_DisableRedZone:
+ build_context.disable_red_zone = true;
+ break;
case BuildFlag_TestName: {
GB_ASSERT(value.kind == ExactValue_String);
{
@@ -1384,9 +1430,15 @@ bool parse_build_flags(Array args) {
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
+ case BuildFlag_DisallowRTTI:
+ build_context.disallow_rtti = true;
+ break;
case BuildFlag_DefaultToNilAllocator:
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+ case BuildFlag_ForeignErrorProcedures:
+ build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
+ break;
case BuildFlag_InsertSemicolon: {
gb_printf_err("-insert-semicolon flag is not required any more\n");
bad_flags = true;
@@ -1469,6 +1521,20 @@ bool parse_build_flags(Array args) {
case BuildFlag_VerboseErrors:
build_context.show_error_line = true;
break;
+
+ case BuildFlag_ErrorPosStyle:
+ GB_ASSERT(value.kind == ExactValue_String);
+
+ if (str_eq_ignore_case(value.value_string, str_lit("odin")) || str_eq_ignore_case(value.value_string, str_lit("default"))) {
+ build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Default;
+ } else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) {
+ build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n");
+ bad_flags = true;
+ }
+ break;
+
case BuildFlag_InternalIgnoreLazy:
build_context.ignore_lazy = true;
break;
@@ -1487,6 +1553,10 @@ bool parse_build_flags(Array args) {
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
bad_flags = true;
break;
+ } else if (!gb_file_exists((const char *)path.text)) {
+ gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
+ bad_flags = true;
+ break;
}
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
build_context.has_resource = true;
@@ -1501,6 +1571,11 @@ bool parse_build_flags(Array args) {
String path = value.value_string;
path = string_trim_whitespace(path);
if (is_build_flag_path_valid(path)) {
+ if (path_is_directory(path)) {
+ gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
+ bad_flags = true;
+ break;
+ }
// #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path);
// if (ext != ".pdb") {
@@ -1842,31 +1917,46 @@ void remove_temp_files(lbGenerator *gen) {
void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0));
- print_usage_line(0, "Usage");
+ print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
if (command == "build") {
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
- } else if (command == "run") {
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
- } else if (command == "check") {
- print_usage_line(1, "check parse and type check .odin file(s)");
- } else if (command == "test") {
- print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
- } else if (command == "query") {
- print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program");
- } else if (command == "doc") {
- print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files");
+ print_usage_line(1, "build Compile directory of .odin files as an executable.");
+ print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
+ print_usage_line(2, "Use `-file` to build a single file instead.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "odin doc core/path");
- print_usage_line(3, "odin doc core/path core/path/filepath");
+ print_usage_line(3, "odin build . # Build package in current directory");
+ print_usage_line(3, "odin build # Build package in ");
+ print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point.");
+ } else if (command == "run") {
+ print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(2, "Append an empty flag and then the args, '-- ', to specify args for the output.");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin run . # Build and run package in current directory");
+ print_usage_line(3, "odin run # Build and run package in ");
+ print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point.");
+ } else if (command == "check") {
+ print_usage_line(1, "check Parse and type check directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin check . # Type check package in current directory");
+ print_usage_line(3, "odin check # Type check package in ");
+ print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point.");
+ } else if (command == "test") {
+ print_usage_line(1, "test Build ands runs procedures with the attribute @(test) in the initial package");
+ } else if (command == "query") {
+ print_usage_line(1, "query [experimental] Parse, type check, and output a .json file containing information about the program");
+ } else if (command == "doc") {
+ print_usage_line(1, "doc generate documentation from a directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin doc . # Generate documentation on package in current directory");
+ print_usage_line(3, "odin doc # Generate documentation on package in ");
+ print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package.");
} else if (command == "version") {
print_usage_line(1, "version print version");
} else if (command == "strip-semicolon") {
print_usage_line(1, "strip-semicolon");
- print_usage_line(2, "parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
+ print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
}
bool doc = command == "doc";
@@ -1881,6 +1971,13 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "Flags");
print_usage_line(0, "");
+ if (check) {
+ print_usage_line(1, "-file");
+ print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
+ print_usage_line(2, "This means that `/a.odin` won't have access to `/b.odin`'s contents.");
+ print_usage_line(0, "");
+ }
+
if (doc) {
print_usage_line(1, "-short");
print_usage_line(2, "Show shortened documentation for the packages");
@@ -1971,6 +2068,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-define:=");
print_usage_line(2, "Defines a global constant with a value");
print_usage_line(2, "Example: -define:SPAM=123");
+ print_usage_line(2, "To use: #config(SPAM, default_value)");
print_usage_line(0, "");
}
@@ -2084,6 +2182,18 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(0, "");
+
+ print_usage_line(1, "-reloc-mode:");
+ print_usage_line(2, "Specifies the reloc mode");
+ print_usage_line(2, "Options:");
+ print_usage_line(3, "default");
+ print_usage_line(3, "static");
+ print_usage_line(3, "pic");
+ print_usage_line(3, "dynamic-no-pic");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-disable-red-zone");
+ print_usage_line(2, "Disable red zone on a supported freestanding target");
}
if (check) {
@@ -2114,6 +2224,11 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-verbose-errors");
print_usage_line(2, "Prints verbose error messages showing the code on that line and the location in that line");
print_usage_line(0, "");
+
+ print_usage_line(1, "-foreign-error-procedures");
+ print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit");
+ print_usage_line(0, "");
+
}
if (run_or_build) {
@@ -2431,8 +2546,6 @@ int strip_semicolons(Parser *parser) {
return cast(int)failed;
}
-
-
int main(int arg_count, char const **arg_ptr) {
if (arg_count < 2) {
usage(make_string_c(arg_ptr[0]));
@@ -2447,6 +2560,7 @@ int main(int arg_count, char const **arg_ptr) {
virtual_memory_init();
mutex_init(&fullpath_mutex);
mutex_init(&hash_exact_value_mutex);
+ mutex_init(&global_type_name_objc_metadata_mutex);
init_string_buffer_memory();
init_string_interner();
@@ -2472,6 +2586,7 @@ int main(int arg_count, char const **arg_ptr) {
String command = args[1];
String init_filename = {};
String run_args_string = {};
+ isize last_non_run_arg = args.count;
bool run_output = false;
if (command == "run" || command == "test") {
@@ -2487,7 +2602,6 @@ int main(int arg_count, char const **arg_ptr) {
Array run_args = array_make(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
- isize last_non_run_arg = args.count;
for_array(i, args) {
if (args[i] == "--") {
last_non_run_arg = i;
@@ -2500,6 +2614,7 @@ int main(int arg_count, char const **arg_ptr) {
args = array_slice(args, 0, last_non_run_arg);
run_args_string = string_join_and_quote(heap_allocator(), run_args);
+
init_filename = args[2];
run_output = true;
@@ -2587,11 +2702,40 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ init_filename = copy_string(permanent_allocator(), init_filename);
+
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
}
+ if (init_filename.len > 0 && !build_context.show_help) {
+ // The command must be build, run, test, check, or another that takes a directory or filename.
+ if (!path_is_directory(init_filename)) {
+ // Input package is a filename. We allow this only if `-file` was given, otherwise we exit with an error message.
+ bool single_file_package = false;
+ for_array(i, args) {
+ if (i >= 3 && i <= last_non_run_arg && args[i] == "-file") {
+ single_file_package = true;
+ break;
+ }
+ }
+
+ if (!single_file_package) {
+ gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
+ gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
+ gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
+ return 1;
+ } else {
+ String const ext = str_lit(".odin");
+ if (!string_ends_with(init_filename, ext)) {
+ gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ return 1;
+ }
+ }
+ }
+ }
+
build_context.command = command;
if (!parse_build_flags(args)) {
@@ -2606,16 +2750,27 @@ int main(int arg_count, char const **arg_ptr) {
// NOTE(bill): add 'shared' directory if it is not already set
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
add_library_collection(str_lit("shared"),
- get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
+ get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
}
-
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
// return 1;
// }
+ // Set and check build paths...
+ if (!init_build_paths(init_filename)) {
+ return 1;
+ }
+
+ if (build_context.show_debug_messages) {
+ for_array(i, build_context.build_paths) {
+ String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
+ debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
+ }
+ }
+
init_global_thread_pool();
defer (thread_pool_destroy(&global_thread_pool));
@@ -2632,6 +2787,8 @@ int main(int arg_count, char const **arg_ptr) {
}
defer (destroy_parser(parser));
+ // TODO(jeroen): Remove the `init_filename` param.
+ // Let's put that on `build_context.build_paths[0]` instead.
if (parse_packages(parser, init_filename) != ParseFile_None) {
return 1;
}
@@ -2710,16 +2867,10 @@ int main(int arg_count, char const **arg_ptr) {
}
if (run_output) {
- #if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
- #else
- //NOTE(thebirk): This whole thing is a little leaky
- String output_ext = {};
- String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
- complete_path = path_to_full_path(permanent_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
- #endif
- }
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+ return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
+ }
return 0;
}
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index b4f815284..812513875 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -45,53 +45,97 @@
//
// Here is the API you need to know about:
//
-
-
gb_global gbAllocator mc_allocator = heap_allocator();
struct Find_Result {
- int windows_sdk_version; // Zero if no Windows SDK found.
+ int windows_sdk_version; // Zero if no Windows SDK found.
- wchar_t const *windows_sdk_root;
- wchar_t const *windows_sdk_um_library_path;
- wchar_t const *windows_sdk_ucrt_library_path;
+ wchar_t const *windows_sdk_root;
+ wchar_t const *windows_sdk_um_library_path;
+ wchar_t const *windows_sdk_ucrt_library_path;
- wchar_t const *vs_exe_path;
- wchar_t const *vs_library_path;
+ wchar_t const *vs_exe_path;
+ wchar_t const *vs_library_path;
};
struct Find_Result_Utf8 {
- int windows_sdk_version; // Zero if no Windows SDK found.
+ int windows_sdk_version; // Zero if no Windows SDK found.
- String windows_sdk_root;
- String windows_sdk_um_library_path;
- String windows_sdk_ucrt_library_path;
+ String windows_sdk_root;
+ String windows_sdk_um_library_path;
+ String windows_sdk_ucrt_library_path;
- String vs_exe_path;
- String vs_library_path;
+ String vs_exe_path;
+ String vs_library_path;
};
-
-Find_Result find_visual_studio_and_windows_sdk();
Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8();
-void free_resources(Find_Result *result) {
- // free(result->windows_sdk_root);
- // free(result->windows_sdk_um_library_path);
- // free(result->windows_sdk_ucrt_library_path);
- // free(result->vs_exe_path);
- // free(result->vs_library_path);
+String mc_wstring_to_string(wchar_t const *str) {
+ return string16_to_string(mc_allocator, make_string16_c(str));
}
-void free_resources(Find_Result_Utf8 *result) {
- // gbAllocator a = heap_allocator();
- // gb_free(a, result->windows_sdk_root.text);
- // gb_free(a, result->windows_sdk_um_library_path.text);
- // gb_free(a, result->windows_sdk_ucrt_library_path.text);
- // gb_free(a, result->vs_exe_path.text);
- // gb_free(a, result->vs_library_path.text);
+String16 mc_string_to_wstring(String str) {
+ return string_to_string16(mc_allocator, str);
}
+String mc_concat(String a, String b) {
+ return concatenate_strings(mc_allocator, a, b);
+}
+
+String mc_concat(String a, String b, String c) {
+ return concatenate3_strings(mc_allocator, a, b, c);
+}
+
+String mc_get_env(String key) {
+ char const * value = gb_get_env((char const *)key.text, mc_allocator);
+ return make_string_c(value);
+}
+
+void mc_free(String str) {
+ if (str.len) gb_free(mc_allocator, str.text);
+}
+
+void mc_free(String16 str) {
+ if (str.len) gb_free(mc_allocator, str.text);
+}
+
+void mc_free_all() {
+ gb_free_all(mc_allocator);
+}
+
+typedef struct _MC_Find_Data {
+ DWORD file_attributes;
+ String filename;
+} MC_Find_Data;
+
+
+HANDLE mc_find_first(String wildcard, MC_Find_Data *find_data) {
+ WIN32_FIND_DATAW _find_data;
+
+ String16 wildcard_wide = mc_string_to_wstring(wildcard);
+ defer (mc_free(wildcard_wide));
+
+ HANDLE handle = FindFirstFileW(wildcard_wide.text, &_find_data);
+ if (handle == INVALID_HANDLE_VALUE) return false;
+
+ find_data->file_attributes = _find_data.dwFileAttributes;
+ find_data->filename = mc_wstring_to_string(_find_data.cFileName);
+ return handle;
+}
+
+bool mc_find_next(HANDLE handle, MC_Find_Data *find_data) {
+ WIN32_FIND_DATAW _find_data;
+ bool success = !!FindNextFileW(handle, &_find_data);
+
+ find_data->file_attributes = _find_data.dwFileAttributes;
+ find_data->filename = mc_wstring_to_string(_find_data.cFileName);
+ return success;
+}
+
+void mc_find_close(HANDLE handle) {
+ FindClose(handle);
+}
//
// Call find_visual_studio_and_windows_sdk, look at the resulting
@@ -142,481 +186,598 @@ void free_resources(Find_Result_Utf8 *result) {
// COM objects for the ridiculous Microsoft craziness.
-
typedef WCHAR* BSTR;
typedef const WCHAR* LPCOLESTR;
struct DECLSPEC_UUID("B41463C3-8866-43B5-BC33-2B0676F7F42E") DECLSPEC_NOVTABLE ISetupInstance : public IUnknown
{
- virtual HRESULT STDMETHODCALLTYPE GetInstanceId(BSTR* pbstrInstanceId) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstallDate(LPFILETIME pInstallDate) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstallationName(BSTR* pbstrInstallationName) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstallationPath(BSTR* pbstrInstallationPath) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstallationVersion(BSTR* pbstrInstallationVersion) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetDisplayName(LCID lcid, BSTR* pbstrDisplayName) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetDescription(LCID lcid, BSTR* pbstrDescription) = 0;
- virtual HRESULT STDMETHODCALLTYPE ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstanceId(BSTR* pbstrInstanceId) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstallDate(LPFILETIME pInstallDate) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstallationName(BSTR* pbstrInstallationName) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstallationPath(BSTR* pbstrInstallationPath) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstallationVersion(BSTR* pbstrInstallationVersion) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetDisplayName(LCID lcid, BSTR* pbstrDisplayName) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetDescription(LCID lcid, BSTR* pbstrDescription) = 0;
+ virtual HRESULT STDMETHODCALLTYPE ResolvePath(LPCOLESTR pwszRelativePath, BSTR* pbstrAbsolutePath) = 0;
};
struct DECLSPEC_UUID("6380BCFF-41D3-4B2E-8B2E-BF8A6810C848") DECLSPEC_NOVTABLE IEnumSetupInstances : public IUnknown
{
- virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, ISetupInstance** rgelt, ULONG* pceltFetched) = 0;
- virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt) = 0;
- virtual HRESULT STDMETHODCALLTYPE Reset(void) = 0;
- virtual HRESULT STDMETHODCALLTYPE Clone(IEnumSetupInstances** ppenum) = 0;
+ virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, ISetupInstance** rgelt, ULONG* pceltFetched) = 0;
+ virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt) = 0;
+ virtual HRESULT STDMETHODCALLTYPE Reset(void) = 0;
+ virtual HRESULT STDMETHODCALLTYPE Clone(IEnumSetupInstances** ppenum) = 0;
};
struct DECLSPEC_UUID("42843719-DB4C-46C2-8E7C-64F1816EFD5B") DECLSPEC_NOVTABLE ISetupConfiguration : public IUnknown
{
- virtual HRESULT STDMETHODCALLTYPE EnumInstances(IEnumSetupInstances** ppEnumInstances) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstanceForCurrentProcess(ISetupInstance** ppInstance) = 0;
- virtual HRESULT STDMETHODCALLTYPE GetInstanceForPath(LPCWSTR wzPath, ISetupInstance** ppInstance) = 0;
+ virtual HRESULT STDMETHODCALLTYPE EnumInstances(IEnumSetupInstances** ppEnumInstances) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstanceForCurrentProcess(ISetupInstance** ppInstance) = 0;
+ virtual HRESULT STDMETHODCALLTYPE GetInstanceForPath(LPCWSTR wzPath, ISetupInstance** ppInstance) = 0;
};
// The beginning of the actual code that does things.
-
-struct Version_Data {
- i32 best_version[4]; // For Windows 8 versions, only two of these numbers are used.
- wchar_t const *best_name;
+struct Version_Data_Utf8 {
+ i32 best_version[4]; // For Windows 8 versions, only two of these numbers are used.
+ String best_name;
};
-bool os_file_exists(wchar_t const *name) {
- // @Robustness: What flags do we really want to check here?
+typedef void (*MC_Visit_Proc)(String short_name, String full_name, Version_Data_Utf8 *data);
+bool mc_visit_files(String dir_name, Version_Data_Utf8 *data, MC_Visit_Proc proc) {
- auto attrib = GetFileAttributesW(name);
- if (attrib == INVALID_FILE_ATTRIBUTES) return false;
- if (attrib & FILE_ATTRIBUTE_DIRECTORY) return false;
+ // Visit everything in one folder (non-recursively). If it's a directory
+ // that doesn't start with ".", call the visit proc on it. The visit proc
+ // will see if the filename conforms to the expected versioning pattern.
- return true;
+ String wildcard_name = mc_concat(dir_name, str_lit("\\*"));
+ defer (mc_free(wildcard_name));
+
+ MC_Find_Data find_data;
+
+ HANDLE handle = mc_find_first(wildcard_name, &find_data);
+ if (handle == INVALID_HANDLE_VALUE) return false;
+
+ bool success = true;
+ while (success) {
+ if ((find_data.file_attributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.filename[0] != '.')) {
+ String full_name = mc_concat(dir_name, str_lit("\\"), find_data.filename);
+ defer (mc_free(full_name));
+
+ proc(find_data.filename, full_name, data);
+ }
+
+ success = mc_find_next(handle, &find_data);
+ if (!success) break;
+ }
+ mc_find_close(handle);
+ return true;
}
-wchar_t *concat(wchar_t const *a, wchar_t const *b, wchar_t const *c = nullptr, wchar_t const *d = nullptr) {
- // Concatenate up to 4 wide strings together. Allocated with malloc.
- // If you don't like that, use a programming language that actually
- // helps you with using custom allocators. Or just edit the code.
+String find_windows_kit_root(HKEY key, String const version) {
+ // Given a key to an already opened registry entry,
+ // get the value stored under the 'version' subkey.
+ // If that's not the right terminology, hey, I never do registry stuff.
- isize len_a = string16_len(a);
- isize len_b = string16_len(b);
- isize len_c = string16_len(c);
- isize len_d = string16_len(d);
+ char *version_str = (char*)version.text;
- wchar_t *result = (wchar_t *)calloc(2, (len_a + len_b + len_c + len_d + 1));
- gb_memmove(result, a, len_a*2);
- gb_memmove(result + len_a, b, len_b*2);
+ DWORD required_length;
+ auto rc = RegQueryValueExA(key, version_str, NULL, NULL, NULL, &required_length);
+ if (rc != 0) return {};
- if (c) gb_memmove(result + len_a + len_b, c, len_c * 2);
- if (d) gb_memmove(result + len_a + len_b + len_c, d, len_d * 2);
+ DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating.
+ char *c_str = gb_alloc_array(mc_allocator, char, length);
- result[len_a + len_b + len_c + len_d] = 0;
+ rc = RegQueryValueExA(key, version_str, NULL, NULL, (LPBYTE)c_str, &length); // We know that version is zero-terminated...
+ if (rc != 0) return {};
- return result;
+ // The documentation says that if the string for some reason was not stored
+ // with zero-termination, we need to manually terminate it. Sigh!!
+
+ if (c_str[required_length]) {
+ c_str[required_length+1] = 0;
+ }
+
+ String value = make_string_c(c_str);
+
+ return value;
}
-typedef void (*Visit_Proc_W)(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data);
-bool visit_files_w(wchar_t const *dir_name, Version_Data *data, Visit_Proc_W proc) {
+void win10_best(String short_name, String full_name, Version_Data_Utf8 *data) {
+ // Find the Windows 10 subdirectory with the highest version number.
- // Visit everything in one folder (non-recursively). If it's a directory
- // that doesn't start with ".", call the visit proc on it. The visit proc
- // will see if the filename conforms to the expected versioning pattern.
+ int i0, i1, i2, i3;
+ auto success = sscanf_s((const char *const)short_name.text, "%d.%d.%d.%d", &i0, &i1, &i2, &i3);
+ if (success < 4) return;
- auto wildcard_name = concat(dir_name, L"\\*");
- defer (free(wildcard_name));
+ if (i0 < data->best_version[0]) return;
+ else if (i0 == data->best_version[0]) {
+ if (i1 < data->best_version[1]) return;
+ else if (i1 == data->best_version[1]) {
+ if (i2 < data->best_version[2]) return;
+ else if (i2 == data->best_version[2]) {
+ if (i3 < data->best_version[3]) return;
+ }
+ }
+ }
- WIN32_FIND_DATAW find_data;
- auto handle = FindFirstFileW(wildcard_name, &find_data);
- if (handle == INVALID_HANDLE_VALUE) return false;
+ // we have to copy_string and free here because visit_files free's the full_name string
+ // after we execute this function, so Win*_Data would contain an invalid pointer.
+ if (data->best_name.len > 0) mc_free(data->best_name);
- while (true) {
- if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (find_data.cFileName[0] != '.')) {
- auto full_name = concat(dir_name, L"\\", find_data.cFileName);
- defer (free(full_name));
+ data->best_name = copy_string(mc_allocator, full_name);
- proc(find_data.cFileName, full_name, data);
- }
-
- auto success = FindNextFileW(handle, &find_data);
- if (!success) break;
- }
-
- FindClose(handle);
-
- return true;
+ if (data->best_name.len > 0) {
+ data->best_version[0] = i0;
+ data->best_version[1] = i1;
+ data->best_version[2] = i2;
+ data->best_version[3] = i3;
+ }
}
+void win8_best(String short_name, String full_name, Version_Data_Utf8 *data) {
+ // Find the Windows 8 subdirectory with the highest version number.
-wchar_t *find_windows_kit_root(HKEY key, wchar_t const *version) {
- // Given a key to an already opened registry entry,
- // get the value stored under the 'version' subkey.
- // If that's not the right terminology, hey, I never do registry stuff.
+ int i0, i1;
+ auto success = sscanf_s((const char *const)short_name.text, "winv%d.%d", &i0, &i1);
+ if (success < 2) return;
- DWORD required_length;
- auto rc = RegQueryValueExW(key, version, NULL, NULL, NULL, &required_length);
- if (rc != 0) return NULL;
+ if (i0 < data->best_version[0]) return;
+ else if (i0 == data->best_version[0]) {
+ if (i1 < data->best_version[1]) return;
+ }
- DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating.
- wchar_t *value = (wchar_t *)calloc(1, length);
- if (!value) return NULL;
+ // we have to copy_string and free here because visit_files free's the full_name string
+ // after we execute this function, so Win*_Data would contain an invalid pointer.
+ if (data->best_name.len > 0) mc_free(data->best_name);
+ data->best_name = copy_string(mc_allocator, full_name);
- rc = RegQueryValueExW(key, version, NULL, NULL, (LPBYTE)value, &length); // We know that version is zero-terminated...
- if (rc != 0) return NULL;
-
- // The documentation says that if the string for some reason was not stored
- // with zero-termination, we need to manually terminate it. Sigh!!
-
- if (value[length]) {
- value[length+1] = 0;
- }
-
- return value;
+ if (data->best_name.len > 0) {
+ data->best_version[0] = i0;
+ data->best_version[1] = i1;
+ }
}
-void win10_best(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data) {
- // Find the Windows 10 subdirectory with the highest version number.
+void find_windows_kit_root(Find_Result_Utf8 *result) {
+ // Information about the Windows 10 and Windows 8 development kits
+ // is stored in the same place in the registry. We open a key
+ // to that place, first checking preferntially for a Windows 10 kit,
+ // then, if that's not found, a Windows 8 kit.
- int i0, i1, i2, i3;
- auto success = swscanf_s(short_name, L"%d.%d.%d.%d", &i0, &i1, &i2, &i3);
- if (success < 4) return;
+ HKEY main_key;
- if (i0 < data->best_version[0]) return;
- else if (i0 == data->best_version[0]) {
- if (i1 < data->best_version[1]) return;
- else if (i1 == data->best_version[1]) {
- if (i2 < data->best_version[2]) return;
- else if (i2 == data->best_version[2]) {
- if (i3 < data->best_version[3]) return;
- }
- }
- }
+ auto rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
+ 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &main_key);
+ if (rc != S_OK) return;
+ defer (RegCloseKey(main_key));
- // we have to copy_string and free here because visit_files free's the full_name string
- // after we execute this function, so Win*_Data would contain an invalid pointer.
- if (data->best_name) free((void *)data->best_name);
- data->best_name = _wcsdup(full_name);
+ // Look for a Windows 10 entry.
+ String windows10_root = find_windows_kit_root(main_key, str_lit("KitsRoot10"));
- if (data->best_name) {
- data->best_version[0] = i0;
- data->best_version[1] = i1;
- data->best_version[2] = i2;
- data->best_version[3] = i3;
- }
+ if (windows10_root.len > 0) {
+ defer (mc_free(windows10_root));
+
+ String windows10_lib = mc_concat(windows10_root, str_lit("Lib"));
+ defer (mc_free(windows10_lib));
+
+ Version_Data_Utf8 data = {0};
+ mc_visit_files(windows10_lib, &data, win10_best);
+ if (data.best_name.len > 0) {
+ result->windows_sdk_version = 10;
+ result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\"));
+ return;
+ }
+ mc_free(data.best_name);
+ }
+
+ // Look for a Windows 8 entry.
+ String windows8_root = find_windows_kit_root(main_key, str_lit("KitsRoot81"));
+
+ if (windows8_root.len > 0) {
+ defer (mc_free(windows8_root));
+
+ String windows8_lib = mc_concat(windows8_root, str_lit("Lib"));
+ defer (mc_free(windows8_lib));
+
+ Version_Data_Utf8 data = {0};
+ mc_visit_files(windows8_lib, &data, win8_best);
+ if (data.best_name.len > 0) {
+ result->windows_sdk_version = 8;
+ result->windows_sdk_root = mc_concat(data.best_name, str_lit("\\"));
+ return;
+ }
+ mc_free(data.best_name);
+ }
+ // If we get here, we failed to find anything.
}
-void win8_best(wchar_t const *short_name, wchar_t const *full_name, Version_Data *data) {
- // Find the Windows 8 subdirectory with the highest version number.
+bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result_Utf8 *result) {
+ // The name of this procedure is kind of cryptic. Its purpose is
+ // to fight through Microsoft craziness. The things that the fine
+ // Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
+ // THAT EVERYONE NEEDS TO FIND, are ridiculous garbage.
- int i0, i1;
- auto success = swscanf_s(short_name, L"winv%d.%d", &i0, &i1);
- if (success < 2) return;
+ // For earlier versions of Visual Studio, you'd find this information in the registry,
+ // similarly to the Windows Kits above. But no, now it's the future, so to ask the
+ // question "Where is the Visual Studio folder?" you have to do a bunch of COM object
+ // instantiation, enumeration, and querying. (For extra bonus points, try doing this in
+ // a new, underdeveloped programming language where you don't have COM routines up
+ // and running yet. So fun.)
+ //
+ // If all this COM object instantiation, enumeration, and querying doesn't give us
+ // a useful result, we drop back to the registry-checking method.
- if (i0 < data->best_version[0]) return;
- else if (i0 == data->best_version[0]) {
- if (i1 < data->best_version[1]) return;
- }
- // we have to copy_string and free here because visit_files free's the full_name string
- // after we execute this function, so Win*_Data would contain an invalid pointer.
- if (data->best_name) free((void *)data->best_name);
- data->best_name = _wcsdup(full_name);
+ auto rc = CoInitialize(NULL);
+ // "Subsequent valid calls return false." So ignore false.
+ if (rc != S_OK) return false;
- if (data->best_name) {
- data->best_version[0] = i0;
- data->best_version[1] = i1;
- }
+ GUID my_uid = {0x42843719, 0xDB4C, 0x46C2, {0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B}};
+ GUID CLSID_SetupConfiguration = {0x177F0C4A, 0x1CD3, 0x4DE7, {0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D}};
+
+ ISetupConfiguration *config = NULL;
+ HRESULT hr = 0;
+ hr = CoCreateInstance(CLSID_SetupConfiguration, NULL, CLSCTX_INPROC_SERVER, my_uid, (void **)&config);
+ if (hr == 0) {
+ defer (config->Release());
+
+ IEnumSetupInstances *instances = NULL;
+ hr = config->EnumInstances(&instances);
+ if (hr != 0) return false;
+ if (!instances) return false;
+ defer (instances->Release());
+
+ for (;;) {
+ ULONG found = 0;
+ ISetupInstance *instance = NULL;
+ auto hr = instances->Next(1, &instance, &found);
+ if (hr != S_OK) break;
+
+ defer (instance->Release());
+
+ wchar_t* inst_path_wide;
+ hr = instance->GetInstallationPath(&inst_path_wide);
+ if (hr != S_OK) continue;
+ defer (SysFreeString(inst_path_wide));
+
+ String inst_path = mc_wstring_to_string(inst_path_wide);
+ defer (mc_free(inst_path));
+
+ String tools_filename = mc_concat(inst_path, str_lit("\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"));
+ defer (mc_free(tools_filename));
+
+ gbFileContents tool_version = gb_file_read_contents(mc_allocator, true, (const char*)tools_filename.text);
+ defer (gb_file_free_contents(&tool_version));
+
+ String version_string = make_string((const u8*)tool_version.data, tool_version.size);
+ version_string = string_trim_whitespace(version_string);
+
+ String base_path = mc_concat(inst_path, str_lit("\\VC\\Tools\\MSVC\\"), version_string);
+ defer (mc_free(base_path));
+
+ String library_path = {};
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ library_path = mc_concat(base_path, str_lit("\\lib\\x64\\"));
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ library_path = mc_concat(base_path, str_lit("\\lib\\x86\\"));
+ } else {
+ continue;
+ }
+
+ String library_file = mc_concat(library_path, str_lit("vcruntime.lib"));
+
+ if (gb_file_exists((const char*)library_file.text)) {
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ result->vs_exe_path = mc_concat(base_path, str_lit("\\bin\\Hostx64\\x64\\"));
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ result->vs_exe_path = mc_concat(base_path, str_lit("\\bin\\Hostx86\\x86\\"));
+ } else {
+ continue;
+ }
+
+ result->vs_library_path = library_path;
+ return true;
+ }
+ /*
+ Ryan Saunderson said:
+ "Clang uses the 'SetupInstance->GetInstallationVersion' / ISetupHelper->ParseVersion to find the newest version
+ and then reads the tools file to define the tools path - which is definitely better than what i did."
+
+ So... @Incomplete: Should probably pick the newest version...
+ */
+ }
+ }
+
+ // If we get here, we didn't find Visual Studio 2017. Try earlier versions.
+ {
+ HKEY vs7_key;
+ rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &vs7_key);
+ if (rc != S_OK) return false;
+ defer (RegCloseKey(vs7_key));
+
+ // Hardcoded search for 4 prior Visual Studio versions. Is there something better to do here?
+ char const *versions[] = { "14.0", "13.0", "12.0", "11.0", "10.0", "9.0", };
+ const int NUM_VERSIONS = sizeof(versions) / sizeof(versions[0]);
+
+ for (int i = 0; i < NUM_VERSIONS; i++) {
+ char const *v = versions[i];
+
+ DWORD dw_type;
+ DWORD required_length;
+
+ auto rc = RegQueryValueExA(vs7_key, v, NULL, &dw_type, NULL, &required_length);
+ if ((rc == ERROR_FILE_NOT_FOUND) || (dw_type != REG_SZ)) {
+ continue;
+ }
+
+ DWORD length = required_length + 2; // The +2 is for the maybe optional zero later on. Probably we are over-allocating.
+ char *c_str = gb_alloc_array(mc_allocator, char, length);
+
+ rc = RegQueryValueExA(vs7_key, v, NULL, NULL, (LPBYTE)c_str, &length);
+ if (rc != 0) continue;
+
+ if (c_str[required_length]) {
+ c_str[required_length+1] = 0;
+ }
+ String base_path = make_string_c(c_str);
+
+ String lib_path = {};
+
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ lib_path = mc_concat(base_path, str_lit("VC\\Lib\\amd64\\"));
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ lib_path = mc_concat(base_path, str_lit("VC\\Lib\\"));
+ } else {
+ continue;
+ }
+
+ // Check to see whether a vcruntime.lib actually exists here.
+ String vcruntime_filename = mc_concat(lib_path, str_lit("vcruntime.lib"));
+ defer (mc_free(vcruntime_filename));
+
+ if (gb_file_exists((const char*)vcruntime_filename.text)) {
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ result->vs_exe_path = mc_concat(base_path, str_lit("VC\\bin\\"));
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ result->vs_exe_path = mc_concat(base_path, str_lit("VC\\bin\\x86_amd64\\"));
+ } else {
+ continue;
+ }
+ result->vs_library_path = lib_path;
+ return true;
+ }
+ mc_free(lib_path);
+ }
+ // If we get here, we failed to find anything.
+ }
+ return false;
}
-void find_windows_kit_root(Find_Result *result) {
- // Information about the Windows 10 and Windows 8 development kits
- // is stored in the same place in the registry. We open a key
- // to that place, first checking preferntially for a Windows 10 kit,
- // then, if that's not found, a Windows 8 kit.
+// NOTE(WalterPlinge): Environment variables can help to find Visual C++ and WinSDK paths for both
+// official and portable installations (like mmozeiko's portable msvc script). This will only use
+// the first paths it finds, and won't overwrite any values that `result` already has.
+bool find_msvc_install_from_env_vars(Find_Result_Utf8 *result) {
+ if (build_context.metrics.arch != TargetArch_amd64 && build_context.metrics.arch != TargetArch_i386) {
+ return false;
+ }
- HKEY main_key;
+ // We can find windows sdk using the following combination of env vars:
+ // (UniversalCRTSdkDir or WindowsSdkDir) and (WindowsSDKLibVersion or WindowsSDKVersion)
+ bool sdk_found = false;
- auto rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots",
- 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &main_key);
- if (rc != S_OK) return;
- defer (RegCloseKey(main_key));
+ // These appear to be suitable env vars used by Visual Studio
+ String win_sdk_ver_env = mc_get_env(str_lit("WindowsSDKVersion"));
+ String win_sdk_lib_env = mc_get_env(str_lit("WindowsSDKLibVersion"));
+ String win_sdk_dir_env = mc_get_env(str_lit("WindowsSdkDir"));
+ String crt_sdk_dir_env = mc_get_env(str_lit("UniversalCRTSdkDir"));
- // Look for a Windows 10 entry.
- auto windows10_root = find_windows_kit_root(main_key, L"KitsRoot10");
+ defer ({
+ mc_free(win_sdk_ver_env);
+ mc_free(win_sdk_lib_env);
+ mc_free(win_sdk_dir_env);
+ mc_free(crt_sdk_dir_env);
+ });
+ // NOTE(WalterPlinge): If any combination is found, let's just assume they are correct
+ if ((win_sdk_ver_env.len || win_sdk_lib_env.len) && (win_sdk_dir_env.len || crt_sdk_dir_env.len)) {
+ //? Maybe we need to handle missing '\' at end of strings, so far it doesn't seem an issue
+ String dir = win_sdk_dir_env.len ? win_sdk_dir_env : crt_sdk_dir_env;
+ String ver = win_sdk_ver_env.len ? win_sdk_ver_env : win_sdk_lib_env;
- if (windows10_root) {
- defer (free(windows10_root));
+ // These have trailing '\' as we are just composing the path
+ String um_dir = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("um\\x64\\")
+ : str_lit("um\\x86\\");
+ String ucrt_dir = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("ucrt\\x64\\")
+ : str_lit("ucrt\\x86\\");
+ result->windows_sdk_root = mc_concat(dir, str_lit("Lib\\"), ver);
+ result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir);
+ result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir);
- Version_Data data = {0};
- auto windows10_lib = concat(windows10_root, L"Lib");
- defer (free(windows10_lib));
+ sdk_found = true;
+ }
+ // If we haven't found it yet, we can loop through LIB for specific folders
+ //? This may not be robust enough using `um\x64` and `ucrt\x64`
+ if (!sdk_found) {
+ String lib = mc_get_env(str_lit("LIB"));
+ defer (mc_free(lib));
- visit_files_w(windows10_lib, &data, win10_best);
- if (data.best_name) {
- result->windows_sdk_version = 10;
- result->windows_sdk_root = concat(data.best_name, L"\\");
- return;
- }
- }
+ if (lib.len) {
+ // NOTE(WalterPlinge): I don't know if there's a chance for the LIB variable
+ // to be set without a trailing '\' (apart from manually), so we can just
+ // check paths without it (see use of `String end` in the loop below)
+ String um_dir = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("um\\x64")
+ : str_lit("um\\x86");
+ String ucrt_dir = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("ucrt\\x64")
+ : str_lit("ucrt\\x86");
- // Look for a Windows 8 entry.
- auto windows8_root = find_windows_kit_root(main_key, L"KitsRoot81");
+ isize lo = {0};
+ isize hi = {0};
+ for (isize c = 0; c <= lib.len; c += 1) {
+ if (c != lib.len && lib[c] != ';') {
+ continue;
+ }
+ hi = c;
+ String dir = substring(lib, lo, hi);
+ defer (lo = hi + 1);
- if (windows8_root) {
- defer (free(windows8_root));
+ // Remove the last slash so we can match with the strings above
+ String end = dir[dir.len - 1] == '\\'
+ ? substring(dir, 0, dir.len - 1)
+ : substring(dir, 0, dir.len);
- auto windows8_lib = concat(windows8_root, L"Lib");
- defer (free(windows8_lib));
+ // Find one and we can make the other
+ if (string_ends_with(end, um_dir)) {
+ result->windows_sdk_um_library_path = mc_concat(end, str_lit("\\"));
+ break;
+ } else if (string_ends_with(end, ucrt_dir)) {
+ result->windows_sdk_ucrt_library_path = mc_concat(end, str_lit("\\"));
+ break;
+ }
+ }
- Version_Data data = {0};
- visit_files_w(windows8_lib, &data, win8_best);
- if (data.best_name) {
- result->windows_sdk_version = 8;
- result->windows_sdk_root = concat(data.best_name, L"\\");
- return;
- }
- }
+ // Get the root from the one we found, and make the other
+ // NOTE(WalterPlinge): we need to copy the string so that we don't risk a double free
+ if (result->windows_sdk_um_library_path.len > 0) {
+ String root = substring(result->windows_sdk_um_library_path, 0, result->windows_sdk_um_library_path.len - 1 - um_dir.len);
+ result->windows_sdk_root = copy_string(mc_allocator, root);
+ result->windows_sdk_ucrt_library_path = mc_concat(result->windows_sdk_root, ucrt_dir, str_lit("\\"));
+ } else if (result->windows_sdk_ucrt_library_path.len > 0) {
+ String root = substring(result->windows_sdk_ucrt_library_path, 0, result->windows_sdk_ucrt_library_path.len - 1 - ucrt_dir.len);
+ result->windows_sdk_root = copy_string(mc_allocator, root);
+ result->windows_sdk_um_library_path = mc_concat(result->windows_sdk_root, um_dir, str_lit("\\"));
+ }
- // If we get here, we failed to find anything.
+ if (result->windows_sdk_root.len > 0) {
+ sdk_found = true;
+ }
+ }
+ }
+
+ // NOTE(WalterPlinge): So far this function assumes it will only be called if MSVC was
+ // installed using mmozeiko's portable msvc script, which uses the windows 10 sdk.
+ // This may need to be changed later if it ends up causing problems.
+ if (sdk_found && result->windows_sdk_version == 0) {
+ result->windows_sdk_version = 10;
+ }
+
+ bool vs_found = false;
+
+ if (result->vs_exe_path.len > 0 && result->vs_library_path.len > 0) {
+ vs_found = true;
+ }
+
+ // We can find visual studio using VCToolsInstallDir
+ if (!vs_found) {
+ String vctid = mc_get_env(str_lit("VCToolsInstallDir"));
+ defer (mc_free(vctid));
+
+ if (vctid.len) {
+ String exe = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("bin\\Hostx64\\x64\\")
+ : str_lit("bin\\Hostx86\\x86\\");
+ String lib = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("lib\\x64\\")
+ : str_lit("lib\\x86\\");
+
+ result->vs_exe_path = mc_concat(vctid, exe);
+ result->vs_library_path = mc_concat(vctid, lib);
+ vs_found = true;
+ }
+ }
+
+ // If we haven't found it yet, we can loop through Path for specific folders
+ if (!vs_found) {
+ String path = mc_get_env(str_lit("Path"));
+ defer (mc_free(path));
+
+ if (path.len) {
+ String exe = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("bin\\Hostx64\\x64")
+ : str_lit("bin\\Hostx86\\x86");
+ String lib = build_context.metrics.arch == TargetArch_amd64
+ ? str_lit("lib\\x64")
+ : str_lit("lib\\x86");
+
+ isize lo = {0};
+ isize hi = {0};
+ for (isize c = 0; c < path.len; c += 1) {
+ if (path[c] != ';') {
+ continue;
+ }
+
+ hi = c;
+ String dir = substring(path, lo, hi);
+ defer (lo = hi + 1);
+
+ String end = dir[dir.len - 1] == '\\'
+ ? substring(dir, 0, dir.len - 1)
+ : substring(dir, 0, dir.len);
+
+ // check if cl.exe and link.exe exist in this folder
+ String cl = mc_concat(end, str_lit("\\cl.exe"));
+ String link = mc_concat(end, str_lit("\\link.exe"));
+ defer (mc_free(cl));
+ defer (mc_free(link));
+
+ if (!string_ends_with(end, exe) || !gb_file_exists((char *)cl.text) || !gb_file_exists((char *)link.text)) {
+ continue;
+ }
+
+ String root = substring(end, 0, end.len - exe.len);
+ result->vs_exe_path = mc_concat(end, str_lit("\\"));
+ result->vs_library_path = mc_concat(root, lib, str_lit("\\"));
+
+ vs_found = true;
+ }
+ }
+ }
+
+ return sdk_found && vs_found;
}
-
-bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *result) {
- // The name of this procedure is kind of cryptic. Its purpose is
- // to fight through Microsoft craziness. The things that the fine
- // Visual Studio team want you to do, JUST TO FIND A SINGLE FOLDER
- // THAT EVERYONE NEEDS TO FIND, are ridiculous garbage.
-
- // For earlier versions of Visual Studio, you'd find this information in the registry,
- // similarly to the Windows Kits above. But no, now it's the future, so to ask the
- // question "Where is the Visual Studio folder?" you have to do a bunch of COM object
- // instantiation, enumeration, and querying. (For extra bonus points, try doing this in
- // a new, underdeveloped programming language where you don't have COM routines up
- // and running yet. So fun.)
- //
- // If all this COM object instantiation, enumeration, and querying doesn't give us
- // a useful result, we drop back to the registry-checking method.
-
-
- auto rc = CoInitialize(NULL);
- // "Subsequent valid calls return false." So ignore false.
- if (rc != S_OK) return false;
-
- GUID my_uid = {0x42843719, 0xDB4C, 0x46C2, {0x8E, 0x7C, 0x64, 0xF1, 0x81, 0x6E, 0xFD, 0x5B}};
- GUID CLSID_SetupConfiguration = {0x177F0C4A, 0x1CD3, 0x4DE7, {0xA3, 0x2C, 0x71, 0xDB, 0xBB, 0x9F, 0xA3, 0x6D}};
-
- ISetupConfiguration *config = NULL;
- HRESULT hr = 0;
- hr = CoCreateInstance(CLSID_SetupConfiguration, NULL, CLSCTX_INPROC_SERVER, my_uid, (void **)&config);
- if (hr == 0) {
- defer (config->Release());
-
- IEnumSetupInstances *instances = NULL;
- hr = config->EnumInstances(&instances);
- if (hr != 0) return false;
- if (!instances) return false;
- defer (instances->Release());
-
- for (;;) {
- ULONG found = 0;
- ISetupInstance *instance = NULL;
- auto hr = instances->Next(1, &instance, &found);
- if (hr != S_OK) break;
-
- defer (instance->Release());
-
- BSTR bstr_inst_path;
- hr = instance->GetInstallationPath(&bstr_inst_path);
- if (hr != S_OK) continue;
- defer (SysFreeString(bstr_inst_path));
-
- auto tools_filename = concat(bstr_inst_path, L"\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
- defer (free(tools_filename));
-
- FILE *f = nullptr;
- auto open_result = _wfopen_s(&f, tools_filename, L"rt");
- if (open_result != 0) continue;
- if (!f) continue;
- defer (fclose(f));
-
- LARGE_INTEGER tools_file_size;
- auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
- BOOL success = GetFileSizeEx(file_handle, &tools_file_size);
- if (!success) continue;
-
- auto version_bytes = (tools_file_size.QuadPart + 1) * 2; // Warning: This multiplication by 2 presumes there is no variable-length encoding in the wchars (wacky characters in the file could betray this expectation).
- if (version_bytes > 0x7FFFFFFF) continue; // Avoid overflow.
-
- wchar_t *version = (wchar_t *)calloc(1, (usize)version_bytes);
- defer (free(version));
-
- auto read_result = fgetws(version, (int)version_bytes, f);
- if (!read_result) continue;
-
- auto version_tail = wcschr(version, '\n');
- if (version_tail) *version_tail = 0; // Stomp the data, because nobody cares about it.
-
- wchar_t *library_path = nullptr;
- if (build_context.metrics.arch == TargetArch_amd64) {
- library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_i386) {
- library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x86\\");
- } else {
- continue;
- }
-
- auto library_file = concat(library_path, L"vcruntime.lib"); // @Speed: Could have library_path point to this string, with a smaller count, to save on memory flailing!
-
- if (os_file_exists(library_file)) {
- wchar_t *link_exe_path = nullptr;
- if (build_context.metrics.arch == TargetArch_amd64) {
- link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx64\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_i386) {
- link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx86\\x86\\");
- } else {
- continue;
- }
-
-
- result->vs_exe_path = link_exe_path;
- result->vs_library_path = library_path;
- return true;
- }
-
- /*
- Ryan Saunderson said:
- "Clang uses the 'SetupInstance->GetInstallationVersion' / ISetupHelper->ParseVersion to find the newest version
- and then reads the tools file to define the tools path - which is definitely better than what i did."
-
- So... @Incomplete: Should probably pick the newest version...
- */
- }
- }
-
- // If we get here, we didn't find Visual Studio 2017. Try earlier versions.
- {
- HKEY vs7_key;
- rc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &vs7_key);
- if (rc != S_OK) return false;
- defer (RegCloseKey(vs7_key));
-
- // Hardcoded search for 4 prior Visual Studio versions. Is there something better to do here?
- wchar_t const *versions[] = { L"14.0", L"13.0", L"12.0", L"11.0", L"10.0", L"9.0", };
- const int NUM_VERSIONS = sizeof(versions) / sizeof(versions[0]);
-
- for (int i = 0; i < NUM_VERSIONS; i++) {
- wchar_t const *v = versions[i];
-
- DWORD dw_type;
- DWORD cb_data;
-
- auto rc = RegQueryValueExW(vs7_key, v, NULL, &dw_type, NULL, &cb_data);
- if ((rc == ERROR_FILE_NOT_FOUND) || (dw_type != REG_SZ)) {
- continue;
- }
-
- auto buffer = (wchar_t *)calloc(1, cb_data);
- if (!buffer) return false;
- defer (free(buffer));
-
- rc = RegQueryValueExW(vs7_key, v, NULL, NULL, (LPBYTE)buffer, &cb_data);
- if (rc != 0) continue;
-
- // @Robustness: Do the zero-termination thing suggested in the RegQueryValue docs?
-
- wchar_t *lib_path = nullptr;
-
- if (build_context.metrics.arch == TargetArch_amd64) {
- lib_path = concat(buffer, L"VC\\Lib\\amd64\\");
- } else if (build_context.metrics.arch == TargetArch_i386) {
- lib_path = concat(buffer, L"VC\\Lib\\");
- } else {
- continue;
- }
-
- // Check to see whether a vcruntime.lib actually exists here.
- auto vcruntime_filename = concat(lib_path, L"vcruntime.lib");
- defer (free(vcruntime_filename));
-
- if (os_file_exists(vcruntime_filename)) {
- if (build_context.metrics.arch == TargetArch_amd64) {
- result->vs_exe_path = concat(buffer, L"VC\\bin\\");
- } else if (build_context.metrics.arch == TargetArch_i386) {
- // result->vs_exe_path = concat(buffer, L"VC\\bin\\amd64_x86\\");
- result->vs_exe_path = concat(buffer, L"VC\\bin\\x86_amd64\\");
- } else {
- continue;
- }
-
- result->vs_library_path = lib_path;
- return true;
- }
-
- free(lib_path);
- }
-
- // If we get here, we failed to find anything.
- }
-
- return false;
-}
-
-
-Find_Result find_visual_studio_and_windows_sdk() {
- Find_Result result = {};
-
- find_windows_kit_root(&result);
-
-
- if (result.windows_sdk_root) {
- if (build_context.metrics.arch == TargetArch_amd64) {
- result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x64\\");
- result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_i386) {
- result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x86\\");
- result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x86\\");
- }
- }
-
- bool ok = find_visual_studio_by_fighting_through_microsoft_craziness(&result);
-
- if (!ok) {
- result.vs_exe_path = concat(L"", L"");
- result.vs_library_path = concat(L"", L"");
- }
-
- return result;
-}
-
-String mc_wstring_to_string(wchar_t const *str) {
- return string16_to_string(mc_allocator, make_string16_c(str));
-}
-
-
Find_Result_Utf8 find_visual_studio_and_windows_sdk_utf8() {
- Find_Result result = find_visual_studio_and_windows_sdk();
- defer (free_resources(&result));
+ Find_Result_Utf8 r = {};
+ find_windows_kit_root(&r);
- Find_Result_Utf8 r = {};
- r.windows_sdk_version = result.windows_sdk_version;
+ if (r.windows_sdk_root.len > 0) {
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x64\\"));
+ r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x64\\"));
+ } else if (build_context.metrics.arch == TargetArch_i386) {
+ r.windows_sdk_um_library_path = mc_concat(r.windows_sdk_root, str_lit("um\\x86\\"));
+ r.windows_sdk_ucrt_library_path = mc_concat(r.windows_sdk_root, str_lit("ucrt\\x86\\"));
+ }
+ }
- r.windows_sdk_root = mc_wstring_to_string(result.windows_sdk_root);
- r.windows_sdk_um_library_path = mc_wstring_to_string(result.windows_sdk_um_library_path);
- r.windows_sdk_ucrt_library_path = mc_wstring_to_string(result.windows_sdk_ucrt_library_path);
- r.vs_exe_path = mc_wstring_to_string(result.vs_exe_path);
- r.vs_library_path = mc_wstring_to_string(result.vs_library_path);
+ find_visual_studio_by_fighting_through_microsoft_craziness(&r);
+
+ bool all_found =
+ r.windows_sdk_root.len > 0 &&
+ r.windows_sdk_um_library_path.len > 0 &&
+ r.windows_sdk_ucrt_library_path.len > 0 &&
+ r.vs_exe_path.len > 0 &&
+ r.vs_library_path.len > 0;
+
+ if (!all_found) {
+ find_msvc_install_from_env_vars(&r);
+ }
#if 0
- printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root));
- printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path));
- printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path));
- printf("vs_exe_path: %.*s\n", LIT(r.vs_exe_path));
- printf("vs_library_path: %.*s\n", LIT(r.vs_library_path));
+ printf("windows_sdk_root: %.*s\n", LIT(r.windows_sdk_root));
+ printf("windows_sdk_um_library_path: %.*s\n", LIT(r.windows_sdk_um_library_path));
+ printf("windows_sdk_ucrt_library_path: %.*s\n", LIT(r.windows_sdk_ucrt_library_path));
+ printf("vs_exe_path: %.*s\n", LIT(r.vs_exe_path));
+ printf("vs_library_path: %.*s\n", LIT(r.vs_library_path));
- gb_exit(1);
+ gb_exit(1);
#endif
- return r;
-}
-
+ return r;
+}
\ No newline at end of file
diff --git a/src/odin_compiler.natvis b/src/odin_compiler.natvis
new file mode 100644
index 000000000..845eaf1c0
--- /dev/null
+++ b/src/odin_compiler.natvis
@@ -0,0 +1,28 @@
+
+
+
+ {text,[len]s8}
+ text,[len]s8
+
+
+ {{ size={count} capacity={capacity} }}
+
+
+ | |