mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
860 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f96fbc94c8 | |||
| bb62bed981 | |||
| bc6b8c5332 | |||
| 6ab6447791 | |||
| f61c4715c1 | |||
| 3061bc8478 | |||
| d035d48c8e | |||
| b55b1ffe14 | |||
| 620d5d34f7 | |||
| f9654b6c36 | |||
| 6659ceb551 | |||
| 5aa591d884 | |||
| efe91b1f91 | |||
| 7c99884afb | |||
| dfd7a194ed | |||
| 2ddb27869b | |||
| 5c608b01ba | |||
| 2bd85e764e | |||
| 822e4894f2 | |||
| ce2e23849e | |||
| 099995e7dd | |||
| 72f4186b21 | |||
| 3742d9e7e9 | |||
| 4ac1218bf8 | |||
| b171cc41e6 | |||
| efc3a9e69d | |||
| 307c58d908 | |||
| ae02e9c34a | |||
| 139fa55c27 | |||
| 562bb6e4c4 | |||
| ef2931d4a5 | |||
| 2d4aa2be6d | |||
| 42b42db675 | |||
| 73e9dbbf8c | |||
| 0971a59493 | |||
| 627c91124a | |||
| 4eba717281 | |||
| 9623e5e032 | |||
| 805cc48f03 | |||
| d894fb3708 | |||
| b6ca913cff | |||
| 39db428603 | |||
| 992502f03b | |||
| edc3a9392a | |||
| f881ebd007 | |||
| 11ea03d2e8 | |||
| 99b4d59f44 | |||
| dfeefc5179 | |||
| ab46406f4d | |||
| 48ad147818 | |||
| 79d49f1955 | |||
| 1ccc8700e4 | |||
| f38d70a235 | |||
| b37b7a0f72 | |||
| f8d7f42208 | |||
| db0756a119 | |||
| 1a4e25f141 | |||
| 79ade6ac7b | |||
| ecce1d9974 | |||
| 834308d8ce | |||
| 9e73189d63 | |||
| 11bddf270c | |||
| 0818a272e2 | |||
| 9750b1162a | |||
| 3106aaaa3d | |||
| d31d4c9bd6 | |||
| 6993777d36 | |||
| 54c044ee09 | |||
| 2e5cecf9e6 | |||
| 7acb49eefb | |||
| 0f6c1f3482 | |||
| 1ee0fe7457 | |||
| 1a18481d8b | |||
| 28c61c0f5d | |||
| 597fb452b1 | |||
| 5961a63880 | |||
| cce5e595e5 | |||
| e7d72f6848 | |||
| 7dcad45e0d | |||
| 3772ea6ae1 | |||
| 2cc2eb1ec0 | |||
| 8a789e33b0 | |||
| 2f86f8f8e0 | |||
| 02f9a27f46 | |||
| 6cb605a025 | |||
| 9f3e42e4ef | |||
| 71d987bd2e | |||
| 637899467c | |||
| 5bdb424c6b | |||
| c62cfddb9c | |||
| 14a4c28f8f | |||
| f1e1814ff9 | |||
| b468cf141b | |||
| 787ea1feba | |||
| 91477e9e69 | |||
| cfd0dfd2bf | |||
| 46b1868185 | |||
| 4c4de1d6c4 | |||
| c8b30de771 | |||
| 4f3837f0e6 | |||
| 76848e8807 | |||
| 12902821d6 | |||
| f5549f6bde | |||
| 3825eab989 | |||
| 3cd6ae311d | |||
| 26cfc0257d | |||
| 1d31eabb6e | |||
| 8cd2797b2e | |||
| 11f5236434 | |||
| 220485a2d2 | |||
| eb274cf316 | |||
| aa542980ce | |||
| e0240c186f | |||
| ae58502a21 | |||
| 6a3697279c | |||
| c19ec5d65d | |||
| 15dca449c9 | |||
| dda985f49d | |||
| 12256beeb2 | |||
| 0858ae2024 | |||
| 6c18864291 | |||
| ae57284912 | |||
| 001837e6bb | |||
| 28523f17e2 | |||
| ae2af8315e | |||
| adbb3bb75f | |||
| 6181c4edb3 | |||
| 830c194da5 | |||
| 1830c1e57c | |||
| e5735af6d6 | |||
| a6b0ae71b2 | |||
| 3365baee8f | |||
| cc88dd0b71 | |||
| f050bfe872 | |||
| ab71acc3a5 | |||
| 0a85d1af6b | |||
| 68adadb01a | |||
| d56f458d11 | |||
| a65eadee63 | |||
| 16dfae62bc | |||
| fe680a8b1f | |||
| 54fe9f3eb1 | |||
| cbc6c2666b | |||
| a4d0ac1802 | |||
| 0dc29a7208 | |||
| a9321bc73f | |||
| e3f0ab7c3d | |||
| 5643ea1ba2 | |||
| 3b6523fbd9 | |||
| ffc4f01470 | |||
| e326f41d16 | |||
| 1d0ac72e4a | |||
| b216e44870 | |||
| 7d39b26cf4 | |||
| 884d5fed9f | |||
| ec84188597 | |||
| 85ac95f81b | |||
| 042550cf87 | |||
| b3ebff715a | |||
| 1ee60663bb | |||
| 59da98d3f0 | |||
| 2d41a42f61 | |||
| e1e4a916a5 | |||
| 71f94bff76 | |||
| c7d6467cfa | |||
| 79a3c0b36c | |||
| 966249c10a | |||
| acc010cba5 | |||
| 89f4e7a8db | |||
| 55f4eabecd | |||
| d0fc9aa069 | |||
| 8be9b5082c | |||
| 708907df31 | |||
| 70586b1cf8 | |||
| 877a78d6ba | |||
| 3928614326 | |||
| 5e5f5bfa8d | |||
| 3a1a7b40f9 | |||
| 835d7dcab2 | |||
| 28816dc491 | |||
| ccdc3438be | |||
| 60711dd355 | |||
| fad3947e26 | |||
| d8e5b2d1a4 | |||
| 2d26ad0226 | |||
| 45d3c6c0d3 | |||
| c6bffd7c35 | |||
| 462d81430c | |||
| d3cada5bd6 | |||
| cdbf831a7a | |||
| 0718f14774 | |||
| a6fe656f21 | |||
| dc5da7933a | |||
| 96fc9138d4 | |||
| 6512a3e5f2 | |||
| 49f2124df0 | |||
| a11d6e696a | |||
| 1705ba8069 | |||
| 8d2c4a78a1 | |||
| 8504ff920b | |||
| e34a9e6185 | |||
| c3c7834246 | |||
| 1ab40d8600 | |||
| 92ce02dab0 | |||
| 8abe9ef507 | |||
| d0e04bf569 | |||
| b92599879a | |||
| 0e91298fd1 | |||
| e515220694 | |||
| a55683d287 | |||
| fa4e95105f | |||
| 1e01085ef7 | |||
| 04a1f869b5 | |||
| e04ba7530d | |||
| ea055f1465 | |||
| 3b2c867817 | |||
| 3de23eb0bf | |||
| 5de3b07e2b | |||
| c0ca4d4635 | |||
| efe4b71bae | |||
| bc37bd5429 | |||
| 5f20e04259 | |||
| 9bef5ec01a | |||
| cdf873542b | |||
| 4742690dec | |||
| 3a16f1e854 | |||
| 877400dd12 | |||
| a4e3201113 | |||
| a99cc2fd70 | |||
| 5fe4c33d0e | |||
| 4d9d38cc28 | |||
| 5b71ffd4f9 | |||
| c2ca24a486 | |||
| e5aff6fd6d | |||
| 3eb8aa8268 | |||
| 6d1c32eb77 | |||
| ba776a3c9f | |||
| cd7e260f4e | |||
| ba67e474d3 | |||
| b92a8c513e | |||
| 13572aeef0 | |||
| 5081ea1a0c | |||
| e9e7ce2606 | |||
| 915dcb0c28 | |||
| 8236c6d4b7 | |||
| 555fe37ad8 | |||
| 881f667558 | |||
| 0a99595efe | |||
| 268491b224 | |||
| 49ea9ed722 | |||
| d7108416c9 | |||
| b136630856 | |||
| fa6f31186a | |||
| b027b1d60f | |||
| 7ed1d931cb | |||
| 2570296b01 | |||
| f0a4526250 | |||
| c39332c7e7 | |||
| 3f4b6b22dc | |||
| e0549df03e | |||
| e46662a546 | |||
| 360a74e2fe | |||
| 597c4591bc | |||
| 80833ed703 | |||
| 106302189c | |||
| 05c5f98e8e | |||
| d556fa2cd8 | |||
| 9bd7f023b2 | |||
| 398109ac84 | |||
| 12b870ba66 | |||
| 6202fb8373 | |||
| ced818ad54 | |||
| ccbb6df749 | |||
| 6eb505a677 | |||
| 619783ca1b | |||
| 642aa0bc4b | |||
| 45b3067068 | |||
| b7858a66b9 | |||
| 4e203feaf4 | |||
| a513b47780 | |||
| 547a2831c7 | |||
| 5c52ffe24e | |||
| a5763d6fee | |||
| 95482c554d | |||
| 10758710d4 | |||
| 86cf9383ea | |||
| 307977d4cf | |||
| 1beff539d7 | |||
| df578d6ec5 | |||
| 6aae381e83 | |||
| 7ee9051a56 | |||
| eb11edabe0 | |||
| c067b90403 | |||
| 5b6770f3d2 | |||
| 718b80ba39 | |||
| 4d052d5119 | |||
| 7e4c643401 | |||
| e920338f21 | |||
| af2048570c | |||
| 1ee4f849cb | |||
| 703393fc63 | |||
| 81420ab246 | |||
| c94d19718b | |||
| e25c72ecdd | |||
| 780b81a59f | |||
| 9f1dda701d | |||
| e597a8d72e | |||
| de9a4b5164 | |||
| 319aca3101 | |||
| 9dc2c01aaa | |||
| 6164672421 | |||
| 61906613b0 | |||
| 3b48fa8e7d | |||
| 324b7d65e7 | |||
| 373a60b9ef | |||
| 2ef22e86e0 | |||
| 830f4f540f | |||
| 56ff5496bc | |||
| 20fbece14c | |||
| 9fbfd86cde | |||
| 7547bc66cf | |||
| 18a9fa7355 | |||
| b32af841c5 | |||
| 66b4252931 | |||
| 2c95eaa418 | |||
| 7382f52dc9 | |||
| 49dd299999 | |||
| e391b05513 | |||
| 2de62910fc | |||
| fc77b5b4ac | |||
| a83d916fad | |||
| e71a641379 | |||
| e2eca45188 | |||
| 4d78540658 | |||
| b83c3f265b | |||
| 30f5a3bb93 | |||
| 2e1e1e6034 | |||
| 991479fbf9 | |||
| 5660f98cc3 | |||
| 5bf0f9d630 | |||
| 15b72119eb | |||
| dc30e7a200 | |||
| db2293144a | |||
| 5016f45429 | |||
| 9fa4aa40b7 | |||
| 52f60c706a | |||
| fff4ead96a | |||
| 3574341b6b | |||
| cbabc80d92 | |||
| f4cf88c2ca | |||
| 6db95b554f | |||
| 105de7705a | |||
| 584dffea14 | |||
| 41b6d215bb | |||
| 9274f29ca9 | |||
| 08c87e57f8 | |||
| b21cdd5037 | |||
| 63ab8b2418 | |||
| cb7a343caf | |||
| 40542e6e26 | |||
| 9da05dd4cb | |||
| ae9da0abfb | |||
| d3ea334e7a | |||
| d76132a3fb | |||
| 223c473cf6 | |||
| fd57cfa1ae | |||
| f23bd2dc27 | |||
| 69062ba3ab | |||
| e75563cb32 | |||
| d63885a495 | |||
| f28a34fa99 | |||
| a1e8de4e00 | |||
| d247ba4751 | |||
| 27b7dc336a | |||
| 60a7c68aa6 | |||
| 78c103e62c | |||
| ffec1c77f2 | |||
| 5357181484 | |||
| 33ddb3ad4d | |||
| 1cd453db14 | |||
| 3b5932699c | |||
| bada81159d | |||
| 652da98c70 | |||
| e14e2c3b4d | |||
| f96a897821 | |||
| b74ae77745 | |||
| 564226be02 | |||
| f6c45fc68a | |||
| 35ba5771a5 | |||
| b2461f7192 | |||
| 60a54f404b | |||
| 921f261377 | |||
| d70a555c1c | |||
| 4c339360e9 | |||
| 731dad480d | |||
| a0f2357cb3 | |||
| e86ac75e9c | |||
| f51de2e488 | |||
| 5efefdcf16 | |||
| cabb2bb992 | |||
| d560f6c920 | |||
| 21432ba96e | |||
| c341597657 | |||
| 2a1420d4e7 | |||
| 28d88f6af4 | |||
| c4d2d287fc | |||
| 6a85546b76 | |||
| 2e92d0c821 | |||
| a499a3aa5e | |||
| 23ab3c4713 | |||
| da300aa9c3 | |||
| e225158a6f | |||
| 2ce55783d2 | |||
| 14eeee40b2 | |||
| 038dea9202 | |||
| 0ae3484171 | |||
| 54976c3249 | |||
| 4c06b44315 | |||
| 678b58e0b1 | |||
| 8f913c656c | |||
| 001b48a5c6 | |||
| 54929a1b92 | |||
| 92780e2683 | |||
| 2891988d3b | |||
| c1728914c6 | |||
| ed2f49e8d2 | |||
| 8a76a370a9 | |||
| 1160fd4331 | |||
| 0134c38759 | |||
| d079095517 | |||
| 028d628e9f | |||
| 5e4b62acfe | |||
| 9366fa8e95 | |||
| 369db3a8e3 | |||
| 8c360b2a3c | |||
| b66e7bed45 | |||
| e919482aa8 | |||
| dce45e7d58 | |||
| 1a0877e965 | |||
| 0361a18551 | |||
| 83d90f1463 | |||
| f661ae9d09 | |||
| bee4cb57f2 | |||
| 53b670b889 | |||
| e2600a3e44 | |||
| 25101b2ae0 | |||
| 4e7867fcc1 | |||
| 101ee64165 | |||
| 4c3e65791e | |||
| a9c8031b61 | |||
| afb3033913 | |||
| 2ad26640a2 | |||
| 2c0b08145f | |||
| aa9c9eda9e | |||
| 1353d61894 | |||
| 88ba6d8015 | |||
| 8b288a2072 | |||
| 4e90644527 | |||
| 6651b65373 | |||
| 705352099f | |||
| 2e28c9d793 | |||
| 2fe660a1d7 | |||
| b03ce0e9b4 | |||
| 386f5f596d | |||
| add53228b2 | |||
| d90008cc52 | |||
| dbf8f9ab38 | |||
| 81a99cf67b | |||
| 876af6fb02 | |||
| b3734a5f77 | |||
| 419ab6f00c | |||
| 5558b55e9f | |||
| 4b14d608f4 | |||
| 9428d86f2b | |||
| ddebf0daf2 | |||
| 3a44c62ecf | |||
| 184efd4f49 | |||
| 6b3c4cc379 | |||
| 0b137e087c | |||
| 37790c13a0 | |||
| 82057f08ce | |||
| 1553421e1a | |||
| f3ea109e6f | |||
| 90dbfe7660 | |||
| 125bad3154 | |||
| 30c83d6c81 | |||
| 4f12c118a5 | |||
| 423775d50e | |||
| 860a5c3e86 | |||
| 649e02f209 | |||
| b449305cc1 | |||
| 49bee6bad0 | |||
| ac277a1cce | |||
| a17310a83c | |||
| b509946b13 | |||
| a69ea58388 | |||
| 30530d058c | |||
| 436928d06a | |||
| 32a502d14e | |||
| 0d665c637f | |||
| 1b6a14ac39 | |||
| 367013f589 | |||
| c980a30bad | |||
| 78b459590c | |||
| 054e241033 | |||
| f7e9649be4 | |||
| fd1f6ec75c | |||
| 6b0d7cb26c | |||
| 3aea08df78 | |||
| 3c6f90e552 | |||
| 3703ca4df4 | |||
| 41b8281c73 | |||
| acd1f83bd0 | |||
| ba8371104d | |||
| 991682e9fd | |||
| f0de994059 | |||
| ebb2a9812c | |||
| 265c05927f | |||
| 05ad38ae2d | |||
| 596a2c8355 | |||
| 9f52b2c283 | |||
| 8035a407a6 | |||
| 97760c3fa4 | |||
| d75291097e | |||
| db632b7e22 | |||
| 1a75dfe075 | |||
| e00d88d82e | |||
| 04cce1826b | |||
| cc28cda053 | |||
| cfabc0e61f | |||
| 91b534d128 | |||
| 3268f43340 | |||
| 05e374934d | |||
| 3e1ff0ec67 | |||
| 65945dac09 | |||
| 1608da2dc8 | |||
| c340827381 | |||
| 74fa7ca25d | |||
| 5a9223afda | |||
| febcd73323 | |||
| df06236076 | |||
| b0d3fbba47 | |||
| adb6c7637e | |||
| 425f83b17d | |||
| 976415ff9d | |||
| 4d7fb3e8d6 | |||
| bcca3bf322 | |||
| 74aaa3408f | |||
| 2a5beee88c | |||
| cec9f7abfe | |||
| 284a9cd4c3 | |||
| 5955c101d4 | |||
| f80b910ba3 | |||
| 2b0521347b | |||
| 0c06a8d154 | |||
| b0e3a4e276 | |||
| b651466630 | |||
| 24c09c9201 | |||
| e48346a9ee | |||
| 9bd8bdaa5a | |||
| a137699d95 | |||
| f6a56c2f82 | |||
| dffa791607 | |||
| 5ce6555721 | |||
| 53b3ad186f | |||
| 82c1c5b3fe | |||
| 6d880bc3bb | |||
| 40281d595d | |||
| 85fab55e57 | |||
| 1d2eb8055e | |||
| 9e0b69312b | |||
| bbddbba340 | |||
| 0d01a6f552 | |||
| ae3672608d | |||
| e5c39fb2a9 | |||
| eb4b3f5976 | |||
| dbb070524f | |||
| 36b0b50ba4 | |||
| ac46b2053d | |||
| 0ffcccdae5 | |||
| 4777bd607e | |||
| 39e9b50482 | |||
| 30adb9c770 | |||
| b1d1497f4b | |||
| 9df3a94d33 | |||
| d4f335d068 | |||
| 74341b9b74 | |||
| 66ee2cb6ed | |||
| 1d4881cbbe | |||
| 04b917a60a | |||
| e6c99cd289 | |||
| 6bc5584add | |||
| 121f0185d6 | |||
| e7999f8450 | |||
| 0b29e42adb | |||
| fcc8b89e6b | |||
| 529d1c78c7 | |||
| 414486829a | |||
| 3e05be8eb8 | |||
| ae24a8e5ae | |||
| d2588f9d1d | |||
| 1eb9994d88 | |||
| a43b89f36e | |||
| 0ed34af19d | |||
| 71729c2855 | |||
| 6c8c430c2a | |||
| 57b97ad0bd | |||
| 56f7a859df | |||
| e5e14b9947 | |||
| 3d8bf36a30 | |||
| 85f7c2d040 | |||
| 26ea8f6dcb | |||
| e05fe1837d | |||
| 94762b56f6 | |||
| b3b688fa50 | |||
| 5eaa8de8f9 | |||
| 26d3c54aff | |||
| 349a62121c | |||
| bbb0e14633 | |||
| 42312d9def | |||
| 065d0e4ee3 | |||
| b772ad7094 | |||
| 444d366c39 | |||
| 8e4233b86a | |||
| 6424966b7a | |||
| 4e42d7df43 | |||
| 580ee5cc4a | |||
| 56a98a483f | |||
| df7a4eda8a | |||
| 91cc0b282a | |||
| 01d8aea4df | |||
| ee904060c5 | |||
| afb5538e83 | |||
| 1f24f105cc | |||
| 8f39ebbe5a | |||
| c1e720a49b | |||
| f38c8875b2 | |||
| e7e51f53ce | |||
| 5259de5872 | |||
| e2b9c87aa8 | |||
| 8c7cf0dbb0 | |||
| e6e9375b09 | |||
| c6096f9205 | |||
| 11614c2649 | |||
| 793bc8c585 | |||
| 335e88b738 | |||
| b77ea94976 | |||
| ae17a51c0d | |||
| ee7a83f124 | |||
| 67ac551a2f | |||
| 572ac616c1 | |||
| 96bf6a5bcb | |||
| c43d66c286 | |||
| 95fb5fa46c | |||
| d614913c11 | |||
| 3bfaac0844 | |||
| 14d0cbf6d7 | |||
| 61a163d773 | |||
| 3a644dad78 | |||
| d2c1c719bd | |||
| 333db4dc94 | |||
| cbcf4b6071 | |||
| e6e0aba8c3 | |||
| 85097a9958 | |||
| 7791c343c4 | |||
| 3bd762591a | |||
| 8e3b77aba8 | |||
| 36e3a02f67 | |||
| 566a242ba3 | |||
| 1e3b3c107c | |||
| 2ac33285c1 | |||
| 7cb8016df3 | |||
| cf3c5a878a | |||
| 2d20bde495 | |||
| b9e347ef50 | |||
| 6707c8750e | |||
| e5502c13ee | |||
| f30d2e43ea | |||
| 6c73f9d3fd | |||
| 1161aa829d | |||
| 01519f2fd5 | |||
| 33aad3a8ce | |||
| 4262c125c5 | |||
| a09d5959ef | |||
| d7bd3f8402 | |||
| 0fff6a2b74 | |||
| f4c0405221 | |||
| 49d337c830 | |||
| 294092979e | |||
| c454ede184 | |||
| d854c5003c | |||
| 66d8776b83 | |||
| ba6ecf35cf | |||
| 10cc9cf661 | |||
| 2db971eedd | |||
| 1775e80b41 | |||
| e4a93619db | |||
| 4d14b3bcb4 | |||
| 9f4f5f9346 | |||
| 0fae31fb54 | |||
| 8987a6630c | |||
| 10ff8e0426 | |||
| a0ae02168a | |||
| a3c1ac2030 | |||
| 629b248f53 | |||
| 62a72f0163 | |||
| 655931f0ea | |||
| ca36fabfc0 | |||
| 7bd62481ad | |||
| fbd27d7c45 | |||
| 3546391311 | |||
| 24c812115e | |||
| 28be0ad69b | |||
| f0980c0a98 | |||
| 1df4aa90ce | |||
| 6b3cf051f8 | |||
| 4ecd6e592b | |||
| dbddec33c8 | |||
| 401a5955a4 | |||
| 9a3b4167bb | |||
| 13bc6eeea4 | |||
| 2da18b6d33 | |||
| 6d37ed12d2 | |||
| eab23cd5b7 | |||
| d233706a2d | |||
| f1ab17ed4e | |||
| 6113164211 | |||
| 4db462a703 | |||
| a22c6d6c0c | |||
| 59fb7b020a | |||
| 65f079ebc4 | |||
| d16aa79492 | |||
| 5af0acc4af | |||
| a459364de3 | |||
| 277ef1a68f | |||
| 193c7c82c8 | |||
| f7d8ba408c | |||
| 9a8759efef | |||
| 054948e701 | |||
| 1c5ddd65b4 | |||
| b8697fb4ed | |||
| 03570275c1 | |||
| b5587f1937 | |||
| c4c6975f1b | |||
| 0be0fb2a57 | |||
| 115e6e7f9e | |||
| 3868a9a0f0 | |||
| ba5050ac7c | |||
| d936ca1ea0 | |||
| fd8c4d58bb | |||
| ce4b7b8b7d | |||
| 069a47220e | |||
| 66e4aaffc5 | |||
| 81336b58cb | |||
| b201670f7a | |||
| 4b051a0d3b | |||
| 45353465a6 | |||
| c63cb98019 | |||
| 773cf5ca08 | |||
| 2db03cb4a5 | |||
| eed873c6ec | |||
| 3d2d461867 | |||
| 36392d658e | |||
| 82696179e8 | |||
| 188bc28f6a | |||
| 240da5c8e0 | |||
| 689a0c0b49 | |||
| bc16b290ba | |||
| 96d32680fe | |||
| d782b3d21d | |||
| ed089b44b9 | |||
| 33f4af2e19 | |||
| 69f7382eec | |||
| 7e3293fc20 | |||
| e4a8283327 | |||
| 001baf4419 | |||
| d167290b28 | |||
| f4879d4723 | |||
| fd81c06c35 | |||
| 94afcec757 | |||
| 4f28e9e1fb | |||
| 0622509807 | |||
| 9ca2246bac | |||
| 647e2cafd7 | |||
| 5df854fcef | |||
| 260089431e | |||
| d0d8da8c08 | |||
| d1365b3466 | |||
| c949ca2a5c | |||
| d974b29f67 | |||
| cc7316bb35 | |||
| a0d8dcd974 | |||
| c642e326ce | |||
| 362a118782 | |||
| 3ab481df17 | |||
| 4e7150b470 | |||
| 1ced92be47 | |||
| 15dbea6899 | |||
| c4081393c1 | |||
| 1d81b73df9 | |||
| 18f885efab | |||
| bba088bee7 | |||
| 6cbb6bef0b | |||
| 8744c60563 | |||
| 8197c02dcf | |||
| 9faf0020cc | |||
| 53075e2570 | |||
| 264ca00db7 | |||
| 6b65ef6d88 | |||
| 5957d7f7be | |||
| 35c102137f | |||
| 5427d14416 | |||
| 178236d1ff | |||
| 736c880ba9 | |||
| 126f7aa892 | |||
| 2957f007e3 | |||
| 04501c93fe | |||
| 4236519b84 | |||
| e4944b4f2e | |||
| 2deb2f8eeb | |||
| 3fa398ec43 | |||
| 1851674b50 | |||
| c5ef5279d4 | |||
| d3c24d159f | |||
| 23f9f9064e | |||
| a134307dcd | |||
| c3b510c2d9 | |||
| e7fc24e48c | |||
| 6a88dc322a | |||
| 6b464e3558 | |||
| 76b0c7b765 | |||
| 91857e8f16 | |||
| ccda456c0a | |||
| 83bad13e9e | |||
| e6a206a430 | |||
| f52a1e4ded | |||
| a8e458339b | |||
| 6b5e9aec8e | |||
| 2ab0d97573 | |||
| 0c05fc1432 | |||
| 33eeb58521 | |||
| 8fafdb185c | |||
| c2c935ba81 | |||
| 2d73c8868b | |||
| b95bb1286b | |||
| 4237c8ec30 | |||
| 49b4b39055 | |||
| bf15fea135 | |||
| 47c03e376d | |||
| 1cabfac36c | |||
| 8e32276283 | |||
| 366b306df0 | |||
| 4bf1f798f5 | |||
| b2fdb69b4d | |||
| af2736daec | |||
| 5cad7d44a6 | |||
| 2b96be0ae8 | |||
| 2a89d8021c | |||
| 13deb4706c | |||
| 9b61adb97d |
+10
@@ -257,7 +257,17 @@ paket-files/
|
||||
builds/
|
||||
bin/
|
||||
*.exe
|
||||
*.obj
|
||||
*.pdb
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
# temp files
|
||||
* .ll
|
||||
*.bc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2017 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-writable-strings -Wno-tautological-compare -Wno-macro-redefined #-Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++11
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), DARWIN)
|
||||
LDFLAGS=$(LDFLAGS) -liconv
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
|
||||
demo:
|
||||
./odin run examples/demo
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
release:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,19 @@
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
|
||||
<p align="center">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="120">
|
||||
<br/>
|
||||
A fast, concise, readable, pragmatic and open sourced programming language.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/odin-lang/odin.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/odin-lang/odin.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
@@ -8,10 +23,35 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
|
||||
* built for modern systems
|
||||
* joy of programming
|
||||
* metaprogramming
|
||||
* designed for good programmers
|
||||
|
||||
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
program := "+ + * 😃 - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
switch token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case '😃': accumulator *= accumulator;
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
|
||||
@@ -20,16 +60,22 @@ Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
|
||||
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
|
||||
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
|
||||
|
||||
## Documentation
|
||||
* [Tutorial](https://odin.handmade.network/wiki/3329-odin_tutorial)
|
||||
* [Frequently Asked Questions](https://github.com/odin-lang/Odin/wiki/Frequently-Asked-Questions-(FAQ))
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
- Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C99 support)
|
||||
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
@@ -42,30 +88,9 @@ Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
* x86-64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is calling the linker for now)
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
|
||||
## Roadmap
|
||||
|
||||
Not in any particular order and not be implemented
|
||||
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Debug Information
|
||||
- pdb format too
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
- Big numbers library
|
||||
- Multithreading for performance increase
|
||||
|
||||
@@ -5,27 +5,25 @@ set exe_name=odin.exe
|
||||
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=1
|
||||
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
rem -DDISPLAY_TIMING
|
||||
) else ( rem Release
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
|
||||
)
|
||||
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 -wd4244 ^
|
||||
-wd4306 ^
|
||||
-wd4201 -wd4204 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4505 -wd4512 -wd4550
|
||||
-wd4512
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
kernel32.lib
|
||||
rem "src\dyncall\lib\*.lib"
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
@@ -38,17 +36,13 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
odin run code/demo.odin
|
||||
rem cl %compiler_settings% "src\main.c" ^
|
||||
rem /link %linker_settings% -OUT:%exe_name% ^
|
||||
rem && odin run code/demo.odin
|
||||
rem && odin build code/metagen.odin ^
|
||||
rem && call "code\metagen.exe" "src\ast_nodes.metagen"
|
||||
rem && odin run code/Jaze/src/main.odin
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run examples/demo/demo.odin
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
release_mode=0
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm"
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g -fno-inline-functions"
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
@@ -19,6 +25,4 @@ if [[ "$(uname)" == "Darwin" ]]; then
|
||||
other_args="${other_args} -liconv"
|
||||
fi
|
||||
|
||||
${compiler} src/main.c ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
|
||||
./odin run code/demo.odin
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
//
|
||||
// Odin v0.3 Demo
|
||||
//
|
||||
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
/*
|
||||
Minor features
|
||||
--------------
|
||||
|
||||
* Lexical sugar
|
||||
- ≠, ≤, ≥
|
||||
* Label syntax change
|
||||
name: for {
|
||||
break name;
|
||||
}
|
||||
* `#no_alias` (replacing keyword `no_alias`)
|
||||
* `#ordered` reimplemented
|
||||
* "bits.odin"
|
||||
* `default:` is replaced with `case:`
|
||||
* XOR for booleans
|
||||
* Bug fixes
|
||||
* Removed Quaternion types quaternion128 & quaternion256
|
||||
* `rune` is a core type - allowing for extra type information at runtime
|
||||
* `byte` is removed - use `u8` instead (which it was an alias for)
|
||||
*/
|
||||
|
||||
// 128 bit integers
|
||||
{
|
||||
x: u128 = 1234567890123;
|
||||
y: u128 = 9876543210123;
|
||||
z := (x * y) + x + y;
|
||||
fmt.println(z);
|
||||
|
||||
a: i128 = +1234567890123;
|
||||
b: i128 = -9876543210123;
|
||||
c := (a * b) + a + b;
|
||||
fmt.println(c);
|
||||
}
|
||||
|
||||
// Divisor based modulo operator
|
||||
{
|
||||
x: i128 = -15;
|
||||
y: i128 = 2;
|
||||
|
||||
fmt.println(x % y); // Dividend based
|
||||
fmt.println(x %% y); // Divisor based
|
||||
|
||||
// a %% b == ((a % b) + b) % b;
|
||||
}
|
||||
|
||||
// Casting syntax
|
||||
{
|
||||
// Casting operations have had their syntax change for simplicity and consistency
|
||||
// Original:
|
||||
// Regular cast: `cast(type) expr`
|
||||
// Bit cast: `transmute(type) expr`
|
||||
// Union cast: `union_cast(type) expr`
|
||||
|
||||
// Regular Cast
|
||||
f: f32 = 123.321;
|
||||
i := i32(f); // type(expr)
|
||||
|
||||
|
||||
// Bit cast
|
||||
fbits := transmute(u32, f);
|
||||
|
||||
|
||||
|
||||
// Type assertion - replaces `union_cast`
|
||||
Entity :: union {
|
||||
id: u64,
|
||||
position: [vector 2]f32,
|
||||
name: string,
|
||||
|
||||
Tree{leaf_count: int},
|
||||
Frog{ribbit_volume: f32},
|
||||
}
|
||||
|
||||
e: Entity;
|
||||
e = Entity.Frog{ribbit_volume = 0.5, name = "Trevor"};
|
||||
|
||||
if frog, ok := e.(Entity.Frog); ok {
|
||||
fmt.printf("%s the frog ribbits at %f\n", frog.name, frog.ribbit_volume);
|
||||
}
|
||||
|
||||
// Panics if the type assertion fails
|
||||
frog := e.(Entity.Frog);
|
||||
|
||||
{
|
||||
// Type assertion can also be applied to `any`
|
||||
foo: any = 123;
|
||||
if i, ok := foo.(int); ok {
|
||||
fmt.println("Foo =", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Syntax changes
|
||||
{
|
||||
// Originally `^` was used to represent pointer types, pointer dereferencing, and addressing of variables
|
||||
// The addressing of variable operation is not represented with `&`
|
||||
// This is to make sure the concept of a pointer type is separate from that of a addressing
|
||||
// it is also used for familiarity coming from other C-like languages
|
||||
x: int = 123;
|
||||
y: ^int = &x;
|
||||
z: int = y^;
|
||||
|
||||
// This change also allows type casting to not require parentheses around the type for pointer evaluation
|
||||
// and consitency with other operations
|
||||
|
||||
data := rawptr(&x);
|
||||
int_ptr := ^int(data);
|
||||
|
||||
array: [10]int; // Type of the left
|
||||
x = array[0]; // Usage on the right
|
||||
|
||||
ptr: ^int = &z; // Type of the left
|
||||
x = ptr^; // Usage on the right
|
||||
|
||||
|
||||
|
||||
// Minor addition - member access through number
|
||||
TupleLike :: struct{int, f32, string}; // Fields all anonymous
|
||||
t: TupleLike;
|
||||
t.0 = 123;
|
||||
t.1 = 46.432;
|
||||
t.2 = "Foo";
|
||||
fmt.println(t);
|
||||
}
|
||||
|
||||
// Bit fields
|
||||
{
|
||||
BoxProps :: bit_field {
|
||||
opaque: 1,
|
||||
fill_colour: 3,
|
||||
_: 4,
|
||||
show_border: 1,
|
||||
_: 3,
|
||||
border_style: 2,
|
||||
_: 2,
|
||||
width: 4,
|
||||
height: 4,
|
||||
};
|
||||
|
||||
props: BoxProps;
|
||||
props.fill_colour = 4;
|
||||
props.show_border = 1;
|
||||
props.width = 12;
|
||||
props.height = 10;
|
||||
|
||||
fmt.printf("Width: %d, Height: %d\n", props.width, props.height);
|
||||
|
||||
|
||||
|
||||
Float32Data :: bit_field #align 4 {
|
||||
fraction: 23,
|
||||
exponent: 8,
|
||||
sign: 1,
|
||||
}
|
||||
|
||||
f: f32 = -123.321;
|
||||
data := transmute(Float32Data, f);
|
||||
bits := transmute(u32, f);
|
||||
fmt.printf("%#05x %#02x %v\n", data.fraction, data.exponent, bool(data.sign));
|
||||
fmt.printf("%#08x\n", bits);
|
||||
}
|
||||
|
||||
// Naming convention
|
||||
{
|
||||
// Odin has finally chose an official naming convention
|
||||
// In general, PascalCase for types and snake_case for values
|
||||
|
||||
// Import Name: snake_case (but prefer single word)
|
||||
// Types: PascalCase
|
||||
// Union Variants: PascalCase
|
||||
// Enum Values: PascalCase
|
||||
// Procedures: snake_case
|
||||
// Local Variables: snake_case
|
||||
// Field Values: snake_case
|
||||
// Constant Variables: SCREAMING_SNAKE_CASE
|
||||
}
|
||||
|
||||
// Goals for v0.4 and further
|
||||
// * Compile as C++ and use some of its constructs for sanity e.g. overloading
|
||||
// - Safe array with bounds checking
|
||||
// - Map type for self documentation
|
||||
// - u128 i128 acting like core types
|
||||
// * Context system implemented as Implicit Parameter Passing (IPP) rather than Thread Local Storage (TLS)
|
||||
// * Parameter Polymorphism
|
||||
// - Type parameter is procedures and types
|
||||
// * Decide upon a declaration syntax
|
||||
// - Current Style (name: type;) vs Prefix Style (var name: type;)
|
||||
// * Import system with a "solution" for packages/modules/libraries
|
||||
// * Better foreign interfacing with C (and maybe C++)
|
||||
// - Foreign variables
|
||||
// * Documentation Generation System for code
|
||||
// * General Documentation for Odin
|
||||
// * Attributes
|
||||
}
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import wgl "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
#import gl "opengl.odin";
|
||||
|
||||
TWO_HEARTS :: '💕';
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := cast(int)win32.GetLastError();
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
}
|
||||
}
|
||||
|
||||
// Yuk!
|
||||
to_c_string :: proc(s: string) -> []u8 {
|
||||
c_str := make([]u8, len(s)+1);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[len(s)] = 0;
|
||||
return c_str;
|
||||
}
|
||||
|
||||
|
||||
Window :: struct {
|
||||
width, height: int,
|
||||
wc: win32.WndClassExA,
|
||||
dc: win32.Hdc,
|
||||
hwnd: win32.Hwnd,
|
||||
opengl_context, rc: wgl.Hglrc,
|
||||
c_title: []u8,
|
||||
}
|
||||
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
|
||||
using win32;
|
||||
|
||||
w: Window;
|
||||
w.width, w.height = msg, height;
|
||||
|
||||
class_name := "Win32-Odin-Window\x00";
|
||||
c_class_name := ^class_name[0];
|
||||
if title[len(title)-1] != 0 {
|
||||
w.c_title = to_c_string(title);
|
||||
} else {
|
||||
w.c_title = cast([]u8)title;
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil);
|
||||
|
||||
w.wc = WndClassExA{
|
||||
size = size_of(WndClassExA),
|
||||
style = CS_VREDRAW | CS_HREDRAW,
|
||||
instance = cast(Hinstance)instance,
|
||||
class_name = c_class_name,
|
||||
wnd_proc = window_proc,
|
||||
};
|
||||
|
||||
if RegisterClassExA(^w.wc) == 0 {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
c_class_name, ^w.c_title[0],
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
cast(i32)w.width, cast(i32)w.height,
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.dc = GetDC(w.hwnd);
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
size = size_of(PIXELFORMATDESCRIPTOR),
|
||||
version = 1,
|
||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
pixel_type = PFD_TYPE_RGBA,
|
||||
color_bits = 32,
|
||||
alpha_bits = 8,
|
||||
depth_bits = 24,
|
||||
stencil_bits = 8,
|
||||
layer_type = PFD_MAIN_PLANE,
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wgl.CreateContext(w.dc);
|
||||
wgl.MakeCurrent(w.dc, w.opengl_context);
|
||||
|
||||
attribs := [8]i32{
|
||||
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||
};
|
||||
|
||||
wgl_str := "wglCreateContextAttribsARB\x00";
|
||||
wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.GetProcAddress(^wgl_str[0]);
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, nil, ^attribs[0]);
|
||||
wgl.MakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
return w, true;
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
win32.SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
|
||||
run :: proc() {
|
||||
using win32;
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, cast(Wnd_Proc)win32_proc);
|
||||
if !window_success {
|
||||
return;
|
||||
}
|
||||
defer destroy_window(^window);
|
||||
|
||||
gl.init();
|
||||
|
||||
|
||||
prev_time := time_now();
|
||||
running := true;
|
||||
|
||||
pos := Vec2{100, 100};
|
||||
|
||||
for running {
|
||||
curr_time := time_now();
|
||||
dt := cast(f32)(curr_time - prev_time);
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: Msg;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false;
|
||||
}
|
||||
TranslateMessage(^msg);
|
||||
DispatchMessageA(^msg);
|
||||
}
|
||||
|
||||
if is_key_down(Key_Code.ESCAPE) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
{
|
||||
SPEED :: 500;
|
||||
v: Vec2;
|
||||
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1; }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
|
||||
|
||||
v = norm(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
|
||||
|
||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.LoadIdentity();
|
||||
gl.Ortho(0, cast(f64)window.width,
|
||||
0, cast(f64)window.height, 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
defer gl.End();
|
||||
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0);
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0);
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
}
|
||||
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window);
|
||||
ms_to_sleep := cast(i32)(16 - 1000*dt);
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
run();
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
|
||||
#foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
||||
|
||||
|
||||
SOCKET :: #type uint;
|
||||
INVALID_SOCKET :: ~(cast(SOCKET)0);
|
||||
|
||||
AF :: enum i32 {
|
||||
UNSPEC = 0, // unspecified
|
||||
UNIX = 1, // local to host (pipes, portals)
|
||||
INET = 2, // internetwork: UDP, TCP, etc.
|
||||
IMPLINK = 3, // arpanet imp addresses
|
||||
PUP = 4, // pup protocols: e.g. BSP
|
||||
CHAOS = 5, // mit CHAOS protocols
|
||||
NS = 6, // XEROX NS protocols
|
||||
ISO = 7, // ISO protocols
|
||||
OSI = ISO, // OSI is ISO
|
||||
ECMA = 8, // european computer manufacturers
|
||||
DATAKIT = 9, // datakit protocols
|
||||
CCITT = 10, // CCITT protocols, X.25 etc
|
||||
SNA = 11, // IBM SNA
|
||||
DECnet = 12, // DECnet
|
||||
DLI = 13, // Direct data link interface
|
||||
LAT = 14, // LAT
|
||||
HYLINK = 15, // NSC Hyperchannel
|
||||
APPLETALK = 16, // AppleTalk
|
||||
ROUTE = 17, // Internal Routing Protocol
|
||||
LINK = 18, // Link layer interface
|
||||
XTP = 19, // eXpress Transfer Protocol (no AF)
|
||||
COIP = 20, // connection-oriented IP, aka ST II
|
||||
CNT = 21, // Computer Network Technology
|
||||
RTIP = 22, // Help Identify RTIP packets
|
||||
IPX = 23, // Novell Internet Protocol
|
||||
SIP = 24, // Simple Internet Protocol
|
||||
PIP = 25, // Help Identify PIP packets
|
||||
MAX = 26,
|
||||
};
|
||||
|
||||
SOCK_STREAM :: 1;
|
||||
SOCKET_ERROR :: -1;
|
||||
IPPROTO_TCP :: 6;
|
||||
AI_PASSIVE :: 0x0020;
|
||||
SOMAXCONN :: 128;
|
||||
|
||||
SD_RECEIVE :: 0;
|
||||
SD_SEND :: 1;
|
||||
SD_BOTH :: 2;
|
||||
|
||||
WSADESCRIPTION_LEN :: 256;
|
||||
WSASYS_STATUS_LEN :: 128;
|
||||
WSADATA :: struct #ordered {
|
||||
version: i16,
|
||||
high_version: i16,
|
||||
|
||||
|
||||
// NOTE(bill): This is x64 ordering
|
||||
max_sockets: u16,
|
||||
max_udp_dg: u16,
|
||||
vendor_info: ^byte,
|
||||
description: [WSADESCRIPTION_LEN+1]byte,
|
||||
system_status: [WSASYS_STATUS_LEN+1]byte,
|
||||
}
|
||||
|
||||
addrinfo :: struct #ordered {
|
||||
flags: i32,
|
||||
family: i32,
|
||||
socktype: i32,
|
||||
protocol: i32,
|
||||
addrlen: uint,
|
||||
canonname: ^u8,
|
||||
addr: ^sockaddr,
|
||||
next: ^addrinfo,
|
||||
}
|
||||
|
||||
sockaddr :: struct #ordered {
|
||||
family: u16,
|
||||
data: [14]byte,
|
||||
}
|
||||
|
||||
|
||||
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign ws2;
|
||||
WSACleanup :: proc() -> i32 #foreign ws2;
|
||||
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign ws2;
|
||||
freeaddrinfo :: proc(ai: ^addrinfo) #foreign ws2;
|
||||
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign ws2;
|
||||
closesocket :: proc(s: SOCKET) -> i32 #foreign ws2;
|
||||
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign ws2;
|
||||
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign ws2;
|
||||
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign ws2;
|
||||
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign ws2;
|
||||
WSAGetLastError :: proc() -> i32 #foreign ws2;
|
||||
|
||||
to_c_string :: proc(s: string) -> ^byte {
|
||||
c_str := new_slice(byte, s.count+1);
|
||||
assert(c_str.data != nil);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[s.count] = 0;
|
||||
return c_str.data;
|
||||
}
|
||||
|
||||
run :: proc() {
|
||||
wsa: WSADATA;
|
||||
res: ^addrinfo = nil;
|
||||
hints: addrinfo;
|
||||
s, client: SOCKET;
|
||||
|
||||
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
|
||||
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer WSACleanup();
|
||||
|
||||
hints.family = cast(i32)AF.INET;
|
||||
hints.socktype = SOCK_STREAM;
|
||||
hints.protocol = IPPROTO_TCP;
|
||||
hints.flags = AI_PASSIVE;
|
||||
|
||||
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer freeaddrinfo(res);
|
||||
|
||||
s = socket(res.family, res.socktype, res.protocol);
|
||||
if s == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(s);
|
||||
|
||||
bind(s, res.addr, cast(i32)res.addrlen);
|
||||
listen(s, SOMAXCONN);
|
||||
|
||||
client = accept(s, nil, 0);
|
||||
if client == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(client);
|
||||
|
||||
html :=
|
||||
`HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Content-type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
buf: [1024]byte;
|
||||
for {
|
||||
bytes := recv(client, ^buf[0], cast(i32)buf.count, 0);
|
||||
if bytes > 0 {
|
||||
// fmt.println(buf[:bytes] as string)
|
||||
bytes_sent := send(client, html.data, cast(i32)(html.count-1), 0);
|
||||
if bytes_sent == SOCKET_ERROR {
|
||||
fmt.println("send failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} else if bytes == 0 {
|
||||
fmt.println("Connection closing...");
|
||||
break;
|
||||
} else {
|
||||
fmt.println("recv failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(client, SD_SEND);
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
|
||||
CANVAS_WIDTH :: 128;
|
||||
CANVAS_HEIGHT :: 128;
|
||||
CANVAS_SCALE :: 3;
|
||||
FRAME_TIME :: 1.0/30.0;
|
||||
WINDOW_TITLE :: "Punity\x00";
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||
|
||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
|
||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
|
||||
|
||||
|
||||
STACK_CAPACITY :: 1<<20;
|
||||
STORAGE_CAPACITY :: 1<<20;
|
||||
|
||||
DRAW_LIST_RESERVE :: 128;
|
||||
|
||||
MAX_KEYS :: 256;
|
||||
|
||||
Core :: struct {
|
||||
stack: ^Bank,
|
||||
storage: ^Bank,
|
||||
|
||||
running: bool,
|
||||
key_modifiers: u32,
|
||||
key_states: [MAX_KEYS]byte,
|
||||
key_deltas: [MAX_KEYS]byte,
|
||||
|
||||
perf_frame,
|
||||
perf_frame_inner,
|
||||
perf_step,
|
||||
perf_audio,
|
||||
perf_blit,
|
||||
perf_blit_cvt,
|
||||
perf_blit_gdi: Perf_Span,
|
||||
|
||||
frame: i64,
|
||||
|
||||
canvas: Canvas,
|
||||
draw_list: ^Draw_List,
|
||||
}
|
||||
|
||||
Perf_Span :: struct {
|
||||
stamp: f64,
|
||||
delta: f32,
|
||||
}
|
||||
|
||||
Bank :: struct {
|
||||
memory: []byte,
|
||||
cursor: int,
|
||||
}
|
||||
|
||||
Bank_State :: struct {
|
||||
state: Bank,
|
||||
bank: ^Bank,
|
||||
}
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{a, b, g, r: byte},
|
||||
rgba: u32,
|
||||
}
|
||||
|
||||
Palette :: struct {
|
||||
colors: [256]Color,
|
||||
colors_count: byte,
|
||||
}
|
||||
|
||||
|
||||
Rect :: raw_union {
|
||||
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
||||
using pos: struct {left, top, right, bottom: int},
|
||||
e: [4]int,
|
||||
}
|
||||
|
||||
Bitmap :: struct {
|
||||
pixels: []byte,
|
||||
width: int,
|
||||
height: int,
|
||||
}
|
||||
|
||||
Font :: struct {
|
||||
using bitmap: Bitmap,
|
||||
char_width: int,
|
||||
char_height: int,
|
||||
}
|
||||
|
||||
Canvas :: struct {
|
||||
using bitmap: ^Bitmap,
|
||||
palette: Palette,
|
||||
translate_x: int,
|
||||
translate_y: int,
|
||||
clip: Rect,
|
||||
font: ^Font,
|
||||
}
|
||||
|
||||
DrawFlag :: enum {
|
||||
NONE = 0,
|
||||
FLIP_H = 1<<0,
|
||||
FLIP_V = 1<<1,
|
||||
MASK = 1<<2,
|
||||
}
|
||||
|
||||
Draw_Item :: struct {}
|
||||
Draw_List :: struct {
|
||||
items: []Draw_Item,
|
||||
}
|
||||
|
||||
Key :: enum {
|
||||
MOD_SHIFT = 0x0001,
|
||||
MOD_CONTROL = 0x0002,
|
||||
MOD_ALT = 0x0004,
|
||||
MOD_SUPER = 0x0008,
|
||||
|
||||
UNKNOWN =-1,
|
||||
INVALID =-2,
|
||||
|
||||
LBUTTON = 1,
|
||||
RBUTTON = 2,
|
||||
CANCEL = 3,
|
||||
MBUTTON = 4,
|
||||
|
||||
BACK = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
SHIFT = 16,
|
||||
CONTROL = 17,
|
||||
MENU = 18,
|
||||
PAUSE = 19,
|
||||
CAPITAL = 20,
|
||||
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 = 32,
|
||||
PRIOR = 33,
|
||||
NEXT = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT = 37,
|
||||
UP = 38,
|
||||
RIGHT = 39,
|
||||
DOWN = 40,
|
||||
SELECT = 41,
|
||||
PRINT = 42,
|
||||
EXEC = 43,
|
||||
SNAPSHOT = 44,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
HELP = 47,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
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,
|
||||
|
||||
APOSTROPHE = 39, /* ' */
|
||||
COMMA = 44, /* , */
|
||||
MINUS = 45, /* - */
|
||||
PERIOD = 46, /* . */
|
||||
SLASH = 47, /* / */
|
||||
NUM0 = 48,
|
||||
NUM1 = 49,
|
||||
NUM2 = 50,
|
||||
NUM3 = 51,
|
||||
NUM4 = 52,
|
||||
NUM5 = 53,
|
||||
NUM6 = 54,
|
||||
NUM7 = 55,
|
||||
NUM8 = 56,
|
||||
NUM9 = 57,
|
||||
SEMICOLON = 59, /* ; */
|
||||
EQUAL = 61, /* = */
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LEFT_BRACKET = 91, /* [ */
|
||||
BACKSLASH = 92, /* \ */
|
||||
RIGHT_BRACKET = 93, /* ] */
|
||||
GRAVE_ACCENT = 96, /* ` */
|
||||
};
|
||||
|
||||
|
||||
key_down :: proc(k: Key) -> bool {
|
||||
return _core.key_states[k] != 0;
|
||||
}
|
||||
|
||||
key_pressed :: proc(k: Key) -> bool {
|
||||
return (_core.key_deltas[k] != 0) && key_down(k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
|
||||
_core: Core;
|
||||
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32;
|
||||
|
||||
_core.running = true;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
|
||||
win32_app_key_mods :: proc() -> u32 {
|
||||
mods: u32 = 0;
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= cast(u32)Key.MOD_SHIFT;
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= cast(u32)Key.MOD_CONTROL;
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= cast(u32)Key.MOD_ALT;
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= cast(u32)Key.MOD_SUPER;
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
match msg {
|
||||
case WM_KEYDOWN:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 1;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_KEYUP:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 0;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
_core.running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
window_class := WNDCLASSEXA{
|
||||
class_name = (cast(string)"Punity\x00").data, // C-style string
|
||||
size = size_of(WNDCLASSEXA),
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = cast(HINSTANCE)GetModuleHandleA(nil),
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
|
||||
};
|
||||
|
||||
if RegisterClassExA(^window_class) == 0 {
|
||||
fmt.fprintln(os.stderr, "RegisterClassExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
screen_width := GetSystemMetrics(SM_CXSCREEN);
|
||||
screen_height := GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
rc: RECT;
|
||||
rc.left = (screen_width - WINDOW_WIDTH) / 2;
|
||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
|
||||
rc.right = rc.left + WINDOW_WIDTH;
|
||||
rc.bottom = rc.top + WINDOW_HEIGHT;
|
||||
|
||||
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
assert(AdjustWindowRect(^rc, style, 0) != 0);
|
||||
|
||||
wt := WINDOW_TITLE;
|
||||
|
||||
win32_window := CreateWindowExA(0,
|
||||
window_class.class_name,
|
||||
wt.data,
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
nil, nil, window_class.instance,
|
||||
nil);
|
||||
|
||||
if win32_window == nil {
|
||||
fmt.fprintln(os.stderr, "CreateWindowExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi.size = size_of(BITMAPINFOHEADER);
|
||||
window_bmi.width = CANVAS_WIDTH;
|
||||
window_bmi.height = CANVAS_HEIGHT;
|
||||
window_bmi.planes = 1;
|
||||
window_bmi.bit_count = 32;
|
||||
window_bmi.compression = BI_RGB;
|
||||
|
||||
|
||||
user_init(^_core);
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW);
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
defer free(window_buffer);
|
||||
|
||||
|
||||
for i := 0; i < window_buffer.count; i += 1 {
|
||||
window_buffer[i] = 0xff00ff;
|
||||
}
|
||||
|
||||
|
||||
dt: f64;
|
||||
prev_time := time_now();
|
||||
curr_time := time_now();
|
||||
total_time : f64 = 0;
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
|
||||
message: MSG;
|
||||
for _core.running {
|
||||
curr_time = time_now();
|
||||
dt = curr_time - prev_time;
|
||||
prev_time = curr_time;
|
||||
total_time += dt;
|
||||
|
||||
offset_x += 1;
|
||||
offset_y += 2;
|
||||
|
||||
{
|
||||
data: [128]byte;
|
||||
buf: fmt.Buffer;
|
||||
buf.data = data[:];
|
||||
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
|
||||
win32.SetWindowTextA(win32_window, ^buf[0]);
|
||||
}
|
||||
|
||||
|
||||
for y := 0; y < CANVAS_HEIGHT; y += 1 {
|
||||
for x := 0; x < CANVAS_WIDTH; x += 1 {
|
||||
g := (x % 32) * 8;
|
||||
b := (y % 32) * 8;
|
||||
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
|
||||
}
|
||||
}
|
||||
|
||||
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
|
||||
|
||||
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false;
|
||||
}
|
||||
TranslateMessage(^message);
|
||||
DispatchMessageA(^message);
|
||||
}
|
||||
|
||||
user_step(^_core);
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
window_buffer.data,
|
||||
^window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
ReleaseDC(win32_window, dc);
|
||||
|
||||
|
||||
{
|
||||
delta := time_now() - prev_time;
|
||||
ms := cast(i32)((FRAME_TIME - delta) * 1000);
|
||||
if ms > 0 {
|
||||
win32.Sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
_core.frame += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
user_init :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
user_step :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
run(user_init, user_step);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#import "fmt.odin" as fmt
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Sub Hello!")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*#import "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Hello1!")
|
||||
}*/
|
||||
|
||||
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("hello, world!");
|
||||
}
|
||||
|
||||
|
||||
/*#import "fmt.odin" as .
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello3!")
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*#import "fmt.odin" as _
|
||||
|
||||
thing :: proc() {
|
||||
// println("Hello4!")
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello5!")
|
||||
}*/
|
||||
@@ -1,757 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
#import "os.odin";
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
#import "raw.odin";
|
||||
|
||||
// Naming Conventions:
|
||||
// In general, PascalCase for types and snake_case for values
|
||||
//
|
||||
// Import Name: snake_case (but prefer single word)
|
||||
// Types: PascalCase
|
||||
// Union Variants: PascalCase
|
||||
// Enum Values: PascalCase
|
||||
// Procedures: snake_case
|
||||
// Local Variables: snake_case
|
||||
// Constant Variables: SCREAMING_SNAKE_CASE
|
||||
|
||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
// This could change at a later date if the all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Do not change the order of any of this data
|
||||
// The compiler relies upon this _exact_ order
|
||||
TypeInfoEnumValue :: raw_union {
|
||||
f: f64,
|
||||
i: i128,
|
||||
}
|
||||
// NOTE(bill): This must match the compiler's
|
||||
CallingConvention :: enum {
|
||||
Odin = 0,
|
||||
C = 1,
|
||||
Std = 2,
|
||||
Fast = 3,
|
||||
}
|
||||
|
||||
TypeInfoRecord :: struct #ordered {
|
||||
types: []^TypeInfo,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
usings: []bool, // usings may not be used in tuples
|
||||
packed: bool,
|
||||
ordered: bool,
|
||||
custom_align: bool,
|
||||
}
|
||||
|
||||
TypeInfo :: union {
|
||||
size: int,
|
||||
align: int,
|
||||
|
||||
Named{name: string, base: ^TypeInfo},
|
||||
Integer{signed: bool},
|
||||
Rune{},
|
||||
Float{},
|
||||
Complex{},
|
||||
String{},
|
||||
Boolean{},
|
||||
Any{},
|
||||
Pointer{
|
||||
elem: ^TypeInfo, // nil -> rawptr
|
||||
},
|
||||
Atomic{elem: ^TypeInfo},
|
||||
Procedure{
|
||||
params: ^TypeInfo, // TypeInfo.Tuple
|
||||
results: ^TypeInfo, // TypeInfo.Tuple
|
||||
variadic: bool,
|
||||
convention: CallingConvention,
|
||||
},
|
||||
Array{
|
||||
elem: ^TypeInfo,
|
||||
elem_size: int,
|
||||
count: int,
|
||||
},
|
||||
DynamicArray{elem: ^TypeInfo, elem_size: int},
|
||||
Slice {elem: ^TypeInfo, elem_size: int},
|
||||
Vector {elem: ^TypeInfo, elem_size, count: int},
|
||||
Tuple {using record: TypeInfoRecord}, // Only really used for procedures
|
||||
Struct {using record: TypeInfoRecord},
|
||||
RawUnion {using record: TypeInfoRecord},
|
||||
Union{
|
||||
common_fields: struct {
|
||||
types: []^TypeInfo,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
},
|
||||
variant_names: []string,
|
||||
variant_types: []^TypeInfo,
|
||||
},
|
||||
Enum{
|
||||
base: ^TypeInfo,
|
||||
names: []string,
|
||||
values: []TypeInfoEnumValue,
|
||||
},
|
||||
Map{
|
||||
key: ^TypeInfo,
|
||||
value: ^TypeInfo,
|
||||
generated_struct: ^TypeInfo,
|
||||
count: int, // == 0 if dynamic
|
||||
},
|
||||
BitField{
|
||||
names: []string,
|
||||
bits: []i32,
|
||||
offsets: []i32,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): only the ones that are needed (not all types)
|
||||
// This will be set by the compiler
|
||||
__type_table: []TypeInfo;
|
||||
|
||||
__argv__: ^^u8;
|
||||
__argc__: i32;
|
||||
|
||||
type_info_base :: proc(info: ^TypeInfo) -> ^TypeInfo {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case TypeInfo.Named:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
type_info_base_without_enum :: proc(info: ^TypeInfo) -> ^TypeInfo {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case TypeInfo.Named:
|
||||
base = i.base;
|
||||
case TypeInfo.Enum:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
|
||||
assume :: proc(cond: bool) #foreign __llvm_core "llvm.assume";
|
||||
|
||||
__debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
|
||||
__trap :: proc() #foreign __llvm_core "llvm.trap";
|
||||
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
AllocatorMode :: enum u8 {
|
||||
Alloc,
|
||||
Free,
|
||||
FreeAll,
|
||||
Resize,
|
||||
}
|
||||
AllocatorProc :: #type proc(allocator_data: rawptr, mode: AllocatorMode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
||||
Allocator :: struct #ordered {
|
||||
procedure: AllocatorProc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
Context :: struct #ordered {
|
||||
thread_id: int,
|
||||
|
||||
allocator: Allocator,
|
||||
|
||||
user_data: rawptr,
|
||||
user_index: int,
|
||||
}
|
||||
|
||||
#thread_local __context: Context;
|
||||
|
||||
|
||||
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
||||
|
||||
|
||||
__check_context :: proc() {
|
||||
c := &__context;
|
||||
|
||||
if c.allocator.procedure == nil {
|
||||
c.allocator = default_allocator();
|
||||
}
|
||||
if c.thread_id == 0 {
|
||||
c.thread_id = os.current_thread_id();
|
||||
}
|
||||
}
|
||||
|
||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT); }
|
||||
|
||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, AllocatorMode.Alloc, size, alignment, nil, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
if a.procedure == nil {
|
||||
return;
|
||||
}
|
||||
a.procedure(a.data, AllocatorMode.Free, 0, 0, ptr, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr :: proc(ptr: rawptr) #inline {
|
||||
__check_context();
|
||||
free_ptr_with_allocator(context.allocator, ptr);
|
||||
}
|
||||
|
||||
free_all :: proc() #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
a.procedure(a.data, AllocatorMode.FreeAll, 0, 0, nil, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT); }
|
||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, AllocatorMode.Resize, new_size, alignment, ptr, old_size, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
|
||||
if old_memory == nil {
|
||||
return alloc_align(new_size, alignment);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc_align(new_size, alignment);
|
||||
if new_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__mem_copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: AllocatorMode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using AllocatorMode;
|
||||
|
||||
match mode {
|
||||
case Alloc:
|
||||
return os.heap_alloc(size);
|
||||
|
||||
case Free:
|
||||
os.heap_free(old_memory);
|
||||
return nil;
|
||||
|
||||
case FreeAll:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case Resize:
|
||||
ptr := os.heap_resize(old_memory, size);
|
||||
assert(ptr != nil);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__string_eq :: proc(a, b: string) -> bool {
|
||||
if len(a) != len(b) {
|
||||
return false;
|
||||
}
|
||||
if len(a) == 0 {
|
||||
return true;
|
||||
}
|
||||
if &a[0] == &b[0] {
|
||||
return true;
|
||||
}
|
||||
return __string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b: string) -> int {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
|
||||
__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; }
|
||||
__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; }
|
||||
__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; }
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
|
||||
|
||||
|
||||
__complex64_eq :: proc(a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex64_ne :: proc(a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
__assert :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__panic :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__bounds_check_error :: proc(file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
|
||||
file, line, column, index, count);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__slice_expr_error :: proc(file: string, line, column: int, low, high, max: int) {
|
||||
if 0 <= low && low <= high && high <= max {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..<%d..<%d]\n",
|
||||
file, line, column, low, high, max);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..<%d]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__type_assertion_check :: proc(ok: bool, file: string, line, column: int, from, to: ^TypeInfo) {
|
||||
if !ok {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
|
||||
file, line, column, from, to);
|
||||
__debug_trap();
|
||||
}
|
||||
}
|
||||
|
||||
__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||
return utf8.decode_rune(s);
|
||||
}
|
||||
|
||||
|
||||
__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
llvm_memset_64bit :: proc(dst: rawptr, val: u8, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
|
||||
llvm_memset_64bit(data, u8(value), len, 1, false);
|
||||
return data;
|
||||
}
|
||||
__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_set(data, 0, len);
|
||||
}
|
||||
__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
__mem_compare :: proc(a, b: ^u8, n: int) -> int {
|
||||
for i in 0..<n {
|
||||
match {
|
||||
case (a+i)^ < (b+i)^:
|
||||
return -1;
|
||||
case (a+i)^ > (b+i)^:
|
||||
return +1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
__sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
__abs_complex64 :: proc(x: complex64) -> f32 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f32(r*r + i*i);
|
||||
}
|
||||
__abs_complex128 :: proc(x: complex128) -> f64 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f64(r*r + i*i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
|
||||
array := ^raw.DynamicArray(array_);
|
||||
__check_context();
|
||||
array.allocator = context.allocator;
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
if cap > 0 {
|
||||
__dynamic_array_reserve(array_, elem_size, elem_align, cap);
|
||||
array.len = len;
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int) -> bool {
|
||||
array := ^raw.DynamicArray(array_);
|
||||
|
||||
if cap <= array.cap {
|
||||
return true;
|
||||
}
|
||||
|
||||
__check_context();
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator;
|
||||
}
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
old_size := array.cap * elem_size;
|
||||
new_size := cap * elem_size;
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, AllocatorMode.Resize, new_size, elem_align, array.data, old_size, 0);
|
||||
if new_data == nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
array.data = new_data;
|
||||
array.cap = cap;
|
||||
return true;
|
||||
}
|
||||
|
||||
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int) -> bool {
|
||||
array := ^raw.DynamicArray(array_);
|
||||
|
||||
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len);
|
||||
if ok {
|
||||
array.len = len;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
array := ^raw.DynamicArray(array_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return array.len;
|
||||
}
|
||||
|
||||
|
||||
ok := true;
|
||||
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);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.len;
|
||||
}
|
||||
data := ^u8(array.data);
|
||||
assert(data != nil);
|
||||
__mem_copy(data + (elem_size*array.len), items, elem_size * item_count);
|
||||
array.len += item_count;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
|
||||
array := ^raw.DynamicArray(array_);
|
||||
|
||||
ok := true;
|
||||
if array.cap <= array.len+1 {
|
||||
cap := 2 * array.cap + max(8, 1);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.len;
|
||||
}
|
||||
data := ^u8(array.data);
|
||||
assert(data != nil);
|
||||
__mem_zero(data + (elem_size*array.len), elem_size);
|
||||
array.len++;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
slice := ^raw.Slice(slice_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
item_count = min(slice.cap-slice.len, item_count);
|
||||
if item_count > 0 {
|
||||
data := ^u8(slice.data);
|
||||
assert(data != nil);
|
||||
__mem_copy(data + (elem_size*slice.len), items, elem_size * item_count);
|
||||
slice.len += item_count;
|
||||
}
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
|
||||
// Map stuff
|
||||
|
||||
__default_hash :: proc(data: []u8) -> u128 {
|
||||
fnv128a :: proc(data: []u8) -> u128 {
|
||||
h: u128 = 0x6c62272e07bb014262b821756295c58d;
|
||||
for b in data {
|
||||
h = (h ~ u128(b)) * 0x1000000000000000000013b;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
return fnv128a(data);
|
||||
}
|
||||
__default_hash_string :: proc(s: string) -> u128 {
|
||||
return __default_hash([]u8(s));
|
||||
}
|
||||
|
||||
__INITIAL_MAP_CAP :: 16;
|
||||
|
||||
__MapKey :: struct #ordered {
|
||||
hash: u128,
|
||||
str: string,
|
||||
}
|
||||
|
||||
__MapFindResult :: struct #ordered {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
__MapEntryHeader :: struct #ordered {
|
||||
key: __MapKey,
|
||||
next: int,
|
||||
/*
|
||||
value: Value_Type,
|
||||
*/
|
||||
}
|
||||
|
||||
__MapHeader :: struct #ordered {
|
||||
m: ^raw.DynamicMap,
|
||||
is_key_string: bool,
|
||||
entry_size: int,
|
||||
entry_align: int,
|
||||
value_offset: int,
|
||||
value_size: int,
|
||||
}
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: __MapHeader, cap: int) {
|
||||
__dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap);
|
||||
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap);
|
||||
}
|
||||
|
||||
__dynamic_map_rehash :: proc(using header: __MapHeader, new_count: int) {
|
||||
new_header: __MapHeader = header;
|
||||
nm: raw.DynamicMap;
|
||||
new_header.m = &nm;
|
||||
|
||||
header_hashes := ^raw.DynamicArray(&header.m.hashes);
|
||||
nm_hashes := ^raw.DynamicArray(&nm.hashes);
|
||||
|
||||
__dynamic_array_resize(nm_hashes, size_of(int), align_of(int), new_count);
|
||||
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len);
|
||||
for i in 0..<new_count {
|
||||
nm.hashes[i] = -1;
|
||||
}
|
||||
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
if len(nm.hashes) == 0 {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
|
||||
entry_header := __dynamic_map_get_entry(header, i);
|
||||
data := ^u8(entry_header);
|
||||
|
||||
fr := __dynamic_map_find(new_header, entry_header.key);
|
||||
j := __dynamic_map_add_entry(new_header, entry_header.key);
|
||||
if fr.entry_prev < 0 {
|
||||
nm.hashes[fr.hash_index] = j;
|
||||
} else {
|
||||
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
|
||||
e.next = j;
|
||||
}
|
||||
|
||||
e := __dynamic_map_get_entry(new_header, j);
|
||||
e.next = fr.entry_index;
|
||||
ndata := ^u8(e);
|
||||
__mem_copy(ndata+value_offset, data+value_offset, value_size);
|
||||
|
||||
if __dynamic_map_full(new_header) {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
}
|
||||
free_ptr_with_allocator(header_hashes.allocator, header_hashes.data);
|
||||
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
|
||||
header.m^ = nm;
|
||||
}
|
||||
|
||||
__dynamic_map_get :: proc(h: __MapHeader, key: __MapKey) -> rawptr {
|
||||
index := __dynamic_map_find(h, key).entry_index;
|
||||
if index >= 0 {
|
||||
data := ^u8(__dynamic_map_get_entry(h, index));
|
||||
val := data + h.value_offset;
|
||||
return val;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
__dynamic_map_set :: proc(using h: __MapHeader, key: __MapKey, value: rawptr) {
|
||||
index: int;
|
||||
assert(value != nil);
|
||||
|
||||
|
||||
if len(m.hashes) == 0 {
|
||||
__dynamic_map_reserve(h, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = __dynamic_map_add_entry(h, key);
|
||||
if fr.entry_prev >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_prev);
|
||||
entry.next = index;
|
||||
} else {
|
||||
m.hashes[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
{
|
||||
e := __dynamic_map_get_entry(h, index);
|
||||
e.key = key;
|
||||
val := ^u8(e) + value_offset;
|
||||
__mem_copy(val, value, value_size);
|
||||
}
|
||||
|
||||
if __dynamic_map_full(h) {
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_grow :: proc(using h: __MapHeader) {
|
||||
new_count := max(2*m.entries.cap + 8, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_rehash(h, new_count);
|
||||
}
|
||||
|
||||
__dynamic_map_full :: proc(using h: __MapHeader) -> bool {
|
||||
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_hash_equal :: proc(h: __MapHeader, a, b: __MapKey) -> bool {
|
||||
if a.hash == b.hash {
|
||||
if h.is_key_string {
|
||||
return a.str == b.str;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__dynamic_map_find :: proc(using h: __MapHeader, key: __MapKey) -> __MapFindResult {
|
||||
fr := __MapFindResult{-1, -1, -1};
|
||||
if len(m.hashes) > 0 {
|
||||
fr.hash_index = int(key.hash % u128(len(m.hashes)));
|
||||
fr.entry_index = m.hashes[fr.hash_index];
|
||||
for fr.entry_index >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
if __dynamic_map_hash_equal(h, entry.key, key) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry.next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
__dynamic_map_add_entry :: proc(using h: __MapHeader, key: __MapKey) -> int {
|
||||
prev := m.entries.len;
|
||||
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align);
|
||||
if c != prev {
|
||||
end := __dynamic_map_get_entry(h, c-1);
|
||||
end.key = key;
|
||||
end.next = -1;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_delete :: proc(using h: __MapHeader, key: __MapKey) {
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
__dynamic_map_erase(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_map_get_entry :: proc(using h: __MapHeader, index: int) -> ^__MapEntryHeader {
|
||||
data := ^u8(m.entries.data) + index*entry_size;
|
||||
return ^__MapEntryHeader(data);
|
||||
}
|
||||
|
||||
__dynamic_map_erase :: proc(using h: __MapHeader, fr: __MapFindResult) {
|
||||
if fr.entry_prev < 0 {
|
||||
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
} else {
|
||||
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == m.entries.len-1 {
|
||||
m.entries.len--;
|
||||
}
|
||||
__mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.len-1), entry_size);
|
||||
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
|
||||
if last.entry_prev >= 0 {
|
||||
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
m.hashes[last.hash_index] = fr.entry_index;
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
__u128_mod :: proc(a, b: u128) -> u128 #cc_odin #link_name "__umodti3" {
|
||||
r: u128;
|
||||
__u128_quo_mod(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__u128_quo :: proc(a, b: u128) -> u128 #cc_odin #link_name "__udivti3" {
|
||||
return __u128_quo_mod(a, b, nil);
|
||||
}
|
||||
|
||||
__i128_mod :: proc(a, b: i128) -> i128 #cc_odin #link_name "__modti3" {
|
||||
r: i128;
|
||||
__i128_quo_mod(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__i128_quo :: proc(a, b: i128) -> i128 #cc_odin #link_name "__divti3" {
|
||||
return __i128_quo_mod(a, b, nil);
|
||||
}
|
||||
|
||||
__i128_quo_mod :: proc(a, b: i128, rem: ^i128) -> (quo: i128) #cc_odin #link_name "__divmodti4" {
|
||||
s: i128;
|
||||
s = b >> 127;
|
||||
b = (b~s) - s;
|
||||
s = a >> 127;
|
||||
b = (a~s) - s;
|
||||
|
||||
uquo: u128;
|
||||
urem := __u128_quo_mod(transmute(u128, a), transmute(u128, b), &uquo);
|
||||
iquo := transmute(i128, uquo);
|
||||
irem := transmute(i128, urem);
|
||||
|
||||
iquo = (iquo~s) - s;
|
||||
irem = (irem~s) - s;
|
||||
if rem != nil { rem^ = irem; }
|
||||
return iquo;
|
||||
}
|
||||
|
||||
|
||||
__u128_quo_mod :: proc(a, b: u128, rem: ^u128) -> (quo: u128) #cc_odin #link_name "__udivmodti4" {
|
||||
alo, ahi := u64(a), u64(a>>64);
|
||||
blo, bhi := u64(b), u64(b>>64);
|
||||
if b == 0 {
|
||||
if rem != nil { rem^ = 0; }
|
||||
return u128(alo/blo);
|
||||
}
|
||||
|
||||
r, d, x, q: u128 = a, b, 1, 0;
|
||||
|
||||
for r >= d && (d>>127)&1 == 0 {
|
||||
x <<= 1;
|
||||
d <<= 1;
|
||||
}
|
||||
|
||||
for x != 0 {
|
||||
if r >= d {
|
||||
r -= d;
|
||||
q |= x;
|
||||
}
|
||||
x >>= 1;
|
||||
d >>= 1;
|
||||
}
|
||||
|
||||
if rem != nil { rem^ = r; }
|
||||
return q;
|
||||
}
|
||||
|
||||
/*
|
||||
__f16_to_f32 :: proc(f: f16) -> f32 #cc_odin #no_inline #link_name "__gnu_h2f_ieee" {
|
||||
when true {
|
||||
// Source: https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
||||
FP32 :: raw_union {u: u32, f: f32};
|
||||
|
||||
magic, was_infnan: FP32;
|
||||
magic.u = (254-15) << 23;
|
||||
was_infnan.u = (127+16) << 23;
|
||||
|
||||
hu := transmute(u16, f);
|
||||
|
||||
o := FP32{};
|
||||
|
||||
o.u = u32((hu & 0x7fff) << 13);
|
||||
o.f *= magic.f;
|
||||
if o.f >= was_infnan.f {
|
||||
o.u |= 255 << 23;
|
||||
}
|
||||
o.u |= u32(hu & 0x8000) << 16;
|
||||
return o.f;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
__f32_to_f16 :: proc(f_: f32) -> f16 #cc_odin #no_inline #link_name "__gnu_f2h_ieee" {
|
||||
when false {
|
||||
// Source: https://gist.github.com/rygorous/2156668
|
||||
FP16 :: raw_union {u: u16, f: f16};
|
||||
FP32 :: raw_union {u: u32, f: f32};
|
||||
|
||||
f32infty, f16infty, magic: FP32;
|
||||
f32infty.u = 255<<23;
|
||||
f16infty.u = 31<<23;
|
||||
magic.u = 15<<23;
|
||||
|
||||
sign_mask :: u32(0x80000000);
|
||||
round_mask :: ~u32(0x0fff);
|
||||
|
||||
f := transmute(FP32, f_);
|
||||
|
||||
o: FP16;
|
||||
sign := f.u & sign_mask;
|
||||
f.u ~= sign;
|
||||
|
||||
// NOTE all the integer compares in this function can be safely
|
||||
// compiled into signed compares since all operands are below
|
||||
// 0x80000000. Important if you want fast straight SSE2 code
|
||||
// (since there's no unsigned PCMPGTD).
|
||||
|
||||
if f.u >= f32infty.u { // Inf or NaN (all exponent bits set)
|
||||
o.u = f.u > f32infty.u ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
|
||||
} else { // (De)normalized number or zero
|
||||
f.u &= round_mask;
|
||||
f.f *= magic.f;
|
||||
f.u -= round_mask;
|
||||
if f.u > f16infty.u {
|
||||
f.u = f16infty.u; // Clamp to signed infinity if overflowed
|
||||
}
|
||||
|
||||
o.u = u16(f.u >> 13); // Take the bits!
|
||||
}
|
||||
|
||||
o.u |= u16(sign >> 16);
|
||||
return o.f;
|
||||
} else {
|
||||
f := transmute(u32, f_);
|
||||
h: u16;
|
||||
hs, he, hf: u16;
|
||||
|
||||
fs := (f >> 31) & 1;
|
||||
fe := (f >> 23) & 0b1111_1111;
|
||||
ff := (f >> 0) & 0b0111_1111_1111_1111_1111_1111;
|
||||
|
||||
add_one := false;
|
||||
|
||||
if (fe == 0) {
|
||||
he = 0;
|
||||
} else if (fe == 255) {
|
||||
he = 31;
|
||||
hf = ff != 0 ? 0x200 : 0;
|
||||
} else {
|
||||
ne := fe - 127 + 15;
|
||||
if ne >= 31 {
|
||||
he = 31;
|
||||
} else if ne <= 0 {
|
||||
if (14-ne) <= 24 {
|
||||
mant := ff | 0x800000;
|
||||
hf = u16(mant >> (14-ne));
|
||||
|
||||
if (mant >> (13-ne)) & 1 != 0 {
|
||||
add_one = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
he = u16(ne);
|
||||
hf = u16(ff >> 13);
|
||||
if ff&0x1000 != 0 {
|
||||
add_one = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hs = u16(hs);
|
||||
h |= (he&0b0001_1111)<<10;
|
||||
h |= (hf&0b0011_1111_1111);
|
||||
if add_one {
|
||||
h++;
|
||||
}
|
||||
h |= (hs&1) << 15;
|
||||
return transmute(f16, h);
|
||||
}
|
||||
}
|
||||
|
||||
__f64_to_f16 :: proc(f: f64) -> f16 #cc_odin #no_inline #link_name "__truncdfhf2" {
|
||||
return __f32_to_f16(f32(f));
|
||||
}
|
||||
|
||||
__f16_to_f64 :: proc(f: f16) -> f64 #cc_odin #no_inline {
|
||||
return f64(__f16_to_f32(f));
|
||||
}
|
||||
*/
|
||||
@@ -1,100 +0,0 @@
|
||||
// TODO(bill): Use assembly instead here to implement atomics
|
||||
// Inline vs external file?
|
||||
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
|
||||
|
||||
|
||||
yield_thread :: proc() { win32.mm_pause(); }
|
||||
mfence :: proc() { win32.read_write_barrier(); }
|
||||
sfence :: proc() { win32.write_barrier(); }
|
||||
lfence :: proc() { win32.read_barrier(); }
|
||||
|
||||
|
||||
load :: proc(a: ^i32) -> i32 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i32, value: i32) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
|
||||
return win32.interlocked_compare_exchange(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
|
||||
return win32.interlocked_exchange(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.interlocked_exchange_add(a, operand);
|
||||
|
||||
}
|
||||
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.interlocked_and(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.interlocked_or(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter++;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i32) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i32) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
|
||||
|
||||
load :: proc(a: ^i64) -> i64 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i64, value: i64) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
|
||||
return win32.interlocked_compare_exchange64(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
|
||||
return win32.interlocked_exchange64(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.interlocked_exchange_add64(a, operand);
|
||||
}
|
||||
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.interlocked_and64(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.interlocked_or64(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter++;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i64) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i64) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
U8_MIN :: u8(0);
|
||||
U16_MIN :: u16(0);
|
||||
U32_MIN :: u32(0);
|
||||
U64_MIN :: u64(0);
|
||||
U128_MIN :: u128(0);
|
||||
|
||||
I8_MIN :: i8(-0x80);
|
||||
I16_MIN :: i16(-0x8000);
|
||||
I32_MIN :: i32(-0x8000_0000);
|
||||
I64_MIN :: i64(-0x8000_0000_0000_0000);
|
||||
I128_MIN :: i128(-0x8000_0000_0000_0000_0000_0000_0000_0000);
|
||||
|
||||
U8_MAX :: ~u8(0);
|
||||
U16_MAX :: ~u16(0);
|
||||
U32_MAX :: ~u32(0);
|
||||
U64_MAX :: ~u64(0);
|
||||
U128_MAX :: ~u128(0);
|
||||
|
||||
I8_MAX :: i8(0x7f);
|
||||
I16_MAX :: i16(0x7fff);
|
||||
I32_MAX :: i32(0x7fff_ffff);
|
||||
I64_MAX :: i64(0x7fff_ffff_ffff_ffff);
|
||||
I128_MAX :: i128(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff);
|
||||
|
||||
|
||||
count_ones :: proc(i: u8) -> u8 { __llvm_ctpop :: proc(u8) -> u8 #foreign __llvm_core "llvm.ctpop.i8"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i8) -> i8 { __llvm_ctpop :: proc(i8) -> i8 #foreign __llvm_core "llvm.ctpop.i8"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u16) -> u16 { __llvm_ctpop :: proc(u16) -> u16 #foreign __llvm_core "llvm.ctpop.i16"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i16) -> i16 { __llvm_ctpop :: proc(i16) -> i16 #foreign __llvm_core "llvm.ctpop.i16"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u32) -> u32 { __llvm_ctpop :: proc(u32) -> u32 #foreign __llvm_core "llvm.ctpop.i32"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i32) -> i32 { __llvm_ctpop :: proc(i32) -> i32 #foreign __llvm_core "llvm.ctpop.i32"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u64) -> u64 { __llvm_ctpop :: proc(u64) -> u64 #foreign __llvm_core "llvm.ctpop.i64"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i64) -> i64 { __llvm_ctpop :: proc(i64) -> i64 #foreign __llvm_core "llvm.ctpop.i64"; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u128) -> u128 { __llvm_ctpop :: proc(u128) -> u128 #foreign __llvm_core "llvm.ctpop.i128";return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i128) -> i128 { __llvm_ctpop :: proc(i128) -> i128 #foreign __llvm_core "llvm.ctpop.i128";return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(count_ones(u32(i))); } else { return uint(count_ones(u64(i))); } }
|
||||
count_ones :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(count_ones(i32(i))); } else { return int(count_ones(i64(i))); } }
|
||||
|
||||
count_zeros :: proc(i: u8) -> u8 { return 8 - count_ones(i); }
|
||||
count_zeros :: proc(i: i8) -> i8 { return 8 - count_ones(i); }
|
||||
count_zeros :: proc(i: u16) -> u16 { return 16 - count_ones(i); }
|
||||
count_zeros :: proc(i: i16) -> i16 { return 16 - count_ones(i); }
|
||||
count_zeros :: proc(i: u32) -> u32 { return 32 - count_ones(i); }
|
||||
count_zeros :: proc(i: i32) -> i32 { return 32 - count_ones(i); }
|
||||
count_zeros :: proc(i: u64) -> u64 { return 64 - count_ones(i); }
|
||||
count_zeros :: proc(i: i64) -> i64 { return 64 - count_ones(i); }
|
||||
count_zeros :: proc(i: u128) -> u128 { return 128 - count_ones(i); }
|
||||
count_zeros :: proc(i: i128) -> i128 { return 128 - count_ones(i); }
|
||||
count_zeros :: proc(i: uint) -> uint { return 8*size_of(uint) - count_ones(i); }
|
||||
count_zeros :: proc(i: int) -> int { return 8*size_of(int) - count_ones(i); }
|
||||
|
||||
|
||||
rotate_left :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
|
||||
rotate_left :: proc(i: i8, s: uint) -> i8 { return (i << s)|(i >> (8*size_of(i8) - s)); }
|
||||
rotate_left :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
|
||||
rotate_left :: proc(i: i16, s: uint) -> i16 { return (i << s)|(i >> (8*size_of(i16) - s)); }
|
||||
rotate_left :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
|
||||
rotate_left :: proc(i: i32, s: uint) -> i32 { return (i << s)|(i >> (8*size_of(i32) - s)); }
|
||||
rotate_left :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
|
||||
rotate_left :: proc(i: i64, s: uint) -> i64 { return (i << s)|(i >> (8*size_of(i64) - s)); }
|
||||
rotate_left :: proc(i: u128, s: uint) -> u128 { return (i << s)|(i >> (8*size_of(u128) - s)); }
|
||||
rotate_left :: proc(i: i128, s: uint) -> i128 { return (i << s)|(i >> (8*size_of(i128) - s)); }
|
||||
rotate_left :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_left(u32(i), s)); } else { return uint(rotate_left(u64(i), s)); } }
|
||||
rotate_left :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_left(i32(i), s)); } else { return int(rotate_left(i64(i), s)); } }
|
||||
|
||||
|
||||
rotate_right :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
|
||||
rotate_right :: proc(i: i8, s: uint) -> i8 { return (i >> s)|(i << (8*size_of(i8) - s)); }
|
||||
rotate_right :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
|
||||
rotate_right :: proc(i: i16, s: uint) -> i16 { return (i >> s)|(i << (8*size_of(i16) - s)); }
|
||||
rotate_right :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
|
||||
rotate_right :: proc(i: i32, s: uint) -> i32 { return (i >> s)|(i << (8*size_of(i32) - s)); }
|
||||
rotate_right :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
|
||||
rotate_right :: proc(i: i64, s: uint) -> i64 { return (i >> s)|(i << (8*size_of(i64) - s)); }
|
||||
rotate_right :: proc(i: u128, s: uint) -> u128 { return (i >> s)|(i << (8*size_of(u128) - s)); }
|
||||
rotate_right :: proc(i: i128, s: uint) -> i128 { return (i >> s)|(i << (8*size_of(i128) - s)); }
|
||||
rotate_right :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_right(u32(i), s)); } else { return uint(rotate_right(u64(i), s)); } }
|
||||
rotate_right :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_right(i32(i), s)); } else { return int(rotate_right(i64(i), s)); } }
|
||||
|
||||
|
||||
leading_zeros :: proc(i: u8) -> u8 { __llvm_ctlz :: proc(u8, bool) -> u8 #foreign __llvm_core "llvm.ctlz.i8"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i8) -> i8 { __llvm_ctlz :: proc(i8, bool) -> i8 #foreign __llvm_core "llvm.ctlz.i8"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u16) -> u16 { __llvm_ctlz :: proc(u16, bool) -> u16 #foreign __llvm_core "llvm.ctlz.i16"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i16) -> i16 { __llvm_ctlz :: proc(i16, bool) -> i16 #foreign __llvm_core "llvm.ctlz.i16"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u32) -> u32 { __llvm_ctlz :: proc(u32, bool) -> u32 #foreign __llvm_core "llvm.ctlz.i32"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i32) -> i32 { __llvm_ctlz :: proc(i32, bool) -> i32 #foreign __llvm_core "llvm.ctlz.i32"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u64) -> u64 { __llvm_ctlz :: proc(u64, bool) -> u64 #foreign __llvm_core "llvm.ctlz.i64"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i64) -> i64 { __llvm_ctlz :: proc(i64, bool) -> i64 #foreign __llvm_core "llvm.ctlz.i64"; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u128) -> u128 { __llvm_ctlz :: proc(u128, bool) -> u128 #foreign __llvm_core "llvm.ctlz.i128";return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i128) -> i128 { __llvm_ctlz :: proc(i128, bool) -> i128 #foreign __llvm_core "llvm.ctlz.i128";return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(leading_zeros(u32(i))); } else { return uint(leading_zeros(u64(i))); } }
|
||||
leading_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(leading_zeros(i32(i))); } else { return int(leading_zeros(i64(i))); } }
|
||||
|
||||
trailing_zeros :: proc(i: u8) -> u8 { __llvm_cttz :: proc(u8, bool) -> u8 #foreign __llvm_core "llvm.cttz.i8"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i8) -> i8 { __llvm_cttz :: proc(i8, bool) -> i8 #foreign __llvm_core "llvm.cttz.i8"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u16) -> u16 { __llvm_cttz :: proc(u16, bool) -> u16 #foreign __llvm_core "llvm.cttz.i16"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i16) -> i16 { __llvm_cttz :: proc(i16, bool) -> i16 #foreign __llvm_core "llvm.cttz.i16"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u32) -> u32 { __llvm_cttz :: proc(u32, bool) -> u32 #foreign __llvm_core "llvm.cttz.i32"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i32) -> i32 { __llvm_cttz :: proc(i32, bool) -> i32 #foreign __llvm_core "llvm.cttz.i32"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u64) -> u64 { __llvm_cttz :: proc(u64, bool) -> u64 #foreign __llvm_core "llvm.cttz.i64"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i64) -> i64 { __llvm_cttz :: proc(i64, bool) -> i64 #foreign __llvm_core "llvm.cttz.i64"; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u128) -> u128 { __llvm_cttz :: proc(u128, bool) -> u128 #foreign __llvm_core "llvm.cttz.i128";return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i128) -> i128 { __llvm_cttz :: proc(i128, bool) -> i128 #foreign __llvm_core "llvm.cttz.i128";return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(trailing_zeros(u32(i))); } else { return uint(trailing_zeros(u64(i))); } }
|
||||
trailing_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(trailing_zeros(i32(i))); } else { return int(trailing_zeros(i64(i))); } }
|
||||
|
||||
|
||||
reverse_bits :: proc(i: u8) -> u8 { __llvm_bitreverse :: proc(u8) -> u8 #foreign __llvm_core "llvm.bitreverse.i8"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i8) -> i8 { __llvm_bitreverse :: proc(i8) -> i8 #foreign __llvm_core "llvm.bitreverse.i8"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u16) -> u16 { __llvm_bitreverse :: proc(u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i16) -> i16 { __llvm_bitreverse :: proc(i16) -> i16 #foreign __llvm_core "llvm.bitreverse.i16"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u32) -> u32 { __llvm_bitreverse :: proc(u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i32) -> i32 { __llvm_bitreverse :: proc(i32) -> i32 #foreign __llvm_core "llvm.bitreverse.i32"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u64) -> u64 { __llvm_bitreverse :: proc(u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i64) -> i64 { __llvm_bitreverse :: proc(i64) -> i64 #foreign __llvm_core "llvm.bitreverse.i64"; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u128) -> u128 { __llvm_bitreverse :: proc(u128) -> u128 #foreign __llvm_core "llvm.bitreverse.i128";return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i128) -> i128 { __llvm_bitreverse :: proc(i128) -> i128 #foreign __llvm_core "llvm.bitreverse.i128";return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(reverse_bits(u32(i))); } else { return uint(reverse_bits(u64(i))); } }
|
||||
reverse_bits :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(reverse_bits(i32(i))); } else { return int(reverse_bits(i64(i))); } }
|
||||
|
||||
|
||||
byte_swap :: proc(u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
|
||||
byte_swap :: proc(i16) -> i16 #foreign __llvm_core "llvm.bswap.i16";
|
||||
byte_swap :: proc(u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
byte_swap :: proc(i32) -> i32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
byte_swap :: proc(u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
byte_swap :: proc(i64) -> i64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
byte_swap :: proc(u128) -> u128 #foreign __llvm_core "llvm.bswap.i128";
|
||||
byte_swap :: proc(i128) -> i128 #foreign __llvm_core "llvm.bswap.i128";
|
||||
byte_swap :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(byte_swap(u32(i))); } else { return uint(byte_swap(u64(i))); } }
|
||||
byte_swap :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(byte_swap(i32(i))); } else { return int(byte_swap(i64(i))); } }
|
||||
|
||||
|
||||
from_be :: proc(i: u8) -> u8 { return i; }
|
||||
from_be :: proc(i: i8) -> i8 { return i; }
|
||||
from_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
from_le :: proc(i: u8) -> u8 { return i; }
|
||||
from_le :: proc(i: i8) -> i8 { return i; }
|
||||
from_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
to_be :: proc(i: u8) -> u8 { return i; }
|
||||
to_be :: proc(i: i8) -> i8 { return i; }
|
||||
to_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
to_le :: proc(i: u8) -> u8 { return i; }
|
||||
to_le :: proc(i: i8) -> i8 { return i; }
|
||||
to_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
overflowing_add :: proc(lhs, rhs: u8) -> (u8, bool) { op :: proc(u8, u8) -> (u8, bool) #foreign __llvm_core "llvm.uadd.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i8) -> (i8, bool) { op :: proc(i8, i8) -> (i8, bool) #foreign __llvm_core "llvm.sadd.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u16) -> (u16, bool) { op :: proc(u16, u16) -> (u16, bool) #foreign __llvm_core "llvm.uadd.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i16) -> (i16, bool) { op :: proc(i16, i16) -> (i16, bool) #foreign __llvm_core "llvm.sadd.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u32) -> (u32, bool) { op :: proc(u32, u32) -> (u32, bool) #foreign __llvm_core "llvm.uadd.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i32) -> (i32, bool) { op :: proc(i32, i32) -> (i32, bool) #foreign __llvm_core "llvm.sadd.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u64) -> (u64, bool) { op :: proc(u64, u64) -> (u64, bool) #foreign __llvm_core "llvm.uadd.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i64) -> (i64, bool) { op :: proc(i64, i64) -> (i64, bool) #foreign __llvm_core "llvm.sadd.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u128) -> (u128, bool) { op :: proc(u128, u128) -> (u128, bool) #foreign __llvm_core "llvm.uadd.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i128) -> (i128, bool) { op :: proc(i128, i128) -> (i128, bool) #foreign __llvm_core "llvm.sadd.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_sub :: proc(lhs, rhs: u8) -> (u8, bool) { op :: proc(u8, u8) -> (u8, bool) #foreign __llvm_core "llvm.usub.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i8) -> (i8, bool) { op :: proc(i8, i8) -> (i8, bool) #foreign __llvm_core "llvm.ssub.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u16) -> (u16, bool) { op :: proc(u16, u16) -> (u16, bool) #foreign __llvm_core "llvm.usub.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i16) -> (i16, bool) { op :: proc(i16, i16) -> (i16, bool) #foreign __llvm_core "llvm.ssub.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u32) -> (u32, bool) { op :: proc(u32, u32) -> (u32, bool) #foreign __llvm_core "llvm.usub.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i32) -> (i32, bool) { op :: proc(i32, i32) -> (i32, bool) #foreign __llvm_core "llvm.ssub.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u64) -> (u64, bool) { op :: proc(u64, u64) -> (u64, bool) #foreign __llvm_core "llvm.usub.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i64) -> (i64, bool) { op :: proc(i64, i64) -> (i64, bool) #foreign __llvm_core "llvm.ssub.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u128) -> (u128, bool) { op :: proc(u128, u128) -> (u128, bool) #foreign __llvm_core "llvm.usub.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i128) -> (i128, bool) { op :: proc(i128, i128) -> (i128, bool) #foreign __llvm_core "llvm.ssub.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc(lhs, rhs: u8) -> (u8, bool) { op :: proc(u8, u8) -> (u8, bool) #foreign __llvm_core "llvm.umul.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i8) -> (i8, bool) { op :: proc(i8, i8) -> (i8, bool) #foreign __llvm_core "llvm.smul.with.overflow.i8"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u16) -> (u16, bool) { op :: proc(u16, u16) -> (u16, bool) #foreign __llvm_core "llvm.umul.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i16) -> (i16, bool) { op :: proc(i16, i16) -> (i16, bool) #foreign __llvm_core "llvm.smul.with.overflow.i16"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u32) -> (u32, bool) { op :: proc(u32, u32) -> (u32, bool) #foreign __llvm_core "llvm.umul.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i32) -> (i32, bool) { op :: proc(i32, i32) -> (i32, bool) #foreign __llvm_core "llvm.smul.with.overflow.i32"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u64) -> (u64, bool) { op :: proc(u64, u64) -> (u64, bool) #foreign __llvm_core "llvm.umul.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i64) -> (i64, bool) { op :: proc(i64, i64) -> (i64, bool) #foreign __llvm_core "llvm.smul.with.overflow.i64"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u128) -> (u128, bool) { op :: proc(u128, u128) -> (u128, bool) #foreign __llvm_core "llvm.umul.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i128) -> (i128, bool) { op :: proc(i128, i128) -> (i128, bool) #foreign __llvm_core "llvm.smul.with.overflow.i128"; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
is_power_of_two :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u128) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i128) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
@@ -0,0 +1,253 @@
|
||||
package bits
|
||||
|
||||
import "core:os"
|
||||
|
||||
U8_MIN :: 0;
|
||||
U16_MIN :: 0;
|
||||
U32_MIN :: 0;
|
||||
U64_MIN :: 0;
|
||||
|
||||
U8_MAX :: 1 << 8 - 1;
|
||||
U16_MAX :: 1 << 16 - 1;
|
||||
U32_MAX :: 1 << 32 - 1;
|
||||
U64_MAX :: 1 << 64 - 1;
|
||||
|
||||
I8_MIN :: - 1 << 7;
|
||||
I16_MIN :: - 1 << 15;
|
||||
I32_MIN :: - 1 << 31;
|
||||
I64_MIN :: - 1 << 63;
|
||||
|
||||
I8_MAX :: 1 << 7 - 1;
|
||||
I16_MAX :: 1 << 15 - 1;
|
||||
I32_MAX :: 1 << 31 - 1;
|
||||
I64_MAX :: 1 << 63 - 1;
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.ctlz.i8") leading_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.ctlz.i16") leading_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.ctlz.i32") leading_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.ctlz.i64") leading_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
|
||||
@(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
|
||||
@(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.bswap.i16") byte_swap_u16 :: proc(u16) -> u16 ---
|
||||
@(link_name="llvm.bswap.i32") byte_swap_u32 :: proc(u32) -> u32 ---
|
||||
@(link_name="llvm.bswap.i64") byte_swap_u64 :: proc(u64) -> u64 ---
|
||||
@(link_name="llvm.bswap.i16") byte_swap_i16 :: proc(i16) -> i16 ---
|
||||
@(link_name="llvm.bswap.i32") byte_swap_i32 :: proc(i32) -> i32 ---
|
||||
@(link_name="llvm.bswap.i64") byte_swap_i64 :: proc(i64) -> i64 ---
|
||||
}
|
||||
|
||||
byte_swap_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
return uint(byte_swap_u32(u32(i)));
|
||||
} else {
|
||||
return uint(byte_swap_u64(u64(i)));
|
||||
}
|
||||
}
|
||||
byte_swap_int :: proc(i: int) -> int {
|
||||
when size_of(int) == size_of(i32) {
|
||||
return int(byte_swap_i32(i32(i)));
|
||||
} else {
|
||||
return int(byte_swap_i64(i64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
byte_swap :: proc[
|
||||
byte_swap_u16,
|
||||
byte_swap_u32,
|
||||
byte_swap_u64,
|
||||
byte_swap_i16,
|
||||
byte_swap_i32,
|
||||
byte_swap_i64,
|
||||
byte_swap_uint,
|
||||
byte_swap_int,
|
||||
];
|
||||
|
||||
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
|
||||
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
|
||||
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
|
||||
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
|
||||
|
||||
|
||||
rotate_left8 :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
|
||||
rotate_left16 :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
|
||||
rotate_left32 :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
|
||||
rotate_left64 :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
|
||||
|
||||
|
||||
rotate_right8 :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
|
||||
rotate_right16 :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
|
||||
rotate_right32 :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
|
||||
rotate_right64 :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
|
||||
|
||||
from_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
from_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
to_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
to_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
|
||||
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_add :: proc[
|
||||
overflowing_add_u8, overflowing_add_i8,
|
||||
overflowing_add_u16, overflowing_add_i16,
|
||||
overflowing_add_u32, overflowing_add_i32,
|
||||
overflowing_add_u64, overflowing_add_i64,
|
||||
overflowing_add_uint, overflowing_add_int,
|
||||
];
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_sub :: proc[
|
||||
overflowing_sub_u8, overflowing_sub_i8,
|
||||
overflowing_sub_u16, overflowing_sub_i16,
|
||||
overflowing_sub_u32, overflowing_sub_i32,
|
||||
overflowing_sub_u64, overflowing_sub_i64,
|
||||
overflowing_sub_uint, overflowing_sub_int,
|
||||
];
|
||||
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc[
|
||||
overflowing_mul_u8, overflowing_mul_i8,
|
||||
overflowing_mul_u16, overflowing_mul_i16,
|
||||
overflowing_mul_u32, overflowing_mul_i32,
|
||||
overflowing_mul_u64, overflowing_mul_i64,
|
||||
overflowing_mul_uint, overflowing_mul_int,
|
||||
];
|
||||
|
||||
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
|
||||
is_power_of_two :: proc[
|
||||
is_power_of_two_u8, is_power_of_two_i8,
|
||||
is_power_of_two_u16, is_power_of_two_i16,
|
||||
is_power_of_two_u32, is_power_of_two_i32,
|
||||
is_power_of_two_u64, is_power_of_two_i64,
|
||||
is_power_of_two_uint, is_power_of_two_int,
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
package c
|
||||
|
||||
import b "core:builtin"
|
||||
import "core:os"
|
||||
|
||||
CHAR_BIT :: 8;
|
||||
|
||||
bool :: b.bool;
|
||||
char :: b.u8;
|
||||
byte :: b.byte;
|
||||
schar :: b.i8;
|
||||
uchar :: b.u8;
|
||||
short :: b.i16;
|
||||
ushort :: b.u16;
|
||||
int :: b.i32;
|
||||
uint :: b.u32;
|
||||
|
||||
long :: (os.OS == "windows" || size_of(b.rawptr) == 4) ? b.i32 : b.i64;
|
||||
ulong :: (os.OS == "windows" || size_of(b.rawptr) == 4) ? b.u32 : b.u64;
|
||||
|
||||
longlong :: b.i64;
|
||||
ulonglong :: b.u64;
|
||||
float :: b.f32;
|
||||
double :: b.f64;
|
||||
complex_float :: b.complex64;
|
||||
complex_double :: b.complex128;
|
||||
|
||||
#assert(size_of(b.uintptr) == size_of(b.int));
|
||||
|
||||
size_t :: b.uint;
|
||||
ssize_t :: b.int;
|
||||
ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
@@ -1,19 +1,17 @@
|
||||
// #import "fmt.odin";
|
||||
// Multiple precision decimal numbers
|
||||
// NOTE: This is only for floating point printing and nothing else
|
||||
package decimal
|
||||
|
||||
Decimal :: struct {
|
||||
digits: [384]u8, // big-endian digits
|
||||
digits: [384]byte, // big-endian digits
|
||||
count: int,
|
||||
decimal_point: int,
|
||||
neg, trunc: bool,
|
||||
}
|
||||
|
||||
decimal_to_string :: proc(buf: []u8, a: ^Decimal) -> string {
|
||||
digit_zero :: proc(buf: []u8) -> int {
|
||||
for _, i in buf {
|
||||
buf[i] = '0';
|
||||
}
|
||||
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
digit_zero :: proc(buf: []byte) -> int {
|
||||
for _, i in buf do buf[i] = '0';
|
||||
return len(buf);
|
||||
}
|
||||
|
||||
@@ -22,35 +20,35 @@ decimal_to_string :: proc(buf: []u8, a: ^Decimal) -> string {
|
||||
|
||||
// TODO(bill): make this work with a buffer that's not big enough
|
||||
assert(len(buf) >= n);
|
||||
buf = buf[0..<n];
|
||||
buf = buf[0:n];
|
||||
|
||||
if a.count == 0 {
|
||||
buf[0] = '0';
|
||||
return string(buf[0..<1]);
|
||||
return string(buf[0:1]);
|
||||
}
|
||||
|
||||
w := 0;
|
||||
if a.decimal_point <= 0 {
|
||||
buf[w] = '0'; w++;
|
||||
buf[w] = '.'; w++;
|
||||
w += digit_zero(buf[w ..< w-a.decimal_point]);
|
||||
w += copy(buf[w..], a.digits[0..<a.count]);
|
||||
buf[w] = '0'; w += 1;
|
||||
buf[w] = '.'; w += 1;
|
||||
w += digit_zero(buf[w : w-a.decimal_point]);
|
||||
w += copy(buf[w:], a.digits[0:a.count]);
|
||||
} else if a.decimal_point < a.count {
|
||||
w += copy(buf[w..], a.digits[0..<a.decimal_point]);
|
||||
buf[w] = '.'; w++;
|
||||
w += copy(buf[w..], a.digits[a.decimal_point ..< a.count]);
|
||||
w += copy(buf[w:], a.digits[0:a.decimal_point]);
|
||||
buf[w] = '.'; w += 1;
|
||||
w += copy(buf[w:], a.digits[a.decimal_point : a.count]);
|
||||
} else {
|
||||
w += copy(buf[w..], a.digits[0..<a.count]);
|
||||
w += digit_zero(buf[w ..< w+a.decimal_point-a.count]);
|
||||
w += copy(buf[w:], a.digits[0:a.count]);
|
||||
w += digit_zero(buf[w : w+a.decimal_point-a.count]);
|
||||
}
|
||||
|
||||
return string(buf[0..<w]);
|
||||
return string(buf[0:w]);
|
||||
}
|
||||
|
||||
// trim trailing zeros
|
||||
trim :: proc(a: ^Decimal) {
|
||||
for a.count > 0 && a.digits[a.count-1] == '0' {
|
||||
a.count--;
|
||||
a.count -= 1;
|
||||
}
|
||||
if a.count == 0 {
|
||||
a.decimal_point = 0;
|
||||
@@ -59,34 +57,33 @@ trim :: proc(a: ^Decimal) {
|
||||
|
||||
|
||||
assign :: proc(a: ^Decimal, i: u64) {
|
||||
buf: [32]u8;
|
||||
buf: [64]byte;
|
||||
n := 0;
|
||||
for i > 0 {
|
||||
j := i/10;
|
||||
i -= 10*j;
|
||||
buf[n] = u8('0'+i);
|
||||
n++;
|
||||
buf[n] = byte('0'+i);
|
||||
n += 1;
|
||||
i = j;
|
||||
}
|
||||
|
||||
a.count = 0;
|
||||
for n--; n >= 0; n-- {
|
||||
for n -= 1; n >= 0; n -= 1 {
|
||||
a.digits[a.count] = buf[n];
|
||||
a.count++;
|
||||
a.count += 1;
|
||||
}
|
||||
a.decimal_point = a.count;
|
||||
trim(a);
|
||||
}
|
||||
|
||||
uint_size :: 8*size_of(uint);
|
||||
max_shift :: uint_size-4;
|
||||
|
||||
|
||||
shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
r := 0; // read index
|
||||
w := 0; // write index
|
||||
|
||||
n: uint;
|
||||
for ; n>>k == 0; r++ {
|
||||
for ; n>>k == 0; r += 1 {
|
||||
if r >= a.count {
|
||||
if n == 0 {
|
||||
// Just in case
|
||||
@@ -95,7 +92,7 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
}
|
||||
for n>>k == 0 {
|
||||
n = n * 10;
|
||||
r++;
|
||||
r += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -106,12 +103,12 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
|
||||
mask: uint = (1<<k) - 1;
|
||||
|
||||
for ; r < a.count; r++ {
|
||||
for ; r < a.count; r += 1 {
|
||||
c := uint(a.digits[r]);
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
a.digits[w] = u8('0' + dig);
|
||||
w++;
|
||||
a.digits[w] = byte('0' + dig);
|
||||
w += 1;
|
||||
n = n*10 + c - '0';
|
||||
}
|
||||
|
||||
@@ -119,8 +116,8 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + dig);
|
||||
w++;
|
||||
a.digits[w] = byte('0' + dig);
|
||||
w += 1;
|
||||
} else if dig > 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
@@ -139,13 +136,13 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
w := a.count+delta; // write index
|
||||
|
||||
n: uint;
|
||||
for r--; r >= 0; r-- {
|
||||
for r -= 1; r >= 0; r -= 1 {
|
||||
n += (uint(a.digits[r]) - '0') << k;
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w--;
|
||||
w -= 1;
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + rem);
|
||||
a.digits[w] = byte('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
@@ -155,9 +152,9 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
for n > 0 {
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w--;
|
||||
w -= 1;
|
||||
if 0 <= w && w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + rem);
|
||||
a.digits[w] = byte('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
@@ -171,7 +168,10 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
}
|
||||
|
||||
shift :: proc(a: ^Decimal, k: int) {
|
||||
match {
|
||||
uint_size :: 8*size_of(uint);
|
||||
max_shift :: uint_size-4;
|
||||
|
||||
switch {
|
||||
case a.count == 0:
|
||||
// no need to update
|
||||
case k > 0:
|
||||
@@ -194,9 +194,7 @@ shift :: proc(a: ^Decimal, k: int) {
|
||||
can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
|
||||
if nd < 0 || nd >= a.count { return false ; }
|
||||
if a.digits[nd] == '5' && nd+1 == a.count {
|
||||
if a.trunc {
|
||||
return true;
|
||||
}
|
||||
if a.trunc do return true;
|
||||
return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
|
||||
}
|
||||
|
||||
@@ -215,9 +213,9 @@ round :: proc(a: ^Decimal, nd: int) {
|
||||
round_up :: proc(a: ^Decimal, nd: int) {
|
||||
if nd < 0 || nd >= a.count { return; }
|
||||
|
||||
for i := nd-1; i >= 0; i-- {
|
||||
for i := nd-1; i >= 0; i -= 1 {
|
||||
if c := a.digits[i]; c < '9' {
|
||||
a.digits[i]++;
|
||||
a.digits[i] += 1;
|
||||
a.count = i+1;
|
||||
return;
|
||||
}
|
||||
@@ -226,7 +224,7 @@ round_up :: proc(a: ^Decimal, nd: int) {
|
||||
// Number is just 9s
|
||||
a.digits[0] = '1';
|
||||
a.count = 1;
|
||||
a.decimal_point++;
|
||||
a.decimal_point += 1;
|
||||
}
|
||||
|
||||
round_down :: proc(a: ^Decimal, nd: int) {
|
||||
@@ -241,17 +239,17 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
|
||||
if a.decimal_point > 20 {
|
||||
return 0xffff_ffff_ffff_ffff;
|
||||
}
|
||||
i: int;
|
||||
i: int = 0;
|
||||
n: u64 = 0;
|
||||
m := min(a.decimal_point, a.count);
|
||||
for i = 0; i < m; i++ {
|
||||
for ; i < m; i += 1 {
|
||||
n = n*10 + u64(a.digits[i]-'0');
|
||||
}
|
||||
for ; i < a.decimal_point; i++ {
|
||||
for ; i < a.decimal_point; i += 1 {
|
||||
n *= 10;
|
||||
}
|
||||
if can_round_up(a, a.decimal_point) {
|
||||
n++;
|
||||
n += 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
-1207
File diff suppressed because it is too large
Load Diff
+1449
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,25 @@
|
||||
crc32 :: proc(data: []u8) -> u32 {
|
||||
package hash
|
||||
|
||||
import "core:mem"
|
||||
|
||||
adler32 :: proc(data: []byte) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = 1, 0;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~u32(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: []u8) -> u64 {
|
||||
crc64 :: proc(data: []byte) -> u64 {
|
||||
result := ~u64(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
@@ -13,7 +27,7 @@ crc64 :: proc(data: []u8) -> u64 {
|
||||
return ~result;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: []u8) -> u32 {
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
@@ -21,7 +35,7 @@ fnv32 :: proc(data: []u8) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: []u8) -> u64 {
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
@@ -29,7 +43,7 @@ fnv64 :: proc(data: []u8) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: []u8) -> u32 {
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
@@ -37,7 +51,7 @@ fnv32a :: proc(data: []u8) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: []u8) -> u64 {
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
@@ -45,17 +59,17 @@ fnv64a :: proc(data: []u8) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []u8) -> u32 {
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := &data[0];
|
||||
p1 := p + 4*nblocks;
|
||||
p1 := mem.ptr_offset(p, 4*nblocks);
|
||||
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := ^u32(p)^;
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
k1 := (cast(^u32)p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
@@ -66,10 +80,9 @@ murmur32 :: proc(data: []u8) -> u32 {
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4 ..];
|
||||
|
||||
tail := data[nblocks*4:];
|
||||
k1: u32;
|
||||
match len(tail)&3 {
|
||||
switch len(tail)&3 {
|
||||
case 3:
|
||||
k1 ~= u32(tail[2]) << 16;
|
||||
fallthrough;
|
||||
@@ -95,7 +108,7 @@ murmur32 :: proc(data: []u8) -> u32 {
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []u8) -> u64 {
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when size_of(int) == 8 {
|
||||
@@ -103,7 +116,7 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := slice_ptr(^u64(&data[0]), len(data)/size_of(u64));
|
||||
data64 := mem.slice_ptr(cast(^u64)&data[0], len(data)/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
@@ -116,7 +129,7 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
h *= m;
|
||||
}
|
||||
|
||||
match len(data)&7 {
|
||||
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;
|
||||
@@ -139,14 +152,13 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
|
||||
data32 := slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
data32 := mem.slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
|
||||
i := 0;
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data32[i]; i++;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
@@ -154,7 +166,7 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data32[i]; i++;
|
||||
k2 = data32[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
@@ -165,7 +177,7 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data32[i]; i++;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
@@ -175,8 +187,8 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := slice_to_bytes(data32[i..])[0..<3];
|
||||
match len {
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3];
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
@@ -197,13 +209,12 @@ murmur64 :: proc(data: []u8) -> u64 {
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
h := cast(u64)(h1)<<32 | cast(u64)(h2);
|
||||
return h;
|
||||
return u64(h1)<<32 | u64(h2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
immutable _crc32_table := [256]u32{
|
||||
_crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
@@ -269,7 +280,7 @@ immutable _crc32_table := [256]u32{
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
immutable _crc64_table := [256]u64{
|
||||
_crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
@@ -0,0 +1,34 @@
|
||||
package log
|
||||
|
||||
Level :: enum {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Fatal,
|
||||
}
|
||||
|
||||
Option :: enum {
|
||||
Level,
|
||||
Time,
|
||||
File,
|
||||
Line,
|
||||
Procedure,
|
||||
}
|
||||
Options :: bit_set[Option];
|
||||
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location);
|
||||
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil};
|
||||
}
|
||||
-379
@@ -1,379 +0,0 @@
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
|
||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||
|
||||
EPSILON :: 1.19209290e-7;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
|
||||
Vec2 :: [vector 2]f32;
|
||||
Vec3 :: [vector 3]f32;
|
||||
Vec4 :: [vector 4]f32;
|
||||
|
||||
// Column major
|
||||
Mat2 :: [2][2]f32;
|
||||
Mat3 :: [3][3]f32;
|
||||
Mat4 :: [4][4]f32;
|
||||
|
||||
Complex :: complex64;
|
||||
|
||||
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
|
||||
sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
|
||||
cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
|
||||
tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); }
|
||||
tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); }
|
||||
|
||||
pow :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32";
|
||||
pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
|
||||
|
||||
|
||||
lerp :: proc(a, b, t: f32) -> (x: f32) { return a*(1-t) + b*t; }
|
||||
lerp :: proc(a, b, t: f64) -> (x: f64) { return a*(1-t) + b*t; }
|
||||
unlerp :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
|
||||
unlerp :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
|
||||
|
||||
|
||||
sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
|
||||
sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
|
||||
|
||||
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
|
||||
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32, x);
|
||||
iy := transmute(u32, y);
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32, ix);
|
||||
}
|
||||
|
||||
copy_sign :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64, x);
|
||||
iy := transmute(u64, y);
|
||||
ix &= 0x7fff_ffff_ffff_ff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64, ix);
|
||||
}
|
||||
|
||||
round :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
round :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
|
||||
floor :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
|
||||
ceil :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); } // TODO: Get accurate versions
|
||||
ceil :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); } // TODO: Get accurate versions
|
||||
|
||||
remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
|
||||
mod :: proc(x, y: f32) -> f32 {
|
||||
y = abs(y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod :: proc(x, y: f64) -> f64 {
|
||||
y = abs(y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
cross :: proc(x, y: Vec3) -> Vec3 {
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
|
||||
mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); }
|
||||
|
||||
norm :: proc(v: Vec2) -> Vec2 { return v / mag(v); }
|
||||
norm :: proc(v: Vec3) -> Vec3 { return v / mag(v); }
|
||||
norm :: proc(v: Vec4) -> Vec4 { return v / mag(v); }
|
||||
|
||||
norm0 :: proc(v: Vec2) -> Vec2 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return 0;
|
||||
}
|
||||
return v / m;
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec3) -> Vec3 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return 0;
|
||||
}
|
||||
return v / m;
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec4) -> Vec4 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return 0;
|
||||
}
|
||||
return v / m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
mat4_identity :: proc() -> Mat4 {
|
||||
return Mat4{
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
};
|
||||
}
|
||||
|
||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4;
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2] +
|
||||
a[3][i]*b[j][3];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
return Vec4{
|
||||
m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w,
|
||||
m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
|
||||
m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w,
|
||||
m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
|
||||
};
|
||||
}
|
||||
|
||||
inverse :: proc(m: Mat4) -> Mat4 {
|
||||
o: Mat4;
|
||||
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
|
||||
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
|
||||
|
||||
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||
m[0][1] * o[0][1] +
|
||||
m[0][2] * o[0][2] +
|
||||
m[0][3] * o[0][3]);
|
||||
|
||||
o[0][0] *= ood;
|
||||
o[0][1] *= ood;
|
||||
o[0][2] *= ood;
|
||||
o[0][3] *= ood;
|
||||
o[1][0] *= ood;
|
||||
o[1][1] *= ood;
|
||||
o[1][2] *= ood;
|
||||
o[1][3] *= ood;
|
||||
o[2][0] *= ood;
|
||||
o[2][1] *= ood;
|
||||
o[2][2] *= ood;
|
||||
o[2][3] *= ood;
|
||||
o[3][0] *= ood;
|
||||
o[3][1] *= ood;
|
||||
o[3][2] *= ood;
|
||||
o[3][3] *= ood;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[3][0] = v.x;
|
||||
m[3][1] = v.y;
|
||||
m[3][2] = v.z;
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
c := cos(angle_radians);
|
||||
s := sin(angle_radians);
|
||||
|
||||
a := norm(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot := mat4_identity();
|
||||
|
||||
rot[0][0] = c + t.x*a.x;
|
||||
rot[0][1] = 0 + t.x*a.y + s*a.z;
|
||||
rot[0][2] = 0 + t.x*a.z - s*a.y;
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t.y*a.x - s*a.z;
|
||||
rot[1][1] = c + t.y*a.y;
|
||||
rot[1][2] = 0 + t.y*a.z + s*a.x;
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t.z*a.x + s*a.y;
|
||||
rot[2][1] = 0 + t.z*a.y - s*a.x;
|
||||
rot[2][2] = c + t.z*a.z;
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||
m[0][0] *= v.x;
|
||||
m[1][1] *= v.y;
|
||||
m[2][2] *= v.z;
|
||||
return m;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||
m[0][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[2][2] *= s;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
f := norm(centre - eye);
|
||||
s := norm(cross(f, up));
|
||||
u := cross(s, f);
|
||||
|
||||
return Mat4{
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
|
||||
};
|
||||
}
|
||||
|
||||
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan(0.5 * fovy);
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[2][3] = -1.0;
|
||||
m[3][2] = -2.0*far*near / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[0][0] = +2.0 / (right - left);
|
||||
m[1][1] = +2.0 / (top - bottom);
|
||||
m[2][2] = -2.0 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
@@ -0,0 +1,494 @@
|
||||
package math
|
||||
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||
|
||||
EPSILON :: 1.19209290e-7;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
|
||||
Vec2 :: distinct [2]f32;
|
||||
Vec3 :: distinct [3]f32;
|
||||
Vec4 :: distinct [4]f32;
|
||||
|
||||
// Column major
|
||||
Mat2 :: distinct [2][2]f32;
|
||||
Mat3 :: distinct [3][3]f32;
|
||||
Mat4 :: distinct [4][4]f32;
|
||||
|
||||
Quat :: struct {x, y, z, w: f32};
|
||||
|
||||
QUAT_IDENTITY := Quat{x = 0, y = 0, z = 0, w = 1};
|
||||
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.sqrt.f32")
|
||||
sqrt_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.sqrt.f64")
|
||||
sqrt_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.sin.f32")
|
||||
sin_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.sin.f64")
|
||||
sin_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.cos.f32")
|
||||
cos_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.cos.f64")
|
||||
cos_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.pow.f32")
|
||||
pow_f32 :: proc(x, power: f32) -> f32 ---;
|
||||
@(link_name="llvm.pow.f64")
|
||||
pow_f64 :: proc(x, power: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.fmuladd.f32")
|
||||
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
|
||||
@(link_name="llvm.fmuladd.f64")
|
||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.log.f32")
|
||||
log_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.log.f64")
|
||||
log_f64 :: proc(x: f64) -> f64 ---;
|
||||
}
|
||||
|
||||
log :: proc[log_f32, log_f64];
|
||||
|
||||
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
|
||||
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
|
||||
|
||||
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
|
||||
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
|
||||
|
||||
|
||||
sign_f32 :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
|
||||
sign_f64 :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
|
||||
|
||||
copy_sign_f32 :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
|
||||
copy_sign_f64 :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64)x;
|
||||
iy := transmute(u64)y;
|
||||
ix &= 0x7fff_ffff_ffff_ff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64)ix;
|
||||
}
|
||||
|
||||
|
||||
sqrt :: proc[sqrt_f32, sqrt_f64];
|
||||
sin :: proc[sin_f32, sin_f64];
|
||||
cos :: proc[cos_f32, cos_f64];
|
||||
tan :: proc[tan_f32, tan_f64];
|
||||
pow :: proc[pow_f32, pow_f64];
|
||||
fmuladd :: proc[fmuladd_f32, fmuladd_f64];
|
||||
sign :: proc[sign_f32, sign_f64];
|
||||
copy_sign :: proc[copy_sign_f32, copy_sign_f64];
|
||||
|
||||
|
||||
round_f32 :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
round_f64 :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
round :: proc[round_f32, round_f64];
|
||||
|
||||
floor_f32 :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor_f64 :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor :: proc[floor_f32, floor_f64];
|
||||
|
||||
ceil_f32 :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); }// TODO: Get accurate versions
|
||||
ceil_f64 :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); }// TODO: Get accurate versions
|
||||
ceil :: proc[ceil_f32, ceil_f64];
|
||||
|
||||
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
remainder :: proc[remainder_f32, remainder_f64];
|
||||
|
||||
mod_f32 :: proc(x, y: f32) -> f32 {
|
||||
result: f32;
|
||||
y = abs(y);
|
||||
result = remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod_f64 :: proc(x, y: f64) -> f64 {
|
||||
result: f64;
|
||||
y = abs(y);
|
||||
result = remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod :: proc[mod_f32, mod_f64];
|
||||
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
|
||||
mul :: proc[
|
||||
mat3_mul,
|
||||
mat4_mul, mat4_mul_vec4,
|
||||
quat_mul, quat_mulf,
|
||||
];
|
||||
|
||||
div :: proc[
|
||||
quat_div, quat_divf,
|
||||
];
|
||||
|
||||
inverse :: proc[mat4_inverse, quat_inverse];
|
||||
dot :: proc[vec_dot, quat_dot];
|
||||
cross :: proc[cross2, cross3];
|
||||
|
||||
vec_dot :: proc(a, b: $T/[$N]$E) -> E {
|
||||
res: E;
|
||||
for i in 0..N-1 {
|
||||
res += a[i] * b[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
cross2 :: proc(a, b: $T/[2]$E) -> E {
|
||||
return a[0]*b[1] - a[1]*b[0];
|
||||
}
|
||||
|
||||
cross3 :: proc(a, b: $T/[3]$E) -> T {
|
||||
i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
|
||||
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
|
||||
return T(i - j);
|
||||
}
|
||||
|
||||
|
||||
length :: proc(v: $T/[$N]$E) -> E { return sqrt(dot(v, v)); }
|
||||
|
||||
norm :: proc(v: $T/[$N]$E) -> T { return v / length(v); }
|
||||
|
||||
norm0 :: proc(v: $T/[$N]$E) -> T {
|
||||
m := length(v);
|
||||
return m == 0 ? 0 : v/m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> T {
|
||||
m: T;
|
||||
for i in 0..N-1 do m[i][i] = E(1);
|
||||
return m;
|
||||
}
|
||||
|
||||
transpose :: proc(m: $M/[$N][N]f32) -> M {
|
||||
for j in 0..N-1 {
|
||||
for i in 0..N-1 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
mat3_mul :: proc(a, b: Mat3) -> Mat3 {
|
||||
c: Mat3;
|
||||
for j in 0..2 {
|
||||
for i in 0..2 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4;
|
||||
for j in 0..3 {
|
||||
for i in 0..3 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2] +
|
||||
a[3][i]*b[j][3];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
return Vec4{
|
||||
m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3],
|
||||
m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3],
|
||||
m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3],
|
||||
m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3],
|
||||
};
|
||||
}
|
||||
|
||||
mat4_inverse :: proc(m: Mat4) -> Mat4 {
|
||||
o: Mat4;
|
||||
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
|
||||
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
|
||||
|
||||
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||
m[0][1] * o[0][1] +
|
||||
m[0][2] * o[0][2] +
|
||||
m[0][3] * o[0][3]);
|
||||
|
||||
o[0][0] *= ood;
|
||||
o[0][1] *= ood;
|
||||
o[0][2] *= ood;
|
||||
o[0][3] *= ood;
|
||||
o[1][0] *= ood;
|
||||
o[1][1] *= ood;
|
||||
o[1][2] *= ood;
|
||||
o[1][3] *= ood;
|
||||
o[2][0] *= ood;
|
||||
o[2][1] *= ood;
|
||||
o[2][2] *= ood;
|
||||
o[2][3] *= ood;
|
||||
o[3][0] *= ood;
|
||||
o[3][1] *= ood;
|
||||
o[3][2] *= ood;
|
||||
o[3][3] *= ood;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||
m := identity(Mat4);
|
||||
m[3][0] = v[0];
|
||||
m[3][1] = v[1];
|
||||
m[3][2] = v[2];
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
c := cos(angle_radians);
|
||||
s := sin(angle_radians);
|
||||
|
||||
a := norm(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot := identity(Mat4);
|
||||
|
||||
rot[0][0] = c + t[0]*a[0];
|
||||
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
|
||||
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
|
||||
rot[1][1] = c + t[1]*a[1];
|
||||
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
|
||||
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
|
||||
rot[2][2] = c + t[2]*a[2];
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
scale_vec3 :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||
m[0][0] *= v[0];
|
||||
m[1][1] *= v[1];
|
||||
m[2][2] *= v[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
scale_f32 :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||
m[0][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[2][2] *= s;
|
||||
return m;
|
||||
}
|
||||
|
||||
scale :: proc[scale_vec3, scale_f32];
|
||||
|
||||
|
||||
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
f := norm(centre - eye);
|
||||
s := norm(cross(f, up));
|
||||
u := cross(s, f);
|
||||
|
||||
return Mat4{
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
|
||||
};
|
||||
}
|
||||
|
||||
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan(0.5 * fovy);
|
||||
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[2][3] = -1.0;
|
||||
m[3][2] = -2.0*far*near / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
m := identity(Mat4);
|
||||
m[0][0] = +2.0 / (right - left);
|
||||
m[1][1] = +2.0 / (top - bottom);
|
||||
m[2][2] = -2.0 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
// Quaternion operations
|
||||
|
||||
conj :: proc(q: Quat) -> Quat {
|
||||
return Quat{-q.x, -q.y, -q.z, q.w};
|
||||
}
|
||||
|
||||
quat_mul :: proc(q0, q1: Quat) -> Quat {
|
||||
d: Quat;
|
||||
d.x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y;
|
||||
d.y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x;
|
||||
d.z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w;
|
||||
d.w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z;
|
||||
return d;
|
||||
}
|
||||
|
||||
quat_mulf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x*f, q.y*f, q.z*f, q.w*f}; }
|
||||
quat_divf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x/f, q.y/f, q.z/f, q.w/f}; }
|
||||
|
||||
quat_div :: proc(q0, q1: Quat) -> Quat { return mul(q0, quat_inverse(q1)); }
|
||||
quat_inverse :: proc(q: Quat) -> Quat { return div(conj(q), dot(q, q)); }
|
||||
quat_dot :: proc(q0, q1: Quat) -> f32 { return q0.x*q1.x + q0.y*q1.y + q0.z*q1.z + q0.w*q1.w; }
|
||||
|
||||
quat_norm :: proc(q: Quat) -> Quat {
|
||||
m := sqrt(dot(q, q));
|
||||
return div(q, m);
|
||||
}
|
||||
|
||||
axis_angle :: proc(axis: Vec3, angle_radians: f32) -> Quat {
|
||||
v := norm(axis) * sin(0.5*angle_radians);
|
||||
w := cos(0.5*angle_radians);
|
||||
return Quat{v.x, v.y, v.z, w};
|
||||
}
|
||||
|
||||
euler_angles :: proc(pitch, yaw, roll: f32) -> Quat {
|
||||
p := axis_angle(Vec3{1, 0, 0}, pitch);
|
||||
y := axis_angle(Vec3{0, 1, 0}, yaw);
|
||||
r := axis_angle(Vec3{0, 0, 1}, roll);
|
||||
return mul(mul(y, p), r);
|
||||
}
|
||||
|
||||
quat_to_mat4 :: proc(q: Quat) -> Mat4 {
|
||||
a := quat_norm(q);
|
||||
xx := a.x*a.x; yy := a.y*a.y; zz := a.z*a.z;
|
||||
xy := a.x*a.y; xz := a.x*a.z; yz := a.y*a.z;
|
||||
wx := a.w*a.x; wy := a.w*a.y; wz := a.w*a.z;
|
||||
|
||||
m := identity(Mat4);
|
||||
|
||||
m[0][0] = 1 - 2*(yy + zz);
|
||||
m[0][1] = 2*(xy + wz);
|
||||
m[0][2] = 2*(xz - wy);
|
||||
|
||||
m[1][0] = 2*(xy - wz);
|
||||
m[1][1] = 1 - 2*(xx + zz);
|
||||
m[1][2] = 2*(yz + wx);
|
||||
|
||||
m[2][0] = 2*(xz + wy);
|
||||
m[2][1] = 2*(yz - wx);
|
||||
m[2][2] = 1 - 2*(xx + yy);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
@@ -0,0 +1,62 @@
|
||||
package rand
|
||||
|
||||
Rand :: struct {
|
||||
state: u64,
|
||||
inc: u64,
|
||||
}
|
||||
|
||||
init :: proc(r: ^Rand, seed: u64 = 8675309) {
|
||||
r.state = 0;
|
||||
r.inc = (seed << 1) | 1;
|
||||
_random(r);
|
||||
r.state += seed;
|
||||
_random(r);
|
||||
}
|
||||
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
old_state := r.state;
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1);
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
|
||||
rot := u32(old_state >> 59);
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
uint32 :: proc(r: ^Rand) -> u32 { return _random(r); }
|
||||
|
||||
uint64 :: proc(r: ^Rand) -> u64 {
|
||||
a := u64(_random(r));
|
||||
b := u64(_random(r));
|
||||
return (a<<32) | b;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
|
||||
int31_max :: proc(r: ^Rand, n: i32) -> i32 {
|
||||
if n <= 0 do panic("Invalid argument to int31_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int31(r) & (n-1);
|
||||
}
|
||||
max := i32((1<<31) - 1 - (1<<31)&u32(n));
|
||||
v := int31(r);
|
||||
for v > max {
|
||||
v = int31(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int63_max :: proc(r: ^Rand, n: i64) -> i64 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int63(r) & (n-1);
|
||||
}
|
||||
max := i64((1<<63) - 1 - (1<<63)&u64(n));
|
||||
v := int63(r);
|
||||
for v > max {
|
||||
v = int63(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
float64 :: proc(r: ^Rand) -> f64 { return f64(int63_max(r, 1<<53)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand) -> f32 { return f32(float64(r)); }
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
|
||||
swap :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
|
||||
swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
|
||||
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
return __mem_set(data, value, len);
|
||||
}
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_zero(data, len);
|
||||
}
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy(dst, src, len);
|
||||
}
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: proc(a, b: []u8) -> int {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int #inline { return megabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false;
|
||||
}
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uint(align);
|
||||
p := uint(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 {
|
||||
p += a - modulo;
|
||||
}
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AllocationHeader :: struct {
|
||||
size: int,
|
||||
}
|
||||
|
||||
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := ^int(header+1);
|
||||
|
||||
for i := 0; rawptr(ptr) < data; i++ {
|
||||
(ptr+i)^ = -1;
|
||||
}
|
||||
}
|
||||
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||
if data == nil {
|
||||
return nil;
|
||||
}
|
||||
p := ^int(data);
|
||||
for (p-1)^ == -1 {
|
||||
p = (p-1);
|
||||
}
|
||||
return ^AllocationHeader(p-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Custom allocators
|
||||
Arena :: struct {
|
||||
backing: Allocator,
|
||||
offset: int,
|
||||
memory: []u8,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
ArenaTempMemory :: struct {
|
||||
arena: ^Arena,
|
||||
original_count: int,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []u8) {
|
||||
backing = Allocator{};
|
||||
memory = data[0..<0];
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator;
|
||||
memory = make([]u8, size);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
free_arena :: proc(using a: ^Arena) {
|
||||
if backing.procedure != nil {
|
||||
push_allocator backing {
|
||||
free(memory);
|
||||
memory = nil;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = arena_allocator_proc,
|
||||
data = arena,
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: AllocatorMode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using AllocatorMode;
|
||||
arena := ^Arena(allocator_data);
|
||||
|
||||
match mode {
|
||||
case Alloc:
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.memory) {
|
||||
fmt.fprintln(os.stderr, "Arena out of memory");
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.memory[arena.offset];
|
||||
|
||||
ptr := align_forward(end, alignment);
|
||||
arena.offset += total_size;
|
||||
return zero(ptr, size);
|
||||
|
||||
case Free:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use ArenaTempMemory if you want to free a block
|
||||
|
||||
case FreeAll:
|
||||
arena.offset = 0;
|
||||
|
||||
case Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
|
||||
tmp: ArenaTempMemory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count++;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.memory = arena.memory[0..<original_count];
|
||||
arena.temp_count--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
align_of_type_info :: proc(type_info: ^TypeInfo) -> int {
|
||||
prev_pow2 :: proc(n: i64) -> i64 {
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
WORD_SIZE :: size_of(int);
|
||||
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
|
||||
using TypeInfo;
|
||||
match info in type_info {
|
||||
case Named:
|
||||
return align_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size;
|
||||
case String:
|
||||
return WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
return align_of_type_info(info.elem);
|
||||
case DynamicArray:
|
||||
return WORD_SIZE;
|
||||
case Slice:
|
||||
return WORD_SIZE;
|
||||
case Vector:
|
||||
size := size_of_type_info(info.elem);
|
||||
count := int(max(prev_pow2(i64(info.count)), 1));
|
||||
total := size * count;
|
||||
return clamp(total, 1, MAX_ALIGN);
|
||||
case Tuple:
|
||||
return info.align;
|
||||
case Struct:
|
||||
return info.align;
|
||||
case Union:
|
||||
return info.align;
|
||||
case RawUnion:
|
||||
return info.align;
|
||||
case Enum:
|
||||
return align_of_type_info(info.base);
|
||||
case Map:
|
||||
return align_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_formula :: proc(size, align: int) -> int {
|
||||
result := size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
|
||||
size_of_type_info :: proc(type_info: ^TypeInfo) -> int {
|
||||
WORD_SIZE :: size_of(int);
|
||||
using TypeInfo;
|
||||
match info in type_info {
|
||||
case Named:
|
||||
return size_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size;
|
||||
case String:
|
||||
return 2*WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return 2*WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case DynamicArray:
|
||||
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
|
||||
case Slice:
|
||||
return 2*WORD_SIZE;
|
||||
case Vector:
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case Struct:
|
||||
return info.size;
|
||||
case Union:
|
||||
return info.size;
|
||||
case RawUnion:
|
||||
return info.size;
|
||||
case Enum:
|
||||
return size_of_type_info(info.base);
|
||||
case Map:
|
||||
return size_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
package mem
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
Allocator_Mode :: enum byte {
|
||||
Alloc,
|
||||
Free,
|
||||
Free_All,
|
||||
Resize,
|
||||
}
|
||||
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
|
||||
|
||||
Allocator :: struct {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
|
||||
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
||||
}
|
||||
}
|
||||
|
||||
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
if new_size == 0 {
|
||||
free(ptr, allocator, loc);
|
||||
return nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
}
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc);
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc);
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc);
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc);
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m;
|
||||
delete_slice(raw.hashes);
|
||||
free(raw.entries.data, raw.entries.allocator, loc);
|
||||
}
|
||||
|
||||
|
||||
delete :: proc[
|
||||
delete_string,
|
||||
delete_cstring,
|
||||
delete_dynamic_array,
|
||||
delete_slice,
|
||||
delete_map,
|
||||
];
|
||||
|
||||
|
||||
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil do ptr^ = T{};
|
||||
return ptr;
|
||||
}
|
||||
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil do ptr^ = data;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_slice_error_loc(loc, len);
|
||||
data := alloc(size_of(E)*len, align_of(E), allocator, loc);
|
||||
s := Raw_Slice{data, len};
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
|
||||
}
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_dynamic_array_error_loc(loc, len, cap);
|
||||
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap);
|
||||
context.allocator = allocator;
|
||||
|
||||
m: T;
|
||||
reserve_map(&m, cap);
|
||||
return m;
|
||||
}
|
||||
|
||||
make :: proc[
|
||||
make_slice,
|
||||
make_dynamic_array,
|
||||
make_dynamic_array_len,
|
||||
make_dynamic_array_len_cap,
|
||||
make_map,
|
||||
];
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory, allocator, loc);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size do return old_memory;
|
||||
|
||||
new_memory := alloc(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil do return nil;
|
||||
|
||||
copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory, allocator, loc);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
scratch.data = data;
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
scratch := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
if scratch.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
scratch_allocator_init(scratch, make([]byte, 1<<22));
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case Allocator_Mode.Alloc:
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := scratch.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
scratch.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := alloc(size, alignment, a, loc);
|
||||
if scratch.leaked_allocations == nil {
|
||||
scratch.leaked_allocations = make([dynamic]rawptr, a);
|
||||
}
|
||||
append(&scratch.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
|
||||
case Allocator_Mode.Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, size);
|
||||
return nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
case Allocator_Mode.Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
for ptr in scratch.leaked_allocations {
|
||||
free(ptr, scratch.backup_allocator);
|
||||
}
|
||||
clear(&scratch.leaked_allocations);
|
||||
|
||||
case Allocator_Mode.Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
return old_memory;
|
||||
}
|
||||
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = scratch_allocator_proc,
|
||||
data = scratch,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Pool :: struct {
|
||||
block_size: int,
|
||||
out_band_size: int,
|
||||
alignment: int,
|
||||
|
||||
unused_blocks: [dynamic]rawptr,
|
||||
used_blocks: [dynamic]rawptr,
|
||||
out_band_allocations: [dynamic]rawptr,
|
||||
|
||||
current_block: rawptr,
|
||||
current_pos: rawptr,
|
||||
bytes_left: int,
|
||||
|
||||
block_allocator: Allocator,
|
||||
}
|
||||
|
||||
|
||||
POOL_BLOCK_SIZE_DEFAULT :: 65536;
|
||||
POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
|
||||
|
||||
|
||||
|
||||
pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
pool := (^Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case Allocator_Mode.Alloc:
|
||||
return pool_alloc(pool, size);
|
||||
case Allocator_Mode.Free:
|
||||
panic("Allocator_Mode.Free is not supported for a pool");
|
||||
case Allocator_Mode.Free_All:
|
||||
pool_free_all(pool);
|
||||
case Allocator_Mode.Resize:
|
||||
panic("Allocator_Mode.Resize is not supported for a pool");
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
}
|
||||
ptr := pool_alloc(pool, size);
|
||||
copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
pool_allocator :: proc(pool: ^Pool) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = pool_allocator_proc,
|
||||
data = pool,
|
||||
};
|
||||
}
|
||||
|
||||
pool_init :: proc(pool: ^Pool,
|
||||
block_allocator := Allocator{} , array_allocator := Allocator{},
|
||||
block_size := POOL_BLOCK_SIZE_DEFAULT, out_band_size := POOL_OUT_OF_BAND_SIZE_DEFAULT,
|
||||
alignment := 8) {
|
||||
pool.block_size = block_size;
|
||||
pool.out_band_size = out_band_size;
|
||||
pool.alignment = alignment;
|
||||
|
||||
if block_allocator.procedure == nil {
|
||||
block_allocator = context.allocator;
|
||||
}
|
||||
if array_allocator.procedure == nil {
|
||||
array_allocator = context.allocator;
|
||||
}
|
||||
|
||||
pool.block_allocator = block_allocator;
|
||||
|
||||
pool.out_band_allocations.allocator = array_allocator;
|
||||
pool. unused_blocks.allocator = array_allocator;
|
||||
pool. used_blocks.allocator = array_allocator;
|
||||
}
|
||||
|
||||
pool_destroy :: proc(using pool: ^Pool) {
|
||||
pool_free_all(pool);
|
||||
delete(unused_blocks);
|
||||
delete(used_blocks);
|
||||
|
||||
zero(pool, size_of(pool^));
|
||||
}
|
||||
|
||||
|
||||
pool_alloc :: proc(using pool: ^Pool, bytes: int) -> rawptr {
|
||||
cycle_new_block :: proc(using pool: ^Pool) {
|
||||
if block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it");
|
||||
}
|
||||
|
||||
if current_block != nil {
|
||||
append(&used_blocks, current_block);
|
||||
}
|
||||
|
||||
new_block: rawptr;
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks);
|
||||
} else {
|
||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
}
|
||||
|
||||
bytes_left = block_size;
|
||||
current_pos = new_block;
|
||||
current_block = new_block;
|
||||
}
|
||||
|
||||
|
||||
extra := alignment - (bytes % alignment);
|
||||
bytes += extra;
|
||||
if bytes >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, (^byte)(memory));
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
if bytes_left < bytes {
|
||||
cycle_new_block(pool);
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), bytes);
|
||||
bytes_left -= bytes;
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
pool_reset :: proc(using pool: ^Pool) {
|
||||
if current_block != nil {
|
||||
append(&unused_blocks, current_block);
|
||||
current_block = nil;
|
||||
}
|
||||
|
||||
for block in used_blocks {
|
||||
append(&unused_blocks, block);
|
||||
}
|
||||
clear(&used_blocks);
|
||||
|
||||
for a in out_band_allocations {
|
||||
free(a, block_allocator);
|
||||
}
|
||||
clear(&out_band_allocations);
|
||||
}
|
||||
|
||||
pool_free_all :: proc(using pool: ^Pool) {
|
||||
pool_reset(pool);
|
||||
|
||||
for block in unused_blocks {
|
||||
free(block, block_allocator);
|
||||
}
|
||||
clear(&unused_blocks);
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
package mem
|
||||
|
||||
foreign _ {
|
||||
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
|
||||
@(link_name = "llvm.bswap.i32") swap32 :: proc(b: u32) -> u32 ---;
|
||||
@(link_name = "llvm.bswap.i64") swap64 :: proc(b: u64) -> u64 ---;
|
||||
}
|
||||
swap :: proc[swap16, swap32, swap64];
|
||||
|
||||
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
if data == nil do return nil;
|
||||
if len < 0 do return data;
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memset.p0i8.i64")
|
||||
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memset.p0i8.i32")
|
||||
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memset(data, byte(value), len, 1, false);
|
||||
return data;
|
||||
}
|
||||
zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
return set(data, 0, len);
|
||||
}
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) {
|
||||
if n := len(data); n > 0 {
|
||||
zero(&data[0], size_of(E)*n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memmove(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memcpy(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
compare :: proc "contextless" (a, b: []byte) -> int {
|
||||
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int {
|
||||
pa :: ptr_offset;
|
||||
for i in 0..n-1 do switch {
|
||||
case pa(a, i)^ < pa(b, i)^: return -1;
|
||||
case pa(a, i)^ > pa(b, i)^: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
compare_ptrs :: inline proc "contextless" (a, b: rawptr, n: int) -> int {
|
||||
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
|
||||
}
|
||||
|
||||
ptr_offset :: proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
ptr_sub :: proc "contextless" (a, b: $P/^$T) -> int {
|
||||
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
|
||||
}
|
||||
|
||||
slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
assert(len >= 0);
|
||||
slice := Raw_Slice{data = ptr, len = len};
|
||||
return transmute([]T)slice;
|
||||
}
|
||||
|
||||
slice_to_bytes :: proc "contextless" (slice: $E/[]$T) -> []byte {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len *= size_of(T);
|
||||
return transmute([]byte)s;
|
||||
}
|
||||
|
||||
|
||||
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
s := transmute(Raw_Slice)backing;
|
||||
d := Raw_Dynamic_Array{
|
||||
data = s.data,
|
||||
len = 0,
|
||||
cap = s.len,
|
||||
allocator = nil_allocator(),
|
||||
};
|
||||
return transmute([dynamic]E)d;
|
||||
}
|
||||
|
||||
ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
|
||||
}
|
||||
|
||||
any_to_bytes :: proc "contextless" (val: any) -> []byte {
|
||||
ti := type_info_of(val.id);
|
||||
size := ti != nil ? ti.size : 0;
|
||||
return transmute([]byte)Raw_Slice{val.data, size};
|
||||
}
|
||||
|
||||
|
||||
kilobytes :: inline proc "contextless" (x: int) -> int do return (x) * 1024;
|
||||
megabytes :: inline proc "contextless" (x: int) -> int do return kilobytes(x) * 1024;
|
||||
gigabytes :: inline proc "contextless" (x: int) -> int do return megabytes(x) * 1024;
|
||||
terabytes :: inline proc "contextless" (x: int) -> int do return gigabytes(x) * 1024;
|
||||
|
||||
is_power_of_two :: proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uintptr(align);
|
||||
p := uintptr(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uintptr(align);
|
||||
p := uintptr(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return uintptr(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AllocationHeader :: struct {size: int};
|
||||
|
||||
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := cast(^uint)(ptr_offset(header, 1));
|
||||
n := ptr_sub(cast(^uint)data, ptr);
|
||||
|
||||
for i in 0..n-1 {
|
||||
ptr_offset(ptr, i)^ = ~uint(0);
|
||||
}
|
||||
}
|
||||
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||
if data == nil do return nil;
|
||||
p := cast(^uint)data;
|
||||
for ptr_offset(p, -1)^ == ~uint(0) do p = ptr_offset(p, -1);
|
||||
return (^AllocationHeader)(ptr_offset(p, -1));
|
||||
}
|
||||
|
||||
|
||||
Fixed_Byte_Buffer :: distinct [dynamic]byte;
|
||||
|
||||
make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
|
||||
s := transmute(Raw_Slice)backing;
|
||||
d: Raw_Dynamic_Array;
|
||||
d.data = s.data;
|
||||
d.len = 0;
|
||||
d.cap = s.len;
|
||||
d.allocator = nil_allocator();
|
||||
return transmute(Fixed_Byte_Buffer)d;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Custom allocators
|
||||
|
||||
Arena :: struct {
|
||||
backing: Allocator,
|
||||
memory: Fixed_Byte_Buffer,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena,
|
||||
original_count: int,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
||||
backing = Allocator{};
|
||||
memory = make_fixed_byte_buffer(data);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator;
|
||||
memory = make_fixed_byte_buffer(make([]byte, size));
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
|
||||
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
|
||||
context.allocator = a;
|
||||
return context;
|
||||
}
|
||||
|
||||
destroy_arena :: proc(using a: ^Arena) {
|
||||
if backing.procedure != nil {
|
||||
context.allocator = backing;
|
||||
if memory != nil {
|
||||
free(&memory[0]);
|
||||
}
|
||||
memory = nil;
|
||||
}
|
||||
}
|
||||
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = arena_allocator_proc,
|
||||
data = arena,
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
arena := cast(^Arena)allocator_data;
|
||||
|
||||
|
||||
switch mode {
|
||||
case Alloc:
|
||||
total_size := size + alignment;
|
||||
|
||||
if len(arena.memory) + total_size > cap(arena.memory) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.memory[len(arena.memory)];
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
(^Raw_Slice)(&arena.memory).len += total_size;
|
||||
return zero(ptr, size);
|
||||
|
||||
case Free:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
|
||||
case Free_All:
|
||||
(^Raw_Slice)(&arena.memory).len = 0;
|
||||
|
||||
case Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
(^Raw_Dynamic_Array)(&arena.memory).len = original_count;
|
||||
arena.temp_count -= 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
align_formula :: proc(size, align: int) -> int {
|
||||
result := size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package mem
|
||||
|
||||
Raw_Any :: struct {
|
||||
data: rawptr,
|
||||
id: typeid,
|
||||
}
|
||||
|
||||
Raw_String :: struct {
|
||||
data: ^byte,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Cstring :: struct {
|
||||
data: ^byte,
|
||||
}
|
||||
|
||||
Raw_Slice :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Dynamic_Array :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
Raw_Map :: struct {
|
||||
hashes: []int,
|
||||
entries: Raw_Dynamic_Array,
|
||||
}
|
||||
|
||||
|
||||
make_any :: inline proc(data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id};
|
||||
}
|
||||
|
||||
raw_string_data :: inline proc(s: $T/string) -> ^byte {
|
||||
return (^Raw_String)(&s).data;
|
||||
}
|
||||
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(^Raw_Slice)(&a).data;
|
||||
}
|
||||
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(^Raw_Dynamic_Array)(&a).data;
|
||||
}
|
||||
|
||||
raw_data :: proc[raw_string_data, raw_slice_data, raw_dynamic_array_data];
|
||||
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library lib "gl" when ODIN_OS == "linux";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#load "opengl_constants.odin";
|
||||
|
||||
Clear :: proc(mask: u32) #foreign lib "glClear";
|
||||
ClearColor :: proc(r, g, b, a: f32) #foreign lib "glClearColor";
|
||||
Begin :: proc(mode: i32) #foreign lib "glBegin";
|
||||
End :: proc() #foreign lib "glEnd";
|
||||
Finish :: proc() #foreign lib "glFinish";
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign lib "glBlendFunc";
|
||||
Enable :: proc(cap: i32) #foreign lib "glEnable";
|
||||
Disable :: proc(cap: i32) #foreign lib "glDisable";
|
||||
GenTextures :: proc(count: i32, result: ^u32) #foreign lib "glGenTextures";
|
||||
DeleteTextures:: proc(count: i32, result: ^u32) #foreign lib "glDeleteTextures";
|
||||
TexParameteri :: proc(target, pname, param: i32) #foreign lib "glTexParameteri";
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign lib "glTexParameterf";
|
||||
BindTexture :: proc(target: i32, texture: u32) #foreign lib "glBindTexture";
|
||||
LoadIdentity :: proc() #foreign lib "glLoadIdentity";
|
||||
Viewport :: proc(x, y, width, height: i32) #foreign lib "glViewport";
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho";
|
||||
Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f";
|
||||
Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f";
|
||||
GetError :: proc() -> i32 #foreign lib "glGetError";
|
||||
GetString :: proc(name: i32) -> ^u8 #foreign lib "glGetString";
|
||||
GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv";
|
||||
TexCoord2f :: proc(x, y: f32) #foreign lib "glTexCoord2f";
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
|
||||
|
||||
|
||||
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
|
||||
|
||||
_libgl := win32.load_library_a(_string_data("opengl32.dll\x00"));
|
||||
|
||||
get_proc_address :: proc(name: string) -> proc() #cc_c {
|
||||
if name[len(name)-1] == 0 {
|
||||
name = name[0..<len(name)-1];
|
||||
}
|
||||
// NOTE(bill): null terminated
|
||||
assert((&name[0] + len(name))^ == 0);
|
||||
res := wgl.get_proc_address(&name[0]);
|
||||
if res == nil {
|
||||
res = win32.get_proc_address(_libgl, &name[0]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
|
||||
BindVertexArray: proc(buffer: u32) #cc_c;
|
||||
DeleteVertexArrays: proc(count: i32, arrays: ^u32) #cc_c;
|
||||
BindSampler: proc(position: i32, sampler: u32) #cc_c;
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
|
||||
|
||||
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||
|
||||
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||
UnmapBuffer: proc(target: i32) #cc_c;
|
||||
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||
|
||||
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
|
||||
ShaderSource: proc(shader: u32, count: u32, str: ^^u8, length: ^i32) #cc_c;
|
||||
CompileShader: proc(shader: u32) #cc_c;
|
||||
CreateProgram: proc() -> u32 #cc_c;
|
||||
AttachShader: proc(program, shader: u32) #cc_c;
|
||||
DetachShader: proc(program, shader: u32) #cc_c;
|
||||
DeleteShader: proc(shader: u32) #cc_c;
|
||||
LinkProgram: proc(program: u32) #cc_c;
|
||||
UseProgram: proc(program: u32) #cc_c;
|
||||
DeleteProgram: proc(program: u32) #cc_c;
|
||||
|
||||
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
|
||||
|
||||
ActiveTexture: proc(texture: i32) #cc_c;
|
||||
GenerateMipmap: proc(target: i32) #cc_c;
|
||||
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||
|
||||
|
||||
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||
|
||||
GetUniformLocation: proc(program: u32, name: ^u8) -> i32 #cc_c;
|
||||
|
||||
init :: proc() {
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline {
|
||||
x := ^(proc() #cc_c)(p);
|
||||
x^ = get_proc_address(name);
|
||||
}
|
||||
|
||||
set_proc_address(&GenBuffers, "glGenBuffers\x00");
|
||||
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
|
||||
set_proc_address(&GenSamplers, "glGenSamplers\x00");
|
||||
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
|
||||
set_proc_address(&BindBuffer, "glBindBuffer\x00");
|
||||
set_proc_address(&BindSampler, "glBindSampler\x00");
|
||||
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
|
||||
set_proc_address(&DeleteVertexArrays, "glDeleteVertexArrays\x00");
|
||||
set_proc_address(&BufferData, "glBufferData\x00");
|
||||
set_proc_address(&BufferSubData, "glBufferSubData\x00");
|
||||
|
||||
set_proc_address(&DrawArrays, "glDrawArrays\x00");
|
||||
set_proc_address(&DrawElements, "glDrawElements\x00");
|
||||
|
||||
set_proc_address(&MapBuffer, "glMapBuffer\x00");
|
||||
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
|
||||
|
||||
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
|
||||
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
|
||||
|
||||
set_proc_address(&CreateShader, "glCreateShader\x00");
|
||||
set_proc_address(&ShaderSource, "glShaderSource\x00");
|
||||
set_proc_address(&CompileShader, "glCompileShader\x00");
|
||||
set_proc_address(&CreateProgram, "glCreateProgram\x00");
|
||||
set_proc_address(&AttachShader, "glAttachShader\x00");
|
||||
set_proc_address(&DetachShader, "glDetachShader\x00");
|
||||
set_proc_address(&DeleteShader, "glDeleteShader\x00");
|
||||
set_proc_address(&LinkProgram, "glLinkProgram\x00");
|
||||
set_proc_address(&UseProgram, "glUseProgram\x00");
|
||||
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
|
||||
|
||||
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
|
||||
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
|
||||
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
|
||||
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
|
||||
|
||||
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
|
||||
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
|
||||
|
||||
set_proc_address(&Uniform1i, "glUniform1i\x00");
|
||||
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
|
||||
|
||||
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
|
||||
|
||||
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
|
||||
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
|
||||
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
|
||||
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
|
||||
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
|
||||
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
#load "os_windows.odin" when ODIN_OS == "windows";
|
||||
#load "os_x.odin" when ODIN_OS == "osx";
|
||||
#load "os_linux.odin" when ODIN_OS == "linux";
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, []u8(str));
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]u8, bool) {
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data := make([]u8, length);
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data);
|
||||
if read_err != 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
return data[0..<bytes_read], true;
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []u8) -> bool {
|
||||
fd, err := open(name, O_WRONLY, 0);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
bytes_written, write_err := write(fd, data);
|
||||
return write_err != 0;
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
package os
|
||||
|
||||
import "core:mem"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, cast([]byte)str);
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
||||
return write(fd, []byte{b});
|
||||
}
|
||||
|
||||
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
return write_byte(fd, byte(r));
|
||||
}
|
||||
|
||||
b, n := utf8.encode_rune(r);
|
||||
return write(fd, b[:n]);
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) {
|
||||
write_byte(fd, '\'');
|
||||
|
||||
switch r {
|
||||
case '\a': write_string(fd, "\\a");
|
||||
case '\b': write_string(fd, "\\b");
|
||||
case '\e': write_string(fd, "\\e");
|
||||
case '\f': write_string(fd, "\\f");
|
||||
case '\n': write_string(fd, "\\n");
|
||||
case '\r': write_string(fd, "\\r");
|
||||
case '\t': write_string(fd, "\\t");
|
||||
case '\v': write_string(fd, "\\v");
|
||||
case:
|
||||
if r < 32 {
|
||||
write_string(fd, "\\x");
|
||||
b: [2]byte;
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil);
|
||||
switch len(s) {
|
||||
case 0: write_string(fd, "00");
|
||||
case 1: write_rune(fd, '0');
|
||||
case 2: write_string(fd, s);
|
||||
}
|
||||
} else {
|
||||
write_rune(fd, r);
|
||||
}
|
||||
}
|
||||
write_byte(fd, '\'');
|
||||
}
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data = make([]byte, int(length));
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data);
|
||||
if read_err != 0 {
|
||||
delete(data);
|
||||
return nil, false;
|
||||
}
|
||||
return data[0:bytes_read], true;
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
|
||||
flags: int = O_WRONLY|O_CREATE;
|
||||
if truncate {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
fd, err := open(name, flags, 0);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
_, write_err := write(fd, data);
|
||||
return write_err == 0;
|
||||
}
|
||||
|
||||
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return write(fd, s);
|
||||
}
|
||||
|
||||
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return read(fd, s);
|
||||
}
|
||||
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
using mem.Allocator_Mode;
|
||||
|
||||
switch mode {
|
||||
case Alloc:
|
||||
return heap_alloc(size);
|
||||
|
||||
case Free:
|
||||
heap_free(old_memory);
|
||||
return nil;
|
||||
|
||||
case Free_All:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case Resize:
|
||||
if old_memory == nil {
|
||||
return heap_alloc(size);
|
||||
}
|
||||
ptr := heap_resize(old_memory, size);
|
||||
assert(ptr != nil);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
heap_allocator :: proc() -> mem.Allocator {
|
||||
return mem.Allocator{
|
||||
procedure = heap_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package os;
|
||||
|
||||
ARCH :: "x86";
|
||||
ENDIAN :: "little";
|
||||
@@ -0,0 +1,4 @@
|
||||
package os;
|
||||
|
||||
ARCH :: "amd64";
|
||||
ENDIAN :: "little";
|
||||
@@ -0,0 +1,176 @@
|
||||
package os
|
||||
|
||||
OS :: "essence";
|
||||
|
||||
foreign import api "system:api"
|
||||
|
||||
Handle :: distinct int;
|
||||
Errno :: distinct int;
|
||||
|
||||
O_RDONLY :: 0x00001;
|
||||
O_WRONLY :: 0x00002;
|
||||
O_RDWR :: 0x00003;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_APPEND :: 0x00400;
|
||||
|
||||
ERROR_NONE :: Errno(-1);
|
||||
ERROR_UNKNOWN_OPERATION_FAILURE :: Errno(-7);
|
||||
ERROR_PATH_NOT_WITHIN_MOUNTED_VOLUME :: Errno(-14);
|
||||
ERROR_PATH_NOT_FOUND :: Errno(-15);
|
||||
ERROR_FILE_EXISTS :: Errno(-19);
|
||||
ERROR_FILE_NOT_FOUND :: Errno(-20);
|
||||
ERROR_DRIVE_ERROR_FILE_DAMAGED :: Errno(-21);
|
||||
ERROR_ACCESS_NOT_WITHIN_FILE_BOUNDS :: Errno(-22);
|
||||
ERROR_ACCESS_DENIED :: Errno(-23);
|
||||
ERROR_FILE_IN_EXCLUSIVE_USE :: Errno(-24);
|
||||
ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE :: Errno(-25);
|
||||
ERROR_INCORRECT_NODE_TYPE :: Errno(-26);
|
||||
ERROR_EVENT_NOT_SET :: Errno(-27);
|
||||
ERROR_TIMEOUT_REACHED :: Errno(-29);
|
||||
ERROR_REQUEST_CLOSED_BEFORE_COMPLETE :: Errno(-30);
|
||||
ERROR_NO_CHARACTER_AT_COORDINATE :: Errno(-31);
|
||||
ERROR_FILE_ON_READ_ONLY_VOLUME :: Errno(-32);
|
||||
ERROR_USER_CANCELED_IO :: Errno(-33);
|
||||
ERROR_DRIVE_CONTROLLER_REPORTED :: Errno(-35);
|
||||
ERROR_COULD_NOT_ISSUE_PACKET :: Errno(-36);
|
||||
|
||||
ERROR_NOT_IMPLEMENTED :: Errno(1);
|
||||
|
||||
OS_Node_Type :: enum i32 {
|
||||
File = 0,
|
||||
Directory = 1,
|
||||
}
|
||||
|
||||
OS_Node_Information :: struct {
|
||||
handle: Handle,
|
||||
id: [16]byte,
|
||||
ntype: OS_Node_Type,
|
||||
size: i64,
|
||||
|
||||
// Our additions..
|
||||
position: i64,
|
||||
}
|
||||
|
||||
foreign api {
|
||||
@(link_name="OSPrintDirect") OSPrintDirect :: proc(str: ^u8, length: int) ---;
|
||||
@(link_name="malloc") OSMalloc :: proc(bytes: int) -> rawptr ---;
|
||||
@(link_name="free") OSFree :: proc(address: rawptr) ---;
|
||||
@(link_name="OSOpenNode") OSOpenNode :: proc(path: ^u8, path_length: int, flags: u64, information: ^OS_Node_Information) -> Errno ---;
|
||||
@(link_name="OSResizeFile") OSResizeFile :: proc(handle: Handle, new_size: u64) -> Errno ---;
|
||||
@(link_name="OSCloseHandle") OSCloseHandle :: proc(handle: Handle) ---;
|
||||
@(link_name="OSWriteFileSync") OSWriteFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---;
|
||||
@(link_name="OSReadFileSync") OSReadFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---;
|
||||
@(link_name="realloc") OSRealloc :: proc(address: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="OSGetThreadID") OSGetThreadID :: proc(handle: Handle) -> int ---;
|
||||
@(link_name="OSRefreshNodeInformation") OSRefreshNodeInformation :: proc(information: ^OS_Node_Information) ---;
|
||||
}
|
||||
|
||||
stdin := Handle(-1); // Not implemented
|
||||
stdout := Handle(0);
|
||||
stderr := Handle(0);
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return OSGetThreadID(Handle(0x1000));
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return OSMalloc(size);
|
||||
}
|
||||
|
||||
heap_free :: proc(address: rawptr) {
|
||||
OSFree(address);
|
||||
}
|
||||
|
||||
heap_resize :: proc(address: rawptr, new_size: int) -> rawptr {
|
||||
return OSRealloc(address, new_size);
|
||||
}
|
||||
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
flags : u64 = 0;
|
||||
|
||||
if mode & O_CREATE == O_CREATE {
|
||||
flags = flags | 0x9000; // Fail if found and create directories leading to the file if they don't exist
|
||||
} else {
|
||||
flags = flags | 0x2000; // Fail if not found
|
||||
}
|
||||
|
||||
if mode & O_EXCL == O_EXCL {
|
||||
flags = flags | 0x111; // Block opening the node for any reason
|
||||
}
|
||||
|
||||
if mode & O_RDONLY == O_RDONLY {
|
||||
flags = flags | 0x2; // Read access
|
||||
}
|
||||
|
||||
if mode & O_WRONLY == O_WRONLY {
|
||||
flags = flags | 0x220; // Write and resize access
|
||||
}
|
||||
|
||||
if mode & O_TRUNC == O_TRUNC {
|
||||
flags = flags | 0x200; // Resize access
|
||||
}
|
||||
|
||||
information := new(OS_Node_Information);
|
||||
error := OSOpenNode(&path[0], len(path), flags, information);
|
||||
|
||||
if error < ERROR_NONE {
|
||||
free(information);
|
||||
return 0, error;
|
||||
}
|
||||
|
||||
if mode & O_TRUNC == O_TRUNC {
|
||||
error := OSResizeFile(information.handle, 0);
|
||||
if error < ERROR_NONE do return 0, ERROR_UNKNOWN_OPERATION_FAILURE;
|
||||
}
|
||||
|
||||
if mode & O_APPEND == O_APPEND {
|
||||
information.position = information.size;
|
||||
} else {
|
||||
information.position = 0;
|
||||
}
|
||||
|
||||
return Handle(uintptr(information)), ERROR_NONE;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
information := (^OS_Node_Information)(uintptr(fd));
|
||||
OSCloseHandle(information.handle);
|
||||
free(information);
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
x: OS_Node_Information;
|
||||
OSRefreshNodeInformation(&x);
|
||||
return x.size, ERROR_NONE;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if fd == 0 {
|
||||
OSPrintDirect(&data[0], len(data));
|
||||
return len(data), ERROR_NONE;
|
||||
} else if fd == 1 {
|
||||
assert(false);
|
||||
return 0, ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
information := (^OS_Node_Information)(uintptr(fd));
|
||||
count := OSWriteFileSync(information.handle, information.position, i64(len(data)), &data[0]);
|
||||
if count < 0 do return 0, 1;
|
||||
information.position += count;
|
||||
return int(count), 0;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if (fd == 0 || fd == 1) {
|
||||
assert(false);
|
||||
return 0, ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
information := (^OS_Node_Information)(uintptr(fd));
|
||||
count := OSReadFileSync(information.handle, information.position, i64(len(data)), &data[0]);
|
||||
if count < 0 do return 0, ERROR_UNKNOWN_OPERATION_FAILURE;
|
||||
information.position += count;
|
||||
return int(count), ERROR_NONE;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package os
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
OS :: "linux";
|
||||
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct i32;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001;
|
||||
RTLD_NOW :: 0x002;
|
||||
RTLD_BINDING_MASK :: 0x3;
|
||||
RTLD_GLOBAL :: 0x100;
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i32,
|
||||
reserved: i32,
|
||||
}
|
||||
|
||||
// Translated from
|
||||
// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h
|
||||
// Validity is not guaranteed.
|
||||
|
||||
Stat :: struct {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u32, // 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: _File_Time, // Time of last access
|
||||
modified: _File_Time, // Time of last modification
|
||||
status_change: _File_Time, // Time of last status change
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
serial_numbe: u64, // File serial number..? Maybe.
|
||||
_reserve4: i64,
|
||||
};
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
|
||||
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
|
||||
S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
|
||||
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
|
||||
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
|
||||
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
|
||||
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
|
||||
S_ISSOCK :: inline proc(m: u32) -> bool do 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
|
||||
|
||||
foreign libc {
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> i32 ---;
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> i32 ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> i32 ---;
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
|
||||
}
|
||||
foreign dl {
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||
}
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
cstr := strings.new_cstring(path);
|
||||
handle := _unix_open(cstr, mode);
|
||||
delete(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_read(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_write(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, i32(whence));
|
||||
return res, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return size, err;
|
||||
}
|
||||
|
||||
|
||||
// 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 {}
|
||||
*/
|
||||
|
||||
stat :: inline proc(path: string) -> (Stat, int) {
|
||||
cstr := strings.new_cstring(path);
|
||||
defer delete(cstr);
|
||||
|
||||
s: Stat;
|
||||
ret_int := _unix_stat(cstr, &s);
|
||||
return s, int(ret_int);
|
||||
}
|
||||
|
||||
access :: inline proc(path: string, mask: int) -> bool {
|
||||
cstr := strings.new_cstring(path);
|
||||
defer delete(cstr);
|
||||
return _unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0);
|
||||
return _unix_calloc(1, size);
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_cstring(name);
|
||||
defer delete(path_str);
|
||||
cstr := _unix_getenv(path_str);
|
||||
if cstr == nil {
|
||||
return "", false;
|
||||
}
|
||||
return string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||
cstr := strings.new_cstring(filename);
|
||||
defer delete(cstr);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_cstring(symbol);
|
||||
defer delete(cstr);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: inline proc(handle: rawptr) -> bool {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
args := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
args[i] = string(arg);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package os
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
OS :: "osx";
|
||||
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct int;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
Stat :: struct {
|
||||
device_id: i32, // ID of device containing file
|
||||
mode: u16, // Mode of the file
|
||||
nlink: u16, // Number of hard links
|
||||
serial: u64, // File serial number
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
rdev: i32, // Device ID, if device
|
||||
|
||||
last_access: File_Time, // Time of last access
|
||||
modified: File_Time, // Time of last modification
|
||||
status_change: File_Time, // Time of last status change
|
||||
created: File_Time, // Time of creation
|
||||
|
||||
size: i64, // Size of the file, in bytes
|
||||
blocks: i64, // Number of blocks allocated for the file
|
||||
block_size: i32, // Optimal blocksize for I/O
|
||||
flags: u32, // User-defined flags for the file
|
||||
gen_num: u32, // File generation number ..?
|
||||
_spare: i32, // RESERVED
|
||||
_reserve1,
|
||||
_reserve2: i64, // RESERVED
|
||||
};
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
|
||||
S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
|
||||
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
|
||||
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
|
||||
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
|
||||
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
|
||||
S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
foreign libc {
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
|
||||
@(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 ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) ---;
|
||||
}
|
||||
|
||||
foreign dl {
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||
}
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
cstr := strings.new_cstring(path);
|
||||
defer delete(cstr);
|
||||
handle := _unix_open(cstr, mode);
|
||||
if handle == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_written := _unix_write(fd, &data[0], len(data));
|
||||
if(bytes_written == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_written, 0;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_read := _unix_read(fd, &data[0], len(data));
|
||||
if bytes_read == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_read, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
final_offset := i64(_unix_lseek(fd, int(offset), whence));
|
||||
if final_offset == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return i64(size), err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
|
||||
stat :: inline proc(path: string) -> (Stat, bool) {
|
||||
s: Stat;
|
||||
cstr := strings.new_cstring(path);
|
||||
defer delete(cstr);
|
||||
ret_int := _unix_stat(cstr, &s);
|
||||
return s, ret_int==0;
|
||||
}
|
||||
|
||||
access :: inline proc(path: string, mask: int) -> bool {
|
||||
cstr := strings.new_cstring(path);
|
||||
defer delete(cstr);
|
||||
return _unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
heap_alloc :: inline proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return _unix_calloc(1, size);
|
||||
}
|
||||
heap_resize :: inline proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
heap_free :: inline proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_cstring(name);
|
||||
defer delete(path_str);
|
||||
cstr := _unix_getenv(path_str);
|
||||
if cstr == nil {
|
||||
return "", false;
|
||||
}
|
||||
return string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: inline proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||
cstr := strings.new_cstring(filename);
|
||||
defer delete(cstr);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_cstring(symbol);
|
||||
defer delete(cstr);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: inline proc(handle: rawptr) -> bool {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
args := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
args[i] = string(arg);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
// +build windows
|
||||
package os
|
||||
|
||||
import "core:sys/win32"
|
||||
|
||||
OS :: "windows";
|
||||
|
||||
Handle :: distinct uintptr;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct int;
|
||||
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_NO_MORE_FILES: Errno : 18;
|
||||
ERROR_HANDLE_EOF: Errno : 38;
|
||||
ERROR_NETNAME_DELETED: Errno : 64;
|
||||
ERROR_FILE_EXISTS: Errno : 80;
|
||||
ERROR_BROKEN_PIPE: Errno : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
ERROR_MORE_DATA: Errno : 234;
|
||||
ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
ERROR_IO_PENDING: Errno : 997;
|
||||
ERROR_NOT_FOUND: Errno : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
WSAEACCES: Errno : 10013;
|
||||
WSAECONNRESET: Errno : 10054;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
|
||||
access: u32;
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
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;
|
||||
}
|
||||
|
||||
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
|
||||
sa: ^win32.Security_Attributes = nil;
|
||||
sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = true};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
switch {
|
||||
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREATE == O_CREATE:
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path);
|
||||
handle := Handle(win32.create_file_w(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE do return handle, ERROR_NONE;
|
||||
|
||||
err := Errno(win32.get_last_error());
|
||||
return INVALID_HANDLE, err;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.close_handle(win32.Handle(fd));
|
||||
}
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_write_length: i32;
|
||||
total_write: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write;
|
||||
MAX :: 1<<31-1;
|
||||
to_write: i32 = min(i32(remaining), MAX);
|
||||
|
||||
e := win32.write_file(win32.Handle(fd), &data[total_write], to_write, &single_write_length, nil);
|
||||
if single_write_length <= 0 || !e {
|
||||
err := Errno(win32.get_last_error());
|
||||
return int(total_write), err;
|
||||
}
|
||||
total_write += i64(single_write_length);
|
||||
}
|
||||
return int(total_write), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
MAX :: 1<<32-1;
|
||||
to_read: u32 = min(u32(remaining), MAX);
|
||||
|
||||
e := win32.read_file(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.get_last_error());
|
||||
return int(total_read), err;
|
||||
}
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
return int(total_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
w: u32;
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN;
|
||||
case 1: w = win32.FILE_CURRENT;
|
||||
case 2: w = win32.FILE_END;
|
||||
}
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.get_file_type(win32.Handle(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE do return 0, ERROR_FILE_IS_PIPE;
|
||||
|
||||
dw_ptr := win32.set_file_pointer(win32.Handle(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.get_last_error());
|
||||
return 0, err;
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
length: i64;
|
||||
err: Errno;
|
||||
if !win32.get_file_size_ex(win32.Handle(fd), &length) {
|
||||
err = Errno(win32.get_last_error());
|
||||
}
|
||||
return length, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr := get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.get_std_handle(i32(h));
|
||||
win32.set_handle_information(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {
|
||||
file_info: win32.By_Handle_File_Information;
|
||||
win32.get_file_information_by_handle(win32.Handle(fd), &file_info);
|
||||
lo := File_Time(file_info.last_write_time.lo);
|
||||
hi := File_Time(file_info.last_write_time.hi);
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
||||
last_write_time: win32.Filetime;
|
||||
data: win32.File_Attribute_Data;
|
||||
|
||||
wide_path := win32.utf8_to_wstring(name);
|
||||
if win32.get_file_attributes_ex_w(wide_path, win32.GetFileExInfoStandard, &data) {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
l := File_Time(last_write_time.lo);
|
||||
h := File_Time(last_write_time.hi);
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.heap_alloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil do return heap_alloc(new_size);
|
||||
|
||||
return win32.heap_realloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil do return;
|
||||
win32.heap_free(win32.get_process_heap(), 0, ptr);
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
win32.exit_process(u32(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return int(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.command_line_to_argv_w(win32.get_command_line_w(), &arg_count);
|
||||
arg_list := make([]string, int(arg_count));
|
||||
for _, i in arg_list {
|
||||
wc_str := (^win32.Wstring)(uintptr(arg_list_ptr) + size_of(win32.Wstring)*uintptr(i))^;
|
||||
olen := win32.wide_char_to_multi_byte(win32.CP_UTF8, 0, wc_str, -1,
|
||||
nil, 0, nil, nil);
|
||||
|
||||
buf := make([]byte, int(olen));
|
||||
n := win32.wide_char_to_multi_byte(win32.CP_UTF8, 0, wc_str, -1,
|
||||
cstring(&buf[0]), olen, nil, nil);
|
||||
if n > 0 {
|
||||
n -= 1;
|
||||
}
|
||||
arg_list[i] = string(buf[:n]);
|
||||
}
|
||||
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
// #import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
FileTime :: u64;
|
||||
Errno :: i32;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 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;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001;
|
||||
RTLD_NOW :: 0x002;
|
||||
RTLD_BINDING_MASK :: 0x3;
|
||||
RTLD_GLOBAL :: 0x100;
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
_FileTime :: struct #ordered {
|
||||
seconds: i64,
|
||||
nanoseconds: i32,
|
||||
reserved: i32,
|
||||
}
|
||||
|
||||
// Translated from
|
||||
// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h
|
||||
// Validity is not guaranteed.
|
||||
|
||||
Stat :: struct #ordered {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u32, // 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: _FileTime, // Time of last access
|
||||
modified: _FileTime, // Time of last modification
|
||||
status_change: _FileTime, // Time of last status change
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
serial_numbe: u64, // File serial number...? Maybe.
|
||||
_reserve4: i64,
|
||||
};
|
||||
|
||||
// File type
|
||||
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
|
||||
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
_unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
_unix_close :: proc(fd: Handle) -> i32 #foreign libc "close";
|
||||
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "read";
|
||||
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "write";
|
||||
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #foreign libc "lseek64";
|
||||
_unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #foreign libc "stat";
|
||||
_unix_access :: proc(path: ^u8, mask: int) -> i32 #foreign libc "access";
|
||||
|
||||
_unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
_unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
_unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
_unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
_unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
_unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := _unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
sz := _unix_read(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
sz := _unix_write(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, i32(whence));
|
||||
return res, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return size, err;
|
||||
}
|
||||
|
||||
|
||||
// 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) -> FileTime {}
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {}
|
||||
*/
|
||||
|
||||
stat :: proc(path: string) -> (Stat, int) #inline {
|
||||
s: Stat;
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
ret_int := _unix_stat(cstr, &s);
|
||||
return s, int(ret_int);
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool #inline {
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
return _unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// read_entire_file :: proc(name: string) -> ([]u8, bool) {
|
||||
// fd: Handle;
|
||||
// err: Errno;
|
||||
// size: i64;
|
||||
|
||||
// fd, err = open_simple(name, O_RDONLY);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to open file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
// defer close(fd);
|
||||
|
||||
// // We have a file
|
||||
// size, err = seek(fd, 0, SEEK_END);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to seek to end of file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// _, err = seek(fd, 0, SEEK_SET);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to seek to beginning of file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// // We have a file size!
|
||||
|
||||
// data := make([]u8, size+1);
|
||||
// if data == nil {
|
||||
// fmt.println("Failed to allocate file buffer.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// read(fd, data);
|
||||
// data[size] = 0;
|
||||
|
||||
// return data, true;
|
||||
// }
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return _unix_malloc(size);
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = _unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(_unix_dlerror());
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
// TODO(bill):
|
||||
return nil;
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
|
||||
Handle :: int;
|
||||
FileTime :: u64;
|
||||
Errno :: int;
|
||||
|
||||
INVALID_HANDLE: Handle : -1;
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_NO_MORE_FILES: Errno : 18;
|
||||
ERROR_HANDLE_EOF: Errno : 38;
|
||||
ERROR_NETNAME_DELETED: Errno : 64;
|
||||
ERROR_FILE_EXISTS: Errno : 80;
|
||||
ERROR_BROKEN_PIPE: Errno : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
ERROR_MORE_DATA: Errno : 234;
|
||||
ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
ERROR_IO_PENDING: Errno : 997;
|
||||
ERROR_NOT_FOUND: Errno : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
WSAEACCES: Errno : 10013;
|
||||
WSAECONNRESET: Errno : 10054;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
access: u32;
|
||||
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
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 = nil;
|
||||
sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
match {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
|
||||
buf: [300]u8;
|
||||
copy(buf[..], []u8(path));
|
||||
|
||||
handle := Handle(win32.create_file_a(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
err := win32.get_last_error();
|
||||
return INVALID_HANDLE, Errno(err);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.close_handle(win32.Handle(fd));
|
||||
}
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
single_write_length: i32;
|
||||
total_write: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write;
|
||||
to_read: i32;
|
||||
MAX :: 1<<31-1;
|
||||
if remaining <= MAX {
|
||||
to_read = i32(remaining);
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
e := win32.write_file(win32.Handle(fd), &data[total_write], to_read, &single_write_length, nil);
|
||||
if single_write_length <= 0 || e == win32.FALSE {
|
||||
err := win32.get_last_error();
|
||||
return int(total_write), Errno(e);
|
||||
}
|
||||
total_write += i64(single_write_length);
|
||||
}
|
||||
return int(total_write), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = u32(remaining);
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
e := win32.read_file(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 || e == win32.FALSE {
|
||||
err := win32.get_last_error();
|
||||
return int(total_read), Errno(e);
|
||||
}
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
return int(total_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
w: u32;
|
||||
match whence {
|
||||
case 0: w = win32.FILE_BEGIN;
|
||||
case 1: w = win32.FILE_CURRENT;
|
||||
case 2: w = win32.FILE_END;
|
||||
}
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.get_file_type(win32.Handle(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
dw_ptr := win32.set_file_pointer(win32.Handle(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := win32.get_last_error();
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
length: i64;
|
||||
err: Errno;
|
||||
if win32.get_file_size_ex(win32.Handle(fd), &length) == 0 {
|
||||
err = Errno(win32.get_last_error());
|
||||
}
|
||||
return length, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr := get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.get_std_handle(i32(h));
|
||||
win32.set_handle_information(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> FileTime {
|
||||
file_info: win32.ByHandleFileInformation;
|
||||
win32.get_file_information_by_handle(win32.Handle(fd), &file_info);
|
||||
lo := FileTime(file_info.last_write_time.lo);
|
||||
hi := FileTime(file_info.last_write_time.hi);
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {
|
||||
last_write_time: win32.Filetime;
|
||||
data: win32.FileAttributeData;
|
||||
buf: [1024]u8;
|
||||
|
||||
assert(len(buf) > len(name));
|
||||
|
||||
copy(buf[..], []u8(name));
|
||||
|
||||
if win32.get_file_attributes_ex_a(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
l := FileTime(last_write_time.lo);
|
||||
h := FileTime(last_write_time.hi);
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.heap_alloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size);
|
||||
}
|
||||
return win32.heap_realloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
win32.heap_free(win32.get_process_heap(), 0, ptr);
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.exit_process(u32(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
return int(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
|
||||
wstr_len := 0;
|
||||
for (wstr+wstr_len)^ != 0 {
|
||||
wstr_len++;
|
||||
}
|
||||
len := 2*wstr_len-1;
|
||||
buf := make([]u8, len+1);
|
||||
str := slice_ptr(wstr, wstr_len+1);
|
||||
|
||||
i, j := 0, 0;
|
||||
for str[j] != 0 {
|
||||
match {
|
||||
case str[j] < 0x80:
|
||||
if i+1 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = u8(str[j]); i++;
|
||||
j++;
|
||||
case str[j] < 0x800:
|
||||
if i+2 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = u8(0xc0 + (str[j]>>6)); i++;
|
||||
buf[i] = u8(0x80 + (str[j]&0x3f)); i++;
|
||||
j++;
|
||||
case 0xd800 <= str[j] && str[j] < 0xdc00:
|
||||
if i+4 > len {
|
||||
return "";
|
||||
}
|
||||
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
|
||||
buf[i] = u8(0xf0 + (c >> 18)); i++;
|
||||
buf[i] = u8(0x80 + ((c >> 12) & 0x3f)); i++;
|
||||
buf[i] = u8(0x80 + ((c >> 6) & 0x3f)); i++;
|
||||
buf[i] = u8(0x80 + ((c ) & 0x3f)); i++;
|
||||
j += 2;
|
||||
case 0xdc00 <= str[j] && str[j] < 0xe000:
|
||||
return "";
|
||||
case:
|
||||
if i+3 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = 0xe0 + u8 (str[j] >> 12); i++;
|
||||
buf[i] = 0x80 + u8((str[j] >> 6) & 0x3f); i++;
|
||||
buf[i] = 0x80 + u8((str[j] ) & 0x3f); i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[0..<i]);
|
||||
}
|
||||
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.command_line_to_argv_w(win32.get_command_line_w(), &arg_count);
|
||||
arg_list := make([]string, arg_count);
|
||||
for _, i in arg_list {
|
||||
arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
|
||||
}
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
|
||||
-320
@@ -1,320 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
FileTime :: u64;
|
||||
Errno :: int;
|
||||
|
||||
// TODO(zangent): Find out how to make this work on x64 and x32.
|
||||
AddressSize :: i64;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 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;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
args: [dynamic]string;
|
||||
|
||||
_FileTime :: struct #ordered {
|
||||
seconds: i64,
|
||||
nanoseconds: i64
|
||||
}
|
||||
|
||||
Stat :: struct #ordered {
|
||||
device_id : i32, // ID of device containing file
|
||||
mode : u16, // Mode of the file
|
||||
nlink : u16, // Number of hard links
|
||||
serial : u64, // File serial number
|
||||
uid : u32, // User ID of the file's owner
|
||||
gid : u32, // Group ID of the file's group
|
||||
rdev : i32, // Device ID, if device
|
||||
|
||||
last_access : FileTime, // Time of last access
|
||||
modified : FileTime, // Time of last modification
|
||||
status_change : FileTime, // Time of last status change
|
||||
created : FileTime, // Time of creation
|
||||
|
||||
size : i64, // Size of the file, in bytes
|
||||
blocks : i64, // Number of blocks allocated for the file
|
||||
block_size: i32, // Optimal blocksize for I/O
|
||||
flags : u32, // User-defined flags for the file
|
||||
gen_num : u32, // File generation number ...?
|
||||
_spare : i32, // RESERVED
|
||||
_reserve1,
|
||||
_reserve2 : i64, // RESERVED
|
||||
};
|
||||
|
||||
// File type
|
||||
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
|
||||
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
unix_close :: proc(handle: Handle) #foreign libc "close";
|
||||
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read";
|
||||
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write";
|
||||
unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek";
|
||||
unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat";
|
||||
unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access";
|
||||
|
||||
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_written := unix_write(fd, &data[0], len(data));
|
||||
if(bytes_written == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_written, 0;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_read := unix_read(fd, &data[0], len(data));
|
||||
if(bytes_read == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_read, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
final_offset := unix_lseek(fd, offset, whence);
|
||||
if(final_offset == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return size, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> FileTime {}
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {}
|
||||
*/
|
||||
|
||||
stat :: proc(path: string) -> (Stat, bool) #inline {
|
||||
s: Stat;
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
ret_int := unix_stat(cstr, &s);
|
||||
return s, ret_int==0;
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool #inline {
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
return unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
// read_entire_file :: proc(name: string) -> ([]u8, bool) {
|
||||
|
||||
// handle, err := open_simple(name, O_RDONLY);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to open file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
// defer(close(handle));
|
||||
|
||||
// // We have a file!
|
||||
|
||||
// size: AddressSize;
|
||||
// size, err = seek(handle, 0, SEEK_END);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to seek to end of file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// _, err = seek(handle, 0, SEEK_SET);
|
||||
// if(err != 0) {
|
||||
// fmt.println("Failed to seek to beginning of file.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// // We have a file size!
|
||||
|
||||
// data := make([]u8, size+1);
|
||||
// if data == nil {
|
||||
// fmt.println("Failed to allocate file buffer.");
|
||||
// return nil, false;
|
||||
// }
|
||||
|
||||
// read(handle, data);
|
||||
// data[size] = 0;
|
||||
|
||||
// return data, true;
|
||||
// }
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr #inline {
|
||||
assert(size > 0);
|
||||
return unix_malloc(size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
|
||||
return unix_realloc(ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) #inline {
|
||||
unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) #inline {
|
||||
unix_exit(code);
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return cast(int) unix_gettid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(unix_dlerror());
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
Any :: struct #ordered {
|
||||
data: rawptr,
|
||||
type_info: ^TypeInfo,
|
||||
}
|
||||
|
||||
String :: struct #ordered {
|
||||
data: ^u8,
|
||||
len: int,
|
||||
};
|
||||
|
||||
Slice :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
};
|
||||
|
||||
DynamicArray :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: Allocator,
|
||||
};
|
||||
|
||||
DynamicMap :: struct #ordered {
|
||||
hashes: [dynamic]int,
|
||||
entries: DynamicArray,
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,470 @@
|
||||
package runtime
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
print_u64 :: proc(fd: os.Handle, u: u64) {
|
||||
digits := "0123456789";
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
b := u64(10);
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
|
||||
os.write(fd, a[i:]);
|
||||
}
|
||||
|
||||
print_i64 :: proc(fd: os.Handle, u: i64) {
|
||||
digits := "0123456789";
|
||||
|
||||
neg := u < 0;
|
||||
u = abs(u);
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
b := i64(10);
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
if neg {
|
||||
i -= 1; a[i] = '-';
|
||||
}
|
||||
|
||||
os.write(fd, a[i:]);
|
||||
}
|
||||
|
||||
print_caller_location :: proc(fd: os.Handle, using loc: Source_Code_Location) {
|
||||
os.write_string(fd, file_path);
|
||||
os.write_byte(fd, '(');
|
||||
print_u64(fd, u64(line));
|
||||
os.write_byte(fd, ':');
|
||||
print_u64(fd, u64(column));
|
||||
os.write_byte(fd, ')');
|
||||
}
|
||||
print_typeid :: proc(fd: os.Handle, id: typeid) {
|
||||
ti := type_info_of(id);
|
||||
print_type(fd, ti);
|
||||
}
|
||||
print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
if ti == nil {
|
||||
os.write_string(fd, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
os.write_string(fd, info.name);
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: os.write_string(fd, "int");
|
||||
case uint: os.write_string(fd, "uint");
|
||||
case uintptr: os.write_string(fd, "uintptr");
|
||||
case:
|
||||
os.write_byte(fd, info.signed ? 'i' : 'u');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
}
|
||||
case Type_Info_Rune:
|
||||
os.write_string(fd, "rune");
|
||||
case Type_Info_Float:
|
||||
os.write_byte(fd, 'f');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_Complex:
|
||||
os.write_string(fd, "complex");
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_String:
|
||||
os.write_string(fd, "string");
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: os.write_string(fd, "bool");
|
||||
case:
|
||||
os.write_byte(fd, 'b');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
}
|
||||
case Type_Info_Any:
|
||||
os.write_string(fd, "any");
|
||||
case Type_Info_Type_Id:
|
||||
os.write_string(fd, "typeid");
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
os.write_string(fd, "rawptr");
|
||||
} else {
|
||||
os.write_string(fd, "^");
|
||||
print_type(fd, info.elem);
|
||||
}
|
||||
case Type_Info_Procedure:
|
||||
os.write_string(fd, "proc");
|
||||
if info.params == nil {
|
||||
os.write_string(fd, "()");
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Tuple);
|
||||
os.write_string(fd, "(");
|
||||
for t, i in t.types {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, t);
|
||||
}
|
||||
os.write_string(fd, ")");
|
||||
}
|
||||
if info.results != nil {
|
||||
os.write_string(fd, " -> ");
|
||||
print_type(fd, info.results);
|
||||
}
|
||||
case Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do os.write_string(fd, "(");
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
|
||||
t := info.types[i];
|
||||
|
||||
if len(name) > 0 {
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
}
|
||||
print_type(fd, t);
|
||||
}
|
||||
if count != 1 do os.write_string(fd, ")");
|
||||
|
||||
case Type_Info_Array:
|
||||
os.write_string(fd, "[");
|
||||
print_u64(fd, u64(info.count));
|
||||
os.write_string(fd, "]");
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Dynamic_Array:
|
||||
os.write_string(fd, "[dynamic]");
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Slice:
|
||||
os.write_string(fd, "[]");
|
||||
print_type(fd, info.elem);
|
||||
|
||||
case Type_Info_Map:
|
||||
os.write_string(fd, "map[");
|
||||
print_type(fd, info.key);
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.value);
|
||||
|
||||
case Type_Info_Struct:
|
||||
os.write_string(fd, "struct ");
|
||||
if info.is_packed do os.write_string(fd, "#packed ");
|
||||
if info.is_raw_union do os.write_string(fd, "#raw_union ");
|
||||
if info.custom_align {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
os.write_byte(fd, ' ');
|
||||
}
|
||||
os.write_byte(fd, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
print_type(fd, info.types[i]);
|
||||
}
|
||||
os.write_byte(fd, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
os.write_string(fd, "union {");
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, variant);
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Enum:
|
||||
os.write_string(fd, "enum ");
|
||||
print_type(fd, info.base);
|
||||
os.write_string(fd, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
os.write_string(fd, "bit_field ");
|
||||
if ti.align != 1 {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
os.write_byte(fd, ' ');
|
||||
}
|
||||
os.write_string(fd, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
print_u64(fd, u64(info.bits[i]));
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
os.write_string(fd, "bit_set[");
|
||||
|
||||
switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Rune:
|
||||
os.write_encoded_rune(fd, rune(info.lower));
|
||||
os.write_string(fd, "..");
|
||||
os.write_encoded_rune(fd, rune(info.upper));
|
||||
case:
|
||||
print_i64(fd, info.lower);
|
||||
os.write_string(fd, "..");
|
||||
print_i64(fd, info.upper);
|
||||
}
|
||||
if info.underlying != nil {
|
||||
os.write_string(fd, "; ");
|
||||
print_type(fd, info.underlying);
|
||||
}
|
||||
os.write_byte(fd, ']');
|
||||
}
|
||||
}
|
||||
|
||||
string_eq :: proc "contextless" (a, b: string) -> bool {
|
||||
switch {
|
||||
case len(a) != len(b): return false;
|
||||
case len(a) == 0: return true;
|
||||
case &a[0] == &b[0]: return true;
|
||||
}
|
||||
return string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
|
||||
string_lt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0; }
|
||||
string_gt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0; }
|
||||
string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0; }
|
||||
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
|
||||
|
||||
cstring_len :: proc "contextless" (s: cstring) -> int {
|
||||
n := 0;
|
||||
for p := (^byte)(s); p != nil && p^ != 0; p = mem.ptr_offset(p, 1) {
|
||||
n += 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
if s == nil do return "";
|
||||
ptr := (^byte)(s);
|
||||
n := cstring_len(s);
|
||||
return transmute(string)mem.Raw_String{ptr, n};
|
||||
}
|
||||
|
||||
|
||||
complex64_eq :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex64_ne :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
|
||||
os.write_string(fd, " Index ");
|
||||
print_i64(fd, i64(index));
|
||||
os.write_string(fd, " is out of bounds range 0:");
|
||||
print_i64(fd, i64(count));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
slice_expr_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
|
||||
if 0 <= lo && lo <= hi && hi <= len do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
|
||||
os.write_string(fd, " Invalid slice indices: ");
|
||||
print_i64(fd, i64(lo));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(hi));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
|
||||
if 0 <= low && low <= high && high <= max do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
|
||||
os.write_string(fd, " Invalid dynamic array values: ");
|
||||
print_i64(fd, i64(low));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(high));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(max));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid) {
|
||||
if ok do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
|
||||
os.write_string(fd, " Invalid type assertion from ");
|
||||
print_typeid(fd, from);
|
||||
os.write_string(fd, " to ");
|
||||
print_typeid(fd, to);
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
|
||||
return utf8.decode_rune_from_string(s);
|
||||
}
|
||||
|
||||
bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) {
|
||||
bounds_check_error(file_path, int(line), int(column), index, count);
|
||||
}
|
||||
|
||||
slice_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) {
|
||||
slice_expr_error(file_path, int(line), int(column), lo, hi, len);
|
||||
}
|
||||
|
||||
dynamic_array_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, low, high, max: int) {
|
||||
dynamic_array_expr_error(file_path, int(line), int(column), low, high, max);
|
||||
}
|
||||
|
||||
|
||||
make_slice_error_loc :: inline proc "contextless" (using loc := #caller_location, len: int) {
|
||||
if 0 <= len do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid slice length for make: ");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
make_dynamic_array_error_loc :: inline proc "contextless" (using loc := #caller_location, len, cap: int) {
|
||||
if 0 <= len && len <= cap do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid dynamic array parameters for make: ");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, ':');
|
||||
print_i64(fd, i64(cap));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
make_map_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, cap: int) {
|
||||
if 0 <= cap do return;
|
||||
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid map capacity for make: ");
|
||||
print_i64(fd, i64(cap));
|
||||
os.write_byte(fd, '\n');
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@(default_calling_convention = "c")
|
||||
foreign {
|
||||
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
|
||||
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
|
||||
}
|
||||
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
|
||||
}
|
||||
return _abs(x);
|
||||
}
|
||||
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
|
||||
}
|
||||
return _abs(x);
|
||||
}
|
||||
|
||||
min_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
}
|
||||
min_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
}
|
||||
max_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
}
|
||||
max_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
}
|
||||
|
||||
abs_complex64 :: inline proc "contextless" (x: complex64) -> f32 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f32(r*r + i*i);
|
||||
}
|
||||
abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f64(r*r + i*i);
|
||||
}
|
||||
|
||||
|
||||
quo_complex64 :: proc(n, m: complex64) -> complex64 {
|
||||
e, f: f32;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
ratio := imag(m) / real(m);
|
||||
denom := real(m) + ratio*imag(m);
|
||||
e = (real(n) + imag(n)*ratio) / denom;
|
||||
f = (imag(n) - real(n)*ratio) / denom;
|
||||
} else {
|
||||
ratio := real(m) / imag(m);
|
||||
denom := imag(m) + ratio*real(m);
|
||||
e = (real(n)*ratio + imag(n)) / denom;
|
||||
f = (imag(n)*ratio - real(n)) / denom;
|
||||
}
|
||||
|
||||
return complex(e, f);
|
||||
}
|
||||
|
||||
quo_complex128 :: proc(n, m: complex128) -> complex128 {
|
||||
e, f: f64;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
ratio := imag(m) / real(m);
|
||||
denom := real(m) + ratio*imag(m);
|
||||
e = (real(n) + imag(n)*ratio) / denom;
|
||||
f = (imag(n) - real(n)*ratio) / denom;
|
||||
} else {
|
||||
ratio := real(m) / imag(m);
|
||||
denom := imag(m) + ratio*real(m);
|
||||
e = (real(n)*ratio + imag(n)) / denom;
|
||||
f = (imag(n)*ratio - real(n)) / denom;
|
||||
}
|
||||
|
||||
return complex(e, f);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(link_name="memcpy")
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) ---
|
||||
}
|
||||
RtlCopyMemory(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memmove")
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) ---
|
||||
}
|
||||
RtlMoveMemory(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
RtlFillMemory :: proc "c" (dst: rawptr, len: int, fill: byte) ---
|
||||
}
|
||||
RtlFillMemory(ptr, len, byte(val));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// @(link_name="memcmp")
|
||||
// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 {
|
||||
// if dst == nil || src == nil {
|
||||
// return 0;
|
||||
// }
|
||||
// if dst == src {
|
||||
// return 0;
|
||||
// }
|
||||
// d, s := uintptr(dst), uintptr(src);
|
||||
// n := uintptr(len);
|
||||
|
||||
// for i := uintptr(0); i < n; i += 1 {
|
||||
// x, y := (^byte)(d+i)^, (^byte)(s+i)^;
|
||||
// if x != y {
|
||||
// return x < y ? -1 : +1;
|
||||
// }
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
@@ -0,0 +1,217 @@
|
||||
package sort
|
||||
|
||||
import "core:mem"
|
||||
|
||||
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil);
|
||||
count := len(array);
|
||||
|
||||
init_j, last_j := 0, count-1;
|
||||
|
||||
for {
|
||||
init_swap, prev_swap := -1, -1;
|
||||
|
||||
for j in init_j..last_j-1 {
|
||||
if f(array[j], array[j+1]) > 0 {
|
||||
array[j], array[j+1] = array[j+1], array[j];
|
||||
prev_swap = j;
|
||||
if init_swap == -1 do init_swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if prev_swap == -1 do return;
|
||||
|
||||
init_j = max(init_swap-1, 0);
|
||||
last_j = prev_swap;
|
||||
}
|
||||
}
|
||||
|
||||
bubble_sort :: proc(array: $A/[]$T) {
|
||||
count := len(array);
|
||||
|
||||
init_j, last_j := 0, count-1;
|
||||
|
||||
for {
|
||||
init_swap, prev_swap := -1, -1;
|
||||
|
||||
for j in init_j..last_j-1 {
|
||||
if array[j] > array[j+1] {
|
||||
array[j], array[j+1] = array[j+1], array[j];
|
||||
prev_swap = j;
|
||||
if init_swap == -1 do init_swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if prev_swap == -1 do return;
|
||||
|
||||
init_j = max(init_swap-1, 0);
|
||||
last_j = prev_swap;
|
||||
}
|
||||
}
|
||||
|
||||
quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil);
|
||||
a := array;
|
||||
n := len(a);
|
||||
if n < 2 do return;
|
||||
|
||||
p := a[n/2];
|
||||
i, j := 0, n-1;
|
||||
|
||||
loop: for {
|
||||
for f(a[i], p) < 0 do i += 1;
|
||||
for f(p, a[j]) < 0 do j -= 1;
|
||||
|
||||
if i >= j do break loop;
|
||||
|
||||
a[i], a[j] = a[j], a[i];
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
quick_sort_proc(a[0:i], f);
|
||||
quick_sort_proc(a[i:n], f);
|
||||
}
|
||||
|
||||
quick_sort :: proc(array: $A/[]$T) {
|
||||
a := array;
|
||||
n := len(a);
|
||||
if n < 2 do return;
|
||||
|
||||
p := a[n/2];
|
||||
i, j := 0, n-1;
|
||||
|
||||
loop: for {
|
||||
for a[i] < p do i += 1;
|
||||
for p < a[j] do j -= 1;
|
||||
|
||||
if i >= j do break loop;
|
||||
|
||||
a[i], a[j] = a[j], a[i];
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
quick_sort(a[0:i]);
|
||||
quick_sort(a[i:n]);
|
||||
}
|
||||
|
||||
_log2 :: proc(n: int) -> int {
|
||||
res := 0;
|
||||
for ; n != 0; n >>= 1 do res += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
merge_slices :: proc(arr1, arr2, out: A, f: proc(T, T) -> int) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..N1+N2-1 {
|
||||
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(f != nil);
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M {
|
||||
for j in 0..a-1 {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:], f);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:], f);
|
||||
} else {
|
||||
copy(arr2[b:N], arr1[b:N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
}
|
||||
|
||||
merge_sort :: proc(array: $A/[]$T) {
|
||||
merge_slices :: proc(arr1, arr2, out: A) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..N1+N2-1 {
|
||||
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M {
|
||||
for j in 0..a-1 {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:]);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:]);
|
||||
} else {
|
||||
copy(arr2[b:N], arr1[b:N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
compare_ints :: proc(a, b: int) -> int {
|
||||
switch delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
compare_f32s :: proc(a, b: f32) -> int {
|
||||
switch delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
compare_f64s :: proc(a, b: f64) -> int {
|
||||
switch delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
compare_strings :: proc(a, b: string) -> int {
|
||||
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
@@ -1,498 +0,0 @@
|
||||
#import . "decimal.odin";
|
||||
|
||||
IntFlag :: enum {
|
||||
Prefix = 1<<0,
|
||||
Plus = 1<<1,
|
||||
Space = 1<<2,
|
||||
}
|
||||
|
||||
|
||||
parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
|
||||
match s {
|
||||
case "1", "t", "T", "true", "TRUE", "True":
|
||||
return true, true;
|
||||
case "0", "f", "F", "false", "FALSE", "False":
|
||||
return false, true;
|
||||
}
|
||||
return false, false;
|
||||
}
|
||||
|
||||
_digit_value :: proc(r: rune) -> (int) {
|
||||
ri := int(r);
|
||||
v: int = 16;
|
||||
match r {
|
||||
case '0'..'9': v = ri-'0';
|
||||
case 'a'..'z': v = ri-'a'+10;
|
||||
case 'A'..'Z': v = ri-'A'+10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
parse_i128 :: proc(s: string) -> i128 {
|
||||
neg := false;
|
||||
if len(s) > 1 {
|
||||
match s[0] {
|
||||
case '-':
|
||||
neg = true;
|
||||
s = s[1..];
|
||||
case '+':
|
||||
s = s[1..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
base: i128 = 10;
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
match s[1] {
|
||||
case 'b': base = 2; s = s[2..];
|
||||
case 'o': base = 8; s = s[2..];
|
||||
case 'd': base = 10; s = s[2..];
|
||||
case 'z': base = 12; s = s[2..];
|
||||
case 'x': base = 16; s = s[2..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: i128;
|
||||
for r in s {
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
|
||||
v := i128(_digit_value(r));
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
value *= base;
|
||||
value += v;
|
||||
}
|
||||
|
||||
return neg ? -value : value;
|
||||
}
|
||||
|
||||
parse_u128 :: proc(s: string) -> u128 {
|
||||
neg := false;
|
||||
if len(s) > 1 && s[0] == '+' {
|
||||
s = s[1..];
|
||||
}
|
||||
|
||||
|
||||
base: = u128(10);
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
match s[1] {
|
||||
case 'b': base = 2; s = s[2..];
|
||||
case 'o': base = 8; s = s[2..];
|
||||
case 'd': base = 10; s = s[2..];
|
||||
case 'z': base = 12; s = s[2..];
|
||||
case 'x': base = 16; s = s[2..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: u128;
|
||||
for r in s {
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
|
||||
v := u128(_digit_value(r));
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
value *= base;
|
||||
value += u128(v);
|
||||
}
|
||||
|
||||
return neg ? -value : value;
|
||||
}
|
||||
|
||||
|
||||
parse_int :: proc(s: string) -> int {
|
||||
return int(parse_i128(s));
|
||||
}
|
||||
parse_uint :: proc(s: string, base: int) -> uint {
|
||||
return uint(parse_u128(s));
|
||||
}
|
||||
|
||||
parse_f64 :: proc(s: string) -> f64 {
|
||||
i := 0;
|
||||
|
||||
sign: f64 = 1;
|
||||
match s[i] {
|
||||
case '-': i++; sign = -1;
|
||||
case '+': i++;
|
||||
}
|
||||
|
||||
value: f64 = 0;
|
||||
for ; i < len(s); i++ {
|
||||
r := rune(s[i]);
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
v := _digit_value(r);
|
||||
if v >= 10 {
|
||||
break;
|
||||
}
|
||||
value *= 10;
|
||||
value += f64(v);
|
||||
}
|
||||
|
||||
if s[i] == '.' {
|
||||
pow10: f64 = 10;
|
||||
i++;
|
||||
|
||||
for ; i < len(s); i++ {
|
||||
r := rune(s[i]);
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
v := _digit_value(r);
|
||||
if v >= 10 {
|
||||
break;
|
||||
}
|
||||
value += f64(v)/pow10;
|
||||
pow10 *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
frac := false;
|
||||
scale: f64 = 1;
|
||||
|
||||
if s[i] == 'e' || s[i] == 'E' {
|
||||
i++;
|
||||
|
||||
match s[i] {
|
||||
case '-': i++; frac = true;
|
||||
case '+': i++;
|
||||
}
|
||||
|
||||
exp: u32 = 0;
|
||||
for ; i < len(s); i++ {
|
||||
r := rune(s[i]);
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
d := u32(_digit_value(r));
|
||||
if d >= 10 {
|
||||
break;
|
||||
}
|
||||
exp = exp * 10 + d;
|
||||
}
|
||||
if exp > 308 { exp = 308; }
|
||||
|
||||
for exp >= 50 { scale *= 1e50; exp -= 50; }
|
||||
for exp >= 8 { scale *= 1e8; exp -= 8; }
|
||||
for exp > 0 { scale *= 10; exp -= 1; }
|
||||
}
|
||||
|
||||
return sign * (frac ? (value/scale) : (value*scale));
|
||||
}
|
||||
|
||||
|
||||
append_bool :: proc(buf: []u8, b: bool) -> string {
|
||||
s := b ? "true" : "false";
|
||||
append(buf, ..[]u8(s));
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
append_uint :: proc(buf: []u8, u: u64, base: int) -> string {
|
||||
return append_bits(buf, u128(u), base, false, 8*size_of(uint), digits, 0);
|
||||
}
|
||||
append_int :: proc(buf: []u8, i: i64, base: int) -> string {
|
||||
return append_bits(buf, u128(i), base, true, 8*size_of(int), digits, 0);
|
||||
}
|
||||
itoa :: proc(buf: []u8, i: int) -> string { return append_int(buf, i64(i), 10); }
|
||||
|
||||
append_float :: proc(buf: []u8, f: f64, fmt: u8, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DecimalSlice :: struct {
|
||||
digits: []u8,
|
||||
count: int,
|
||||
decimal_point: int,
|
||||
neg: bool,
|
||||
}
|
||||
|
||||
Float_Info :: struct {
|
||||
mantbits: uint,
|
||||
expbits: uint,
|
||||
bias: int,
|
||||
}
|
||||
|
||||
_f16_info := Float_Info{10, 5, -15};
|
||||
_f32_info := Float_Info{23, 8, -127};
|
||||
_f64_info := Float_Info{52, 11, -1023};
|
||||
|
||||
|
||||
generic_ftoa :: proc(buf: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 {
|
||||
bits: u64;
|
||||
flt: ^Float_Info;
|
||||
match bit_size {
|
||||
case 32:
|
||||
bits = u64(transmute(u32, f32(val)));
|
||||
flt = &_f32_info;
|
||||
case 64:
|
||||
bits = transmute(u64, val);
|
||||
flt = &_f64_info;
|
||||
case:
|
||||
panic("strconv: invalid bit_size");
|
||||
}
|
||||
|
||||
neg := bits>>(flt.expbits+flt.mantbits) != 0;
|
||||
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
|
||||
mant := bits & (u64(1) << flt.mantbits - 1);
|
||||
|
||||
match exp {
|
||||
case 1<<flt.expbits - 1:
|
||||
s: string;
|
||||
if mant != 0 {
|
||||
s = "NaN";
|
||||
} else if neg {
|
||||
s = "-Inf";
|
||||
} else {
|
||||
s = "+Inf";
|
||||
}
|
||||
append(buf, ..[]u8(s));
|
||||
return buf;
|
||||
|
||||
case 0: // denormalized
|
||||
exp++;
|
||||
|
||||
case:
|
||||
mant |= u64(1) << flt.mantbits;
|
||||
}
|
||||
|
||||
exp += flt.bias;
|
||||
|
||||
d_: Decimal;
|
||||
d := &d_;
|
||||
assign(d, mant);
|
||||
shift(d, exp - int(flt.mantbits));
|
||||
digs: DecimalSlice;
|
||||
shortest := prec < 0;
|
||||
if shortest {
|
||||
round_shortest(d, mant, exp, flt);
|
||||
digs = DecimalSlice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
|
||||
match fmt {
|
||||
case 'e', 'E': prec = digs.count-1;
|
||||
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
|
||||
case 'g', 'G': prec = digs.count;
|
||||
}
|
||||
} else {
|
||||
match fmt {
|
||||
case 'e', 'E': round(d, prec+1);
|
||||
case 'f', 'F': round(d, d.decimal_point+prec);
|
||||
case 'g', 'G':
|
||||
if prec == 0 {
|
||||
prec = 1;
|
||||
}
|
||||
round(d, prec);
|
||||
}
|
||||
|
||||
digs = DecimalSlice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
|
||||
}
|
||||
return format_digits(buf, shortest, neg, digs, prec, fmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
format_digits :: proc(buf: []u8, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: u8) -> []u8 {
|
||||
match fmt {
|
||||
case 'f', 'F':
|
||||
append(buf, neg ? '-' : '+');
|
||||
|
||||
// integer, padded with zeros when needed
|
||||
if digs.decimal_point > 0 {
|
||||
m := min(digs.count, digs.decimal_point);
|
||||
append(buf, ..digs.digits[0..<m]);
|
||||
for ; m < digs.decimal_point; m++ {
|
||||
append(buf, '0');
|
||||
}
|
||||
} else {
|
||||
append(buf, '0');
|
||||
}
|
||||
|
||||
|
||||
// fractional part
|
||||
if prec > 0 {
|
||||
append(buf, '.');
|
||||
for i in 0..<prec {
|
||||
c: u8 = '0';
|
||||
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
|
||||
c = digs.digits[j];
|
||||
}
|
||||
append(buf, c);
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
||||
case 'e', 'E':
|
||||
panic("strconv: e/E float printing is not yet supported");
|
||||
return buf; // TODO
|
||||
|
||||
case 'g', 'G':
|
||||
panic("strconv: g/G float printing is not yet supported");
|
||||
return buf; // TODO
|
||||
}
|
||||
|
||||
c: [2]u8;
|
||||
c[0] = '%';
|
||||
c[1] = fmt;
|
||||
append(buf, ..c[..]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
if mant == 0 { // If mantissa is zero, the number is zero
|
||||
d.count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
10^(dp-nd) > 2^(exp-mantbits)
|
||||
log2(10) * (dp-nd) > exp-mantbits
|
||||
log(2) >~ 0.332
|
||||
332*(dp-nd) >= 100*(exp-mantbits)
|
||||
*/
|
||||
minexp := flt.bias+1;
|
||||
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
|
||||
// Number is already its shortest
|
||||
return;
|
||||
}
|
||||
|
||||
upper_: Decimal; upper: = &upper_;
|
||||
assign(upper, 2*mant - 1);
|
||||
shift(upper, exp - int(flt.mantbits) - 1);
|
||||
|
||||
mantlo: u64;
|
||||
explo: int;
|
||||
if mant > 1<<flt.mantbits || exp == minexp {
|
||||
mantlo = mant-1;
|
||||
explo = exp;
|
||||
} else {
|
||||
mantlo = 2*mant - 1;
|
||||
explo = exp-1;
|
||||
}
|
||||
lower_: Decimal; lower: = &lower_;
|
||||
assign(lower, 2*mantlo + 1);
|
||||
shift(lower, explo - int(flt.mantbits) - 1);
|
||||
|
||||
inclusive := mant%2 == 0;
|
||||
|
||||
for i in 0..<d.count {
|
||||
l: u8 = '0'; // lower digit
|
||||
if i < lower.count {
|
||||
l = lower.digits[i];
|
||||
}
|
||||
m := d.digits[i]; // middle digit
|
||||
u: u8 = '0'; // upper digit
|
||||
if i < upper.count {
|
||||
u = upper.digits[i];
|
||||
}
|
||||
|
||||
ok_round_down := l != m || inclusive && i+1 == lower.count;
|
||||
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
|
||||
|
||||
if (ok_round_down && ok_round_up) {
|
||||
round(d, i+1);
|
||||
return;
|
||||
}
|
||||
if (ok_round_down) {
|
||||
round_down(d, i+1);
|
||||
return;
|
||||
}
|
||||
if (ok_round_up) {
|
||||
round_up(d, i+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MAX_BASE :: 32;
|
||||
immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
|
||||
neg := false;
|
||||
if is_signed {
|
||||
match bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 128:
|
||||
i := i128(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
append_bits :: proc(buf: []u8, u_: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: IntFlag) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
a: [129]u8;
|
||||
i := len(a);
|
||||
u, neg := is_integer_negative(u_, is_signed, bit_size);
|
||||
b := u128(base);
|
||||
for u >= b {
|
||||
i--; a[i] = digits[uint(u % b)];
|
||||
u /= b;
|
||||
}
|
||||
i--; a[i] = digits[uint(u % b)];
|
||||
|
||||
if flags&IntFlag.Prefix != 0 {
|
||||
ok := true;
|
||||
match base {
|
||||
case 2: i--; a[i] = 'b';
|
||||
case 8: i--; a[i] = 'o';
|
||||
case 10: i--; a[i] = 'd';
|
||||
case 12: i--; a[i] = 'z';
|
||||
case 16: i--; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i--; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
i--; a[i] = '-';
|
||||
} else if flags&IntFlag.Plus != 0 {
|
||||
i--; a[i] = '+';
|
||||
} else if flags&IntFlag.Space != 0 {
|
||||
i--; a[i] = ' ';
|
||||
}
|
||||
|
||||
append(buf, ..a[i..]);
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,506 @@
|
||||
package strconv
|
||||
|
||||
using import "core:decimal"
|
||||
|
||||
Int_Flag :: enum {
|
||||
Prefix,
|
||||
Plus,
|
||||
Space,
|
||||
}
|
||||
Int_Flags :: bit_set[Int_Flag];
|
||||
|
||||
|
||||
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
|
||||
switch s {
|
||||
case "1", "t", "T", "true", "TRUE", "True":
|
||||
return true, true;
|
||||
case "0", "f", "F", "false", "FALSE", "False":
|
||||
return false, true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_digit_value :: proc(r: rune) -> int {
|
||||
ri := int(r);
|
||||
v: int = 16;
|
||||
switch r {
|
||||
case '0'..'9': v = ri-'0';
|
||||
case 'a'..'z': v = ri-'a'+10;
|
||||
case 'A'..'Z': v = ri-'A'+10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
parse_i64 :: proc(s: string) -> i64 {
|
||||
neg := false;
|
||||
if len(s) > 1 {
|
||||
switch s[0] {
|
||||
case '-':
|
||||
neg = true;
|
||||
s = s[1:];
|
||||
case '+':
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
base: i64 = 10;
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
switch s[1] {
|
||||
case 'b': base = 2; s = s[2:];
|
||||
case 'o': base = 8; s = s[2:];
|
||||
case 'd': base = 10; s = s[2:];
|
||||
case 'z': base = 12; s = s[2:];
|
||||
case 'x': base = 16; s = s[2:];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: i64;
|
||||
for r in s {
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
|
||||
v := i64(_digit_value(r));
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
value *= base;
|
||||
value += v;
|
||||
}
|
||||
|
||||
if neg do return -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
parse_u64 :: proc(s: string) -> u64 {
|
||||
neg := false;
|
||||
if len(s) > 1 && s[0] == '+' {
|
||||
s = s[1:];
|
||||
}
|
||||
|
||||
|
||||
base := u64(10);
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
switch s[1] {
|
||||
case 'b': base = 2; s = s[2:];
|
||||
case 'o': base = 8; s = s[2:];
|
||||
case 'd': base = 10; s = s[2:];
|
||||
case 'z': base = 12; s = s[2:];
|
||||
case 'x': base = 16; s = s[2:];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: u64;
|
||||
for r in s {
|
||||
if r == '_' do continue;
|
||||
v := u64(_digit_value(r));
|
||||
if v >= base do break;
|
||||
value *= base;
|
||||
value += u64(v);
|
||||
}
|
||||
|
||||
if neg do return -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
parse_int :: proc(s: string) -> int {
|
||||
return int(parse_i64(s));
|
||||
}
|
||||
parse_uint :: proc(s: string, base: int) -> uint {
|
||||
return uint(parse_u64(s));
|
||||
}
|
||||
|
||||
parse_f32 :: proc(s: string) -> f32 {
|
||||
return f32(parse_f64(s));
|
||||
}
|
||||
|
||||
|
||||
parse_f64 :: proc(s: string) -> f64 {
|
||||
if s == "" {
|
||||
return 0;
|
||||
}
|
||||
i := 0;
|
||||
|
||||
sign: f64 = 1;
|
||||
switch s[i] {
|
||||
case '-': i += 1; sign = -1;
|
||||
case '+': i += 1;
|
||||
}
|
||||
|
||||
value: f64 = 0;
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
v := _digit_value(r);
|
||||
if v >= 10 do break;
|
||||
value *= 10;
|
||||
value += f64(v);
|
||||
}
|
||||
|
||||
if i < len(s) && s[i] == '.' {
|
||||
pow10: f64 = 10;
|
||||
i += 1;
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
v := _digit_value(r);
|
||||
if v >= 10 do break;
|
||||
value += f64(v)/pow10;
|
||||
pow10 *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
frac := false;
|
||||
scale: f64 = 1;
|
||||
|
||||
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
|
||||
i += 1;
|
||||
|
||||
if i < len(s) {
|
||||
switch s[i] {
|
||||
case '-': i += 1; frac = true;
|
||||
case '+': i += 1;
|
||||
}
|
||||
|
||||
exp: u32 = 0;
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
d := u32(_digit_value(r));
|
||||
if d >= 10 do break;
|
||||
exp = exp * 10 + d;
|
||||
}
|
||||
if exp > 308 { exp = 308; }
|
||||
|
||||
for exp >= 50 { scale *= 1e50; exp -= 50; }
|
||||
for exp >= 8 { scale *= 1e8; exp -= 8; }
|
||||
for exp > 0 { scale *= 10; exp -= 1; }
|
||||
}
|
||||
}
|
||||
|
||||
if frac do return sign * (value/scale);
|
||||
return sign * (value*scale);
|
||||
}
|
||||
|
||||
|
||||
append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
n := 0;
|
||||
if b do n = copy(buf, cast([]byte)"true");
|
||||
else do n = copy(buf, cast([]byte)"false");
|
||||
return string(buf[:n]);
|
||||
}
|
||||
|
||||
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
|
||||
return append_bits(buf, u64(u), base, false, 8*size_of(uint), digits, nil);
|
||||
}
|
||||
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
|
||||
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil);
|
||||
}
|
||||
itoa :: proc(buf: []byte, i: int) -> string do return append_int(buf, i64(i), 10);
|
||||
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DecimalSlice :: struct {
|
||||
digits: []byte,
|
||||
count: int,
|
||||
decimal_point: int,
|
||||
neg: bool,
|
||||
}
|
||||
|
||||
FloatInfo :: struct {
|
||||
mantbits: uint,
|
||||
expbits: uint,
|
||||
bias: int,
|
||||
}
|
||||
|
||||
|
||||
_f16_info := FloatInfo{10, 5, -15};
|
||||
_f32_info := FloatInfo{23, 8, -127};
|
||||
_f64_info := FloatInfo{52, 11, -1023};
|
||||
|
||||
|
||||
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte {
|
||||
bits: u64;
|
||||
flt: ^FloatInfo;
|
||||
switch bit_size {
|
||||
case 32:
|
||||
bits = u64(transmute(u32)f32(val));
|
||||
flt = &_f32_info;
|
||||
case 64:
|
||||
bits = transmute(u64)val;
|
||||
flt = &_f64_info;
|
||||
case:
|
||||
panic("strconv: invalid bit_size");
|
||||
}
|
||||
|
||||
neg := bits>>(flt.expbits+flt.mantbits) != 0;
|
||||
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
|
||||
mant := bits & (u64(1) << flt.mantbits - 1);
|
||||
|
||||
switch exp {
|
||||
case 1<<flt.expbits - 1:
|
||||
s: string;
|
||||
if mant != 0 {
|
||||
s = "NaN";
|
||||
} else if neg {
|
||||
s = "-Inf";
|
||||
} else {
|
||||
s = "+Inf";
|
||||
}
|
||||
n := copy(buf, cast([]byte)s);
|
||||
return buf[:n];
|
||||
|
||||
case 0: // denormalized
|
||||
exp += 1;
|
||||
|
||||
case:
|
||||
mant |= u64(1) << flt.mantbits;
|
||||
}
|
||||
|
||||
exp += flt.bias;
|
||||
|
||||
d_: Decimal;
|
||||
d := &d_;
|
||||
assign(d, mant);
|
||||
shift(d, exp - int(flt.mantbits));
|
||||
digs: DecimalSlice;
|
||||
shortest := prec < 0;
|
||||
if shortest {
|
||||
round_shortest(d, mant, exp, flt);
|
||||
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
|
||||
switch fmt {
|
||||
case 'e', 'E': prec = digs.count-1;
|
||||
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
|
||||
case 'g', 'G': prec = digs.count;
|
||||
}
|
||||
} else {
|
||||
switch fmt {
|
||||
case 'e', 'E': round(d, prec+1);
|
||||
case 'f', 'F': round(d, d.decimal_point+prec);
|
||||
case 'g', 'G':
|
||||
if prec == 0 {
|
||||
prec = 1;
|
||||
}
|
||||
round(d, prec);
|
||||
}
|
||||
|
||||
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
|
||||
}
|
||||
return format_digits(buf, shortest, neg, digs, prec, fmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: byte) -> []byte {
|
||||
Buffer :: struct {
|
||||
b: []byte,
|
||||
n: int,
|
||||
}
|
||||
|
||||
to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n];
|
||||
add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
|
||||
buf.n += copy(buf.b[buf.n:], bytes);
|
||||
}
|
||||
|
||||
b := Buffer{b = buf};
|
||||
|
||||
switch fmt {
|
||||
case 'f', 'F':
|
||||
add_bytes(&b, neg ? '-' : '+');
|
||||
|
||||
// integer, padded with zeros when needed
|
||||
if digs.decimal_point > 0 {
|
||||
m := min(digs.count, digs.decimal_point);
|
||||
add_bytes(&b, ..digs.digits[0:m]);
|
||||
for ; m < digs.decimal_point; m += 1 {
|
||||
add_bytes(&b, '0');
|
||||
}
|
||||
} else {
|
||||
add_bytes(&b, '0');
|
||||
}
|
||||
|
||||
|
||||
// fractional part
|
||||
if prec > 0 {
|
||||
add_bytes(&b, '.');
|
||||
for i in 0..prec-1 {
|
||||
c: byte = '0';
|
||||
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
|
||||
c = digs.digits[j];
|
||||
}
|
||||
add_bytes(&b, c);
|
||||
}
|
||||
}
|
||||
return to_bytes(b);
|
||||
|
||||
case 'e', 'E':
|
||||
panic("strconv: e/E float printing is not yet supported");
|
||||
return to_bytes(b); // TODO
|
||||
|
||||
case 'g', 'G':
|
||||
panic("strconv: g/G float printing is not yet supported");
|
||||
return to_bytes(b); // TODO
|
||||
|
||||
case:
|
||||
add_bytes(&b, '%', fmt);
|
||||
return to_bytes(b);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) {
|
||||
if mant == 0 { // If mantissa is zero, the number is zero
|
||||
d.count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
10^(dp-nd) > 2^(exp-mantbits)
|
||||
log2(10) * (dp-nd) > exp-mantbits
|
||||
log(2) >~ 0.332
|
||||
332*(dp-nd) >= 100*(exp-mantbits)
|
||||
*/
|
||||
minexp := flt.bias+1;
|
||||
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
|
||||
// Number is already its shortest
|
||||
return;
|
||||
}
|
||||
|
||||
upper_: Decimal; upper := &upper_;
|
||||
assign(upper, 2*mant - 1);
|
||||
shift(upper, exp - int(flt.mantbits) - 1);
|
||||
|
||||
mantlo: u64;
|
||||
explo: int;
|
||||
if mant > 1<<flt.mantbits || exp == minexp {
|
||||
mantlo = mant-1;
|
||||
explo = exp;
|
||||
} else {
|
||||
mantlo = 2*mant - 1;
|
||||
explo = exp-1;
|
||||
}
|
||||
lower_: Decimal; lower := &lower_;
|
||||
assign(lower, 2*mantlo + 1);
|
||||
shift(lower, explo - int(flt.mantbits) - 1);
|
||||
|
||||
inclusive := mant%2 == 0;
|
||||
|
||||
for i in 0..d.count-1 {
|
||||
l: byte = '0'; // lower digit
|
||||
if i < lower.count {
|
||||
l = lower.digits[i];
|
||||
}
|
||||
m := d.digits[i]; // middle digit
|
||||
u: byte = '0'; // upper digit
|
||||
if i < upper.count {
|
||||
u = upper.digits[i];
|
||||
}
|
||||
|
||||
ok_round_down := l != m || inclusive && i+1 == lower.count;
|
||||
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
|
||||
|
||||
if ok_round_down && ok_round_up {
|
||||
round(d, i+1);
|
||||
return;
|
||||
}
|
||||
if ok_round_down {
|
||||
round_down(d, i+1);
|
||||
return;
|
||||
}
|
||||
if ok_round_up {
|
||||
round_up(d, i+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MAX_BASE :: 32;
|
||||
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
neg: bool;
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
u, neg = is_integer_negative(u, is_signed, bit_size);
|
||||
b := u64(base);
|
||||
for u >= b {
|
||||
i-=1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i-=1; a[i] = digits[u % b];
|
||||
|
||||
if Int_Flag.Prefix in flags {
|
||||
ok := true;
|
||||
switch base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case neg:
|
||||
i-=1; a[i] = '-';
|
||||
case Int_Flag.Plus in flags:
|
||||
i-=1; a[i] = '+';
|
||||
case Int_Flag.Space in flags:
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
out := a[i:];
|
||||
copy(buf, out);
|
||||
return string(buf[0:len(out)]);
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
new_c_string :: proc(s: string) -> ^u8 {
|
||||
c := make([]u8, len(s)+1);
|
||||
copy(c, []u8(s));
|
||||
c[len(s)] = 0;
|
||||
return &c[0];
|
||||
}
|
||||
|
||||
to_odin_string :: proc(c: ^u8) -> string {
|
||||
len := 0;
|
||||
for (c+len)^ != 0 {
|
||||
len++;
|
||||
}
|
||||
return string(slice_ptr(c, len));
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package strings
|
||||
|
||||
import "core:mem"
|
||||
|
||||
new_string :: proc(s: string) -> string {
|
||||
c := make([]byte, len(s)+1);
|
||||
copy(c, cast([]byte)s);
|
||||
c[len(s)] = 0;
|
||||
return string(c[:len(s)]);
|
||||
}
|
||||
|
||||
new_cstring :: proc(s: string) -> cstring {
|
||||
c := make([]byte, len(s)+1);
|
||||
copy(c, cast([]byte)s);
|
||||
c[len(s)] = 0;
|
||||
return cstring(&c[0]);
|
||||
}
|
||||
|
||||
@(deprecated="Please use a standard cast for cstring to string")
|
||||
to_odin_string :: proc(str: cstring) -> string {
|
||||
return string(str);
|
||||
}
|
||||
|
||||
string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
|
||||
return transmute(string)mem.Raw_String{ptr, len};
|
||||
}
|
||||
|
||||
contains_rune :: proc(s: string, r: rune) -> int {
|
||||
for c, offset in s {
|
||||
if c == r do return offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#load "sync_windows.odin" when ODIN_OS == "windows";
|
||||
#load "sync_linux.odin" when ODIN_OS == "linux";
|
||||
@@ -0,0 +1,184 @@
|
||||
package sync
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
Ordering :: enum {
|
||||
Relaxed, // Monotonic
|
||||
Release,
|
||||
Acquire,
|
||||
Acquire_Release,
|
||||
Sequentially_Consistent,
|
||||
}
|
||||
|
||||
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return Relaxed;
|
||||
case Release: return Relaxed;
|
||||
case Acquire: return Acquire;
|
||||
case Acquire_Release: return Acquire;
|
||||
case Sequentially_Consistent: return Sequentially_Consistent;
|
||||
}
|
||||
return Relaxed;
|
||||
}
|
||||
|
||||
fence :: inline proc "contextless" (order: Ordering) {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: panic("there is no such thing as a relaxed fence");
|
||||
case Release: intrinsics.atomic_fence_rel();
|
||||
case Acquire: intrinsics.atomic_fence_acq();
|
||||
case Acquire_Release: intrinsics.atomic_fence_acqrel();
|
||||
case Sequentially_Consistent: intrinsics.atomic_fence();
|
||||
case: panic("unknown order");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: intrinsics.atomic_store_relaxed(dst, val);
|
||||
case Release: intrinsics.atomic_store_rel(dst, val);
|
||||
case Sequentially_Consistent: intrinsics.atomic_store(dst, val);
|
||||
case Acquire: panic("there is not such thing as an acquire store");
|
||||
case Acquire_Release: panic("there is not such thing as an acquire/release store");
|
||||
case: panic("unknown order");
|
||||
}
|
||||
}
|
||||
|
||||
atomic_load :: inline proc "contextless" (dst: ^$T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_load_relaxed(dst);
|
||||
case Acquire: return intrinsics.atomic_load_acq(dst);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_load(dst);
|
||||
case Release: panic("there is no such thing as a release load");
|
||||
case Acquire_Release: panic("there is no such thing as an acquire/release load");
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_xchg_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_xchg_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_xchg_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, success, failure: Ordering) -> (val: T, ok: bool) {
|
||||
using Ordering;
|
||||
switch failure {
|
||||
case Relaxed:
|
||||
switch success {
|
||||
case Relaxed: return intrinsics.atomic_cxchg_relaxed(dst, old, new);
|
||||
case Acquire: return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
|
||||
case Acquire_Release: return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
|
||||
case: panic("an unknown ordering combination");
|
||||
}
|
||||
case Acquire:
|
||||
switch success {
|
||||
case Acquire: return intrinsics.atomic_cxchg_acq(dst, old, new);
|
||||
case: panic("an unknown ordering combination");
|
||||
}
|
||||
case Sequentially_Consistent:
|
||||
switch success {
|
||||
case Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
|
||||
case: panic("an unknown ordering combination");
|
||||
}
|
||||
case Acquire_Release:
|
||||
panic("there is not such thing as an acquire/release failure ordering");
|
||||
case Release:
|
||||
panic("there is not such thing as an release failure ordering");
|
||||
}
|
||||
return T{}, false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_add_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_add_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_add_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_sub_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_sub_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_sub_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_and_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_and_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_and_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_nand_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_nand_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_nand_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_or_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_or_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_or_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
|
||||
using Ordering;
|
||||
#complete switch order {
|
||||
case Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
|
||||
case Release: return intrinsics.atomic_xor_rel(dst, val);
|
||||
case Acquire: return intrinsics.atomic_xor_acq(dst, val);
|
||||
case Acquire_Release: return intrinsics.atomic_xor_acqrel(dst, val);
|
||||
case Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
|
||||
}
|
||||
panic("unknown order");
|
||||
return T{};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#import "atomics.odin";
|
||||
#import "os.odin";
|
||||
package sync
|
||||
|
||||
/*
|
||||
|
||||
import "core:atomics"
|
||||
import "core:os"
|
||||
|
||||
Semaphore :: struct {
|
||||
// _handle: win32.Handle,
|
||||
@@ -28,7 +32,7 @@ semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
// win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
|
||||
}
|
||||
|
||||
semaphore_release :: proc(s: ^Semaphore) #inline {
|
||||
semaphore_release :: inline proc(s: ^Semaphore) {
|
||||
semaphore_post(s, 1);
|
||||
}
|
||||
|
||||
@@ -54,7 +58,7 @@ mutex_lock :: proc(m: ^Mutex) {
|
||||
}
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
m._recursion++;
|
||||
m._recursion += 1;
|
||||
}
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
thread_id := current_thread_id();
|
||||
@@ -70,7 +74,7 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
}
|
||||
m._recursion++;
|
||||
m._recursion += 1;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
@@ -78,7 +82,7 @@ mutex_unlock :: proc(m: ^Mutex) {
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomics.load(&m._owner));
|
||||
|
||||
m._recursion--;
|
||||
m._recursion -= 1;
|
||||
recursion = m._recursion;
|
||||
if recursion == 0 {
|
||||
atomics.store(&m._owner, thread_id);
|
||||
@@ -91,3 +95,4 @@ mutex_unlock :: proc(m: ^Mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,83 @@
|
||||
package sync
|
||||
|
||||
import "core:sys/win32"
|
||||
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle,
|
||||
}
|
||||
|
||||
Mutex :: struct {
|
||||
_critical_section: win32.Critical_Section,
|
||||
}
|
||||
|
||||
Condition :: struct {
|
||||
event: win32.Handle,
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return i32(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
s._handle = win32.create_semaphore_w(nil, 0, 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.close_handle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
win32.release_semaphore(s._handle, i32(count), nil);
|
||||
}
|
||||
|
||||
semaphore_release :: inline proc(s: ^Semaphore) {
|
||||
semaphore_post(s, 1);
|
||||
}
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
result := win32.wait_for_single_object(s._handle, win32.INFINITE);
|
||||
assert(result != win32.WAIT_FAILED);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
|
||||
win32.initialize_critical_section_and_spin_count(&m._critical_section, u32(spin_count));
|
||||
}
|
||||
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
win32.delete_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
win32.enter_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
return bool(win32.try_enter_critical_section(&m._critical_section));
|
||||
}
|
||||
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
win32.leave_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
|
||||
condition_init :: proc(using c: ^Condition) {
|
||||
event = win32.create_event_w(nil, false, false, nil);
|
||||
assert(event != nil);
|
||||
}
|
||||
|
||||
condition_signal :: proc(using c: ^Condition) {
|
||||
ok := win32.set_event(event);
|
||||
assert(bool(ok));
|
||||
}
|
||||
|
||||
condition_wait_for :: proc(using c: ^Condition) {
|
||||
result := win32.wait_for_single_object(event, win32.INFINITE);
|
||||
assert(result != win32.WAIT_FAILED);
|
||||
}
|
||||
|
||||
condition_destroy :: proc(using c: ^Condition) {
|
||||
if event != nil {
|
||||
win32.close_handle(event);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "atomics.odin";
|
||||
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle,
|
||||
}
|
||||
|
||||
Mutex :: struct {
|
||||
_semaphore: Semaphore,
|
||||
_counter: i32,
|
||||
_owner: i32,
|
||||
_recursion: i32,
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return i32(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
s._handle = win32.create_semaphore_a(nil, 0, 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.close_handle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
win32.release_semaphore(s._handle, i32(count), nil);
|
||||
}
|
||||
|
||||
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
win32.wait_for_single_object(s._handle, win32.INFINITE);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex) {
|
||||
atomics.store(&m._counter, 0);
|
||||
atomics.store(&m._owner, current_thread_id());
|
||||
semaphore_init(&m._semaphore);
|
||||
m._recursion = 0;
|
||||
}
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
semaphore_destroy(&m._semaphore);
|
||||
}
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
thread_id := current_thread_id();
|
||||
if atomics.fetch_add(&m._counter, 1) > 0 {
|
||||
if thread_id != atomics.load(&m._owner) {
|
||||
semaphore_wait(&m._semaphore);
|
||||
}
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
m._recursion++;
|
||||
}
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
thread_id := current_thread_id();
|
||||
if atomics.load(&m._owner) == thread_id {
|
||||
atomics.fetch_add(&m._counter, 1);
|
||||
} else {
|
||||
expected: i32 = 0;
|
||||
if atomics.load(&m._counter) != 0 {
|
||||
return false;
|
||||
}
|
||||
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
|
||||
return false;
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
}
|
||||
m._recursion++;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomics.load(&m._owner));
|
||||
|
||||
m._recursion--;
|
||||
recursion = m._recursion;
|
||||
if recursion == 0 {
|
||||
atomics.store(&m._owner, thread_id);
|
||||
}
|
||||
|
||||
if atomics.fetch_add(&m._counter, -1) > 1 {
|
||||
if recursion == 0 {
|
||||
semaphore_release(&m._semaphore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.text)
|
||||
}
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
#foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
|
||||
#import . "windows.odin";
|
||||
|
||||
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 :: Handle;
|
||||
ColorRef :: u32;
|
||||
|
||||
LayerPlaneDescriptor :: 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: ColorRef,
|
||||
}
|
||||
|
||||
PointFloat :: struct {
|
||||
x, y: f32,
|
||||
}
|
||||
|
||||
Glyph_MetricsFloat :: struct {
|
||||
black_box_x: f32,
|
||||
black_box_y: f32,
|
||||
glyph_origin: PointFloat,
|
||||
cell_inc_x: f32,
|
||||
cell_inc_y: f32,
|
||||
}
|
||||
|
||||
CreateContextAttribsARBType :: #type proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
|
||||
ChoosePixelFormatARBType :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
|
||||
SwapIntervalEXTType :: #type proc(interval: i32) -> bool #cc_c;
|
||||
GetExtensionsStringARBType :: #type proc(Hdc) -> ^u8 #cc_c;
|
||||
|
||||
|
||||
create_context_attribs_arb: CreateContextAttribsARBType;
|
||||
choose_pixel_format_arb: ChoosePixelFormatARBType;
|
||||
swap_interval_ext: SwapIntervalEXTType;
|
||||
get_extensions_string_arb: GetExtensionsStringARBType;
|
||||
|
||||
|
||||
|
||||
create_context :: proc(hdc: Hdc) -> Hglrc #foreign opengl32 "wglCreateContext";
|
||||
make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #foreign opengl32 "wglMakeCurrent";
|
||||
get_proc_address :: proc(c_str: ^u8) -> Proc #foreign opengl32 "wglGetProcAddress";
|
||||
delete_context :: proc(hglrc: Hglrc) -> Bool #foreign opengl32 "wglDeleteContext";
|
||||
copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool #foreign opengl32 "wglCopyContext";
|
||||
create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #foreign opengl32 "wglCreateLayerContext";
|
||||
describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^LayerPlaneDescriptor) -> Bool #foreign opengl32 "wglDescribeLayerPlane";
|
||||
get_current_context :: proc() -> Hglrc #foreign opengl32 "wglGetCurrentContext";
|
||||
get_current_dc :: proc() -> Hdc #foreign opengl32 "wglGetCurrentDC";
|
||||
get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries";
|
||||
realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #foreign opengl32 "wglRealizeLayerPalette";
|
||||
set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries";
|
||||
share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #foreign opengl32 "wglShareLists";
|
||||
swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool #foreign opengl32 "wglSwapLayerBuffers";
|
||||
use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #foreign opengl32 "wglUseFontBitmaps";
|
||||
use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_MetricsFloat) -> Bool #foreign opengl32 "wglUseFontOutlines";
|
||||
@@ -0,0 +1,114 @@
|
||||
// +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 ---;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,656 +0,0 @@
|
||||
#foreign_system_library "kernel32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "user32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "gdi32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "winmm.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "shell32.lib" when ODIN_OS == "windows";
|
||||
|
||||
Handle :: rawptr;
|
||||
Hwnd :: Handle;
|
||||
Hdc :: Handle;
|
||||
Hinstance :: Handle;
|
||||
Hicon :: Handle;
|
||||
Hcursor :: Handle;
|
||||
Hmenu :: Handle;
|
||||
Hbrush :: Handle;
|
||||
Hgdiobj :: Handle;
|
||||
Hmodule :: Handle;
|
||||
Wparam :: uint;
|
||||
Lparam :: int;
|
||||
Lresult :: int;
|
||||
Bool :: i32;
|
||||
WndProc :: #type proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
|
||||
|
||||
|
||||
INVALID_HANDLE :: Handle(~int(0));
|
||||
|
||||
FALSE: Bool : 0;
|
||||
TRUE: Bool : 1;
|
||||
|
||||
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_CAPTION :: 0x00C00000;
|
||||
WS_VISIBLE :: 0x10000000;
|
||||
WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
|
||||
|
||||
WM_DESTROY :: 0x0002;
|
||||
WM_SIZE :: 0x0005;
|
||||
WM_CLOSE :: 0x0010;
|
||||
WM_ACTIVATEAPP :: 0x001C;
|
||||
WM_QUIT :: 0x0012;
|
||||
WM_KEYDOWN :: 0x0100;
|
||||
WM_KEYUP :: 0x0101;
|
||||
WM_SIZING :: 0x0214;
|
||||
WM_MOUSEWHEEL :: 0x020A;
|
||||
WM_SYSKEYDOWN :: 0x0104;
|
||||
WM_WINDOWPOSCHANGED :: 0x0047;
|
||||
WM_SETCURSOR :: 0x0020;
|
||||
WM_CHAR :: 0x0102;
|
||||
|
||||
PM_NOREMOVE :: 0x0000;
|
||||
PM_REMOVE :: 0x0001;
|
||||
PM_NOYIELD :: 0x0002;
|
||||
|
||||
COLOR_BACKGROUND :: Hbrush(int(1));
|
||||
BLACK_BRUSH :: 4;
|
||||
|
||||
SM_CXSCREEN :: 0;
|
||||
SM_CYSCREEN :: 1;
|
||||
|
||||
SW_SHOW :: 5;
|
||||
|
||||
|
||||
Point :: struct #ordered {
|
||||
x, y: i32,
|
||||
}
|
||||
|
||||
WndClassExA :: struct #ordered {
|
||||
size, style: u32,
|
||||
wnd_proc: WndProc,
|
||||
cls_extra, wnd_extra: i32,
|
||||
instance: Hinstance,
|
||||
icon: Hicon,
|
||||
cursor: Hcursor,
|
||||
background: Hbrush,
|
||||
menu_name, class_name: ^u8,
|
||||
sm: Hicon,
|
||||
}
|
||||
|
||||
Msg :: struct #ordered {
|
||||
hwnd: Hwnd,
|
||||
message: u32,
|
||||
wparam: Wparam,
|
||||
lparam: Lparam,
|
||||
time: u32,
|
||||
pt: Point,
|
||||
}
|
||||
|
||||
Rect :: struct #ordered {
|
||||
left: i32,
|
||||
top: i32,
|
||||
right: i32,
|
||||
bottom: i32,
|
||||
}
|
||||
|
||||
Filetime :: struct #ordered {
|
||||
lo, hi: u32,
|
||||
}
|
||||
|
||||
Systemtime :: struct #ordered {
|
||||
year, month: u16,
|
||||
day_of_week, day: u16,
|
||||
hour, minute, second, millisecond: u16,
|
||||
}
|
||||
|
||||
ByHandleFileInformation :: struct #ordered {
|
||||
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,
|
||||
}
|
||||
|
||||
FileAttributeData :: struct #ordered {
|
||||
file_attributes: u32,
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: Filetime,
|
||||
file_size_high,
|
||||
file_size_low: u32,
|
||||
}
|
||||
|
||||
FindData :: struct #ordered {
|
||||
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]u8,
|
||||
alternate_file_name : [14]u8,
|
||||
}
|
||||
|
||||
|
||||
GET_FILEEX_INFO_LEVELS :: i32;
|
||||
|
||||
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0;
|
||||
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
|
||||
|
||||
get_last_error :: proc() -> i32 #foreign kernel32 "GetLastError";
|
||||
exit_process :: proc(exit_code: u32) #foreign kernel32 "ExitProcess";
|
||||
get_desktop_window :: proc() -> Hwnd #foreign user32 "GetDesktopWindow";
|
||||
show_cursor :: proc(show : Bool) #foreign user32 "ShowCursor";
|
||||
get_cursor_pos :: proc(p: ^Point) -> i32 #foreign user32 "GetCursorPos";
|
||||
screen_to_client :: proc(h: Hwnd, p: ^Point) -> i32 #foreign user32 "ScreenToClient";
|
||||
get_module_handle_a :: proc(module_name: ^u8) -> Hinstance #foreign kernel32 "GetModuleHandleA";
|
||||
get_stock_object :: proc(fn_object: i32) -> Hgdiobj #foreign gdi32 "GetStockObject";
|
||||
post_quit_message :: proc(exit_code: i32) #foreign user32 "PostQuitMessage";
|
||||
set_window_text_a :: proc(hwnd: Hwnd, c_string: ^u8) -> Bool #foreign user32 "SetWindowTextA";
|
||||
|
||||
query_performance_frequency :: proc(result: ^i64) -> i32 #foreign kernel32 "QueryPerformanceFrequency";
|
||||
query_performance_counter :: proc(result: ^i64) -> i32 #foreign kernel32 "QueryPerformanceCounter";
|
||||
|
||||
sleep :: proc(ms: i32) -> i32 #foreign kernel32 "Sleep";
|
||||
|
||||
output_debug_string_a :: proc(c_str: ^u8) #foreign kernel32 "OutputDebugStringA";
|
||||
|
||||
|
||||
register_class_ex_a :: proc(wc: ^WndClassExA) -> i16 #foreign user32 "RegisterClassExA";
|
||||
create_window_ex_a :: proc(ex_style: u32,
|
||||
class_name, title: ^u8,
|
||||
style: u32,
|
||||
x, y, w, h: i32,
|
||||
parent: Hwnd, menu: Hmenu, instance: Hinstance,
|
||||
param: rawptr) -> Hwnd #foreign user32 "CreateWindowExA";
|
||||
|
||||
show_window :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool #foreign user32 "ShowWindow";
|
||||
translate_message :: proc(msg: ^Msg) -> Bool #foreign user32 "TranslateMessage";
|
||||
dispatch_message_a :: proc(msg: ^Msg) -> Lresult #foreign user32 "DispatchMessageA";
|
||||
update_window :: proc(hwnd: Hwnd) -> Bool #foreign user32 "UpdateWindow";
|
||||
get_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max : u32) -> Bool #foreign user32 "GetMessageA";
|
||||
peek_message_a :: proc(msg: ^Msg, hwnd: Hwnd,
|
||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool #foreign user32 "PeekMessageA";
|
||||
|
||||
post_message :: proc(hwnd: Hwnd, msg, wparam, lparam : u32) -> Bool #foreign user32 "PostMessageA";
|
||||
|
||||
def_window_proc_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult #foreign user32 "DefWindowProcA";
|
||||
|
||||
adjust_window_rect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool #foreign user32 "AdjustWindowRect";
|
||||
get_active_window :: proc() -> Hwnd #foreign user32 "GetActiveWindow";
|
||||
|
||||
destroy_window :: proc(wnd: Hwnd) -> Bool #foreign user32 "DestroyWindow";
|
||||
describe_pixel_format :: proc(dc: Hdc, pixel_format: i32, bytes : u32, pfd: ^PixelFormatDescriptor) -> i32 #foreign user32 "DescribePixelFormat";
|
||||
|
||||
|
||||
get_query_performance_frequency :: proc() -> i64 {
|
||||
r: i64;
|
||||
query_performance_frequency(&r);
|
||||
return r;
|
||||
}
|
||||
|
||||
get_command_line_a :: proc() -> ^u8 #foreign kernel32 "GetCommandLineA";
|
||||
get_command_line_w :: proc() -> ^u16 #foreign kernel32 "GetCommandLineW";
|
||||
get_system_metrics :: proc(index: i32) -> i32 #foreign kernel32 "GetSystemMetrics";
|
||||
get_current_thread_id :: proc() -> u32 #foreign kernel32 "GetCurrentThreadId";
|
||||
command_line_to_argv_w :: proc(cmd_list: ^u16, num_args: ^i32) -> ^^u16 #foreign shell32 "CommandLineToArgvW";
|
||||
|
||||
time_get_time :: proc() -> u32 #foreign winmm "timeGetTime";
|
||||
get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) #foreign kernel32 "GetSystemTimeAsFileTime";
|
||||
file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool #foreign kernel32 "FileTimeToLocalFileTime";
|
||||
file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool #foreign kernel32 "FileTimeToSystemTime";
|
||||
system_time_to_file_time :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool #foreign kernel32 "SystemTimeToFileTime";
|
||||
|
||||
// File Stuff
|
||||
|
||||
close_handle :: proc(h: Handle) -> i32 #foreign kernel32 "CloseHandle";
|
||||
get_std_handle :: proc(h: i32) -> Handle #foreign kernel32 "GetStdHandle";
|
||||
create_file_a :: proc(filename: ^u8, desired_access, share_mode: u32,
|
||||
security: rawptr,
|
||||
creation, flags_and_attribs: u32, template_file: Handle) -> Handle #foreign kernel32 "CreateFileA";
|
||||
read_file :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool #foreign kernel32 "ReadFile";
|
||||
write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool #foreign kernel32 "WriteFile";
|
||||
|
||||
get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool #foreign kernel32 "GetFileSizeEx";
|
||||
get_file_attributes_a :: proc(filename: ^u8) -> u32 #foreign kernel32 "GetFileAttributesA";
|
||||
get_file_attributes_ex_a :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool #foreign kernel32 "GetFileAttributesExA";
|
||||
get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^ByHandleFileInformation) -> Bool #foreign kernel32 "GetFileInformationByHandle";
|
||||
|
||||
get_file_type :: proc(file_handle: Handle) -> u32 #foreign kernel32 "GetFileType";
|
||||
set_file_pointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32 "SetFilePointer";
|
||||
|
||||
set_handle_information :: proc(obj: Handle, mask, flags: u32) -> Bool #foreign kernel32 "SetHandleInformation";
|
||||
|
||||
find_first_file_a :: proc(file_name : ^u8, data : ^FindData) -> Handle #foreign kernel32 "FindFirstFileA";
|
||||
find_next_file_a :: proc(file : Handle, data : ^FindData) -> Bool #foreign kernel32 "FindNextFileA";
|
||||
find_close :: proc(file : Handle) -> Bool #foreign kernel32 "FindClose";
|
||||
|
||||
MAX_PATH :: 0x00000104;
|
||||
|
||||
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_APPEND_DATA :: 0x0004;
|
||||
|
||||
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;
|
||||
|
||||
INVALID_SET_FILE_POINTER :: ~u32(0);
|
||||
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc (h: Handle, flags: u32, bytes: int) -> rawptr #foreign kernel32 "HeapAlloc";
|
||||
heap_realloc :: proc (h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32 "HeapReAlloc";
|
||||
heap_free :: proc (h: Handle, flags: u32, memory: rawptr) -> Bool #foreign kernel32 "HeapFree";
|
||||
get_process_heap :: proc () -> Handle #foreign kernel32 "GetProcessHeap";
|
||||
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008;
|
||||
|
||||
// Synchronization
|
||||
|
||||
Security_Attributes :: struct #ordered {
|
||||
length: u32,
|
||||
security_descriptor: rawptr,
|
||||
inherit_handle: Bool,
|
||||
}
|
||||
|
||||
INFINITE :: 0xffffffff;
|
||||
|
||||
create_semaphore_a :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^u8) -> Handle #foreign kernel32 "CreateSemaphoreA";
|
||||
release_semaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool #foreign kernel32 "ReleaseSemaphore";
|
||||
wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 #foreign kernel32 "WaitForSingleObject";
|
||||
|
||||
|
||||
interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32 "InterlockedCompareExchange";
|
||||
interlocked_exchange :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32 "InterlockedExchange";
|
||||
interlocked_exchange_add :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32 "InterlockedExchangeAdd";
|
||||
interlocked_and :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32 "InterlockedAnd";
|
||||
interlocked_or :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32 "InterlockedOr";
|
||||
|
||||
interlocked_compare_exchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 #foreign kernel32 "InterlockedCompareExchange64";
|
||||
interlocked_exchange64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32 "InterlockedExchange64";
|
||||
interlocked_exchange_add64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32 "InterlockedExchangeAdd64";
|
||||
interlocked_and64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32 "InterlockedAnd64";
|
||||
interlocked_or64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32 "InterlockedOr64";
|
||||
|
||||
mm_pause :: proc() #foreign kernel32 "_mm_pause";
|
||||
read_write_barrier :: proc() #foreign kernel32 "ReadWriteBarrier";
|
||||
write_barrier :: proc() #foreign kernel32 "WriteBarrier";
|
||||
read_barrier :: proc() #foreign kernel32 "ReadBarrier";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Hmonitor :: Handle;
|
||||
|
||||
GWL_STYLE :: -16;
|
||||
|
||||
Hwnd_TOP :: Hwnd(uint(0));
|
||||
|
||||
MONITOR_DEFAULTTONULL :: 0x00000000;
|
||||
MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
|
||||
MONITOR_DEFAULTTONEAREST :: 0x00000002;
|
||||
|
||||
SWP_FRAMECHANGED :: 0x0020;
|
||||
SWP_NOOWNERZORDER :: 0x0200;
|
||||
SWP_NOZORDER :: 0x0004;
|
||||
SWP_NOSIZE :: 0x0001;
|
||||
SWP_NOMOVE :: 0x0002;
|
||||
|
||||
|
||||
MonitorInfo :: struct #ordered {
|
||||
size: u32,
|
||||
monitor: Rect,
|
||||
work: Rect,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
WindowPlacement :: struct #ordered {
|
||||
length: u32,
|
||||
flags: u32,
|
||||
show_cmd: u32,
|
||||
min_pos: Point,
|
||||
max_pos: Point,
|
||||
normal_pos: Rect,
|
||||
}
|
||||
|
||||
get_monitor_info_a :: proc(monitor: Hmonitor, mi: ^MonitorInfo) -> Bool #foreign user32 "GetMonitorInfoA";
|
||||
monitor_from_window :: proc(wnd: Hwnd, flags : u32) -> Hmonitor #foreign user32 "MonitorFromWindow";
|
||||
|
||||
set_window_pos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
|
||||
|
||||
get_window_placement :: proc(wnd: Hwnd, wndpl: ^WindowPlacement) -> Bool #foreign user32 "GetWindowPlacement";
|
||||
set_window_placement :: proc(wnd: Hwnd, wndpl: ^WindowPlacement) -> Bool #foreign user32 "SetWindowPlacement";
|
||||
get_window_rect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool #foreign user32 "GetWindowRect";
|
||||
|
||||
get_window_long_ptr_a :: proc(wnd: Hwnd, index: i32) -> i64 #foreign user32 "GetWindowLongPtrA";
|
||||
set_window_long_ptr_a :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #foreign user32 "SetWindowLongPtrA";
|
||||
|
||||
get_window_text :: proc(wnd: Hwnd, str: ^u8, maxCount: i32) -> i32 #foreign user32 "GetWindowText";
|
||||
|
||||
HIWORD :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff); }
|
||||
HIWORD :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff); }
|
||||
LOWORD :: proc(wParam: Wparam) -> u16 { return u16(wParam); }
|
||||
LOWORD :: proc(lParam: Lparam) -> u16 { return u16(lParam); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BitmapInfoHeader :: struct #ordered {
|
||||
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,
|
||||
}
|
||||
BitmapInfo :: struct #ordered {
|
||||
using header: BitmapInfoHeader,
|
||||
colors: [1]RgbQuad,
|
||||
}
|
||||
|
||||
|
||||
RgbQuad :: struct #ordered { blue, green, red, reserved: u8 }
|
||||
|
||||
BI_RGB :: 0;
|
||||
DIB_RGB_COLORS :: 0x00;
|
||||
SRCCOPY: u32 : 0x00cc0020;
|
||||
|
||||
|
||||
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: ^BitmapInfo,
|
||||
usage: u32,
|
||||
rop: u32) -> i32 #foreign gdi32 "StretchDIBits";
|
||||
|
||||
|
||||
|
||||
load_library_a :: proc (c_str: ^u8) -> Hmodule #foreign kernel32 "LoadLibraryA";
|
||||
free_library :: proc (h: Hmodule) #foreign kernel32 "FreeLibrary";
|
||||
get_proc_address :: proc (h: Hmodule, c_str: ^u8) -> Proc #foreign kernel32 "GetProcAddress";
|
||||
|
||||
get_client_rect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool #foreign user32 "GetClientRect";
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
PixelFormatDescriptor :: struct #ordered {
|
||||
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: u8,
|
||||
|
||||
layer_mask,
|
||||
visible_mask,
|
||||
damage_mask: u32,
|
||||
}
|
||||
|
||||
get_dc :: proc(h: Hwnd) -> Hdc #foreign user32 "GetDC";
|
||||
set_pixel_format :: proc(hdc: Hdc, pixel_format: i32, pfd: ^PixelFormatDescriptor) -> Bool #foreign gdi32 "SetPixelFormat";
|
||||
choose_pixel_format :: proc(hdc: Hdc, pfd: ^PixelFormatDescriptor) -> i32 #foreign gdi32 "ChoosePixelFormat";
|
||||
swap_buffers :: proc(hdc: Hdc) -> Bool #foreign gdi32 "SwapBuffers";
|
||||
release_dc :: proc(wnd: Hwnd, hdc: Hdc) -> i32 #foreign user32 "ReleaseDC";
|
||||
|
||||
|
||||
Proc :: #type proc() #cc_c;
|
||||
|
||||
|
||||
get_key_state :: proc(v_key: i32) -> i16 #foreign user32 "GetKeyState";
|
||||
get_async_key_state :: proc(v_key: i32) -> i16 #foreign user32 "GetAsyncKeyState";
|
||||
|
||||
is_key_down :: proc(key: KeyCode) -> bool #inline { return get_async_key_state(i32(key)) < 0; }
|
||||
|
||||
KeyCode :: enum i32 {
|
||||
Lbutton = 0x01,
|
||||
Rbutton = 0x02,
|
||||
Cancel = 0x03,
|
||||
Mbutton = 0x04,
|
||||
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,
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package thread
|
||||
|
||||
import "core:runtime"
|
||||
import "core:sys/win32"
|
||||
|
||||
Thread_Proc :: #type proc(^Thread) -> int;
|
||||
|
||||
Thread_Os_Specific :: struct {
|
||||
win32_thread: win32.Handle,
|
||||
win32_thread_id: u32,
|
||||
}
|
||||
|
||||
Thread :: struct {
|
||||
using specific: Thread_Os_Specific,
|
||||
procedure: Thread_Proc,
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
|
||||
init_context: runtime.Context,
|
||||
use_init_context: bool,
|
||||
}
|
||||
|
||||
|
||||
create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||
win32_thread_id: u32;
|
||||
|
||||
__windows_thread_entry_proc :: proc "c" (t: ^Thread) -> i32 {
|
||||
c := context;
|
||||
if t.use_init_context {
|
||||
c = t.init_context;
|
||||
}
|
||||
context = c;
|
||||
|
||||
return i32(t.procedure(t));
|
||||
}
|
||||
|
||||
|
||||
win32_thread_proc := rawptr(__windows_thread_entry_proc);
|
||||
thread := new(Thread);
|
||||
|
||||
win32_thread := win32.create_thread(nil, 0, win32_thread_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
|
||||
if win32_thread == nil {
|
||||
free(thread);
|
||||
return nil;
|
||||
}
|
||||
thread.procedure = procedure;
|
||||
thread.win32_thread = win32_thread;
|
||||
thread.win32_thread_id = win32_thread_id;
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
start :: proc(using thread: ^Thread) {
|
||||
win32.resume_thread(win32_thread);
|
||||
}
|
||||
|
||||
is_done :: proc(using thread: ^Thread) -> bool {
|
||||
res := win32.wait_for_single_object(win32_thread, 0);
|
||||
return res != win32.WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
join :: proc(using thread: ^Thread) {
|
||||
win32.wait_for_single_object(win32_thread, win32.INFINITE);
|
||||
win32.close_handle(win32_thread);
|
||||
win32_thread = win32.INVALID_HANDLE;
|
||||
}
|
||||
|
||||
destroy :: proc(thread: ^Thread) {
|
||||
join(thread);
|
||||
free(thread);
|
||||
}
|
||||
|
||||
terminate :: proc(using thread : ^Thread, exit_code : u32) {
|
||||
win32.terminate_thread(win32_thread, exit_code);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
is_signed :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
match i in type_info_base(info) {
|
||||
case TypeInfo.Integer: return i.signed;
|
||||
case TypeInfo.Float: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_integer :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Integer);
|
||||
return ok;
|
||||
}
|
||||
is_float :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Float);
|
||||
return ok;
|
||||
}
|
||||
is_complex :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Complex);
|
||||
return ok;
|
||||
}
|
||||
is_any :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Any);
|
||||
return ok;
|
||||
}
|
||||
is_string :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.String);
|
||||
return ok;
|
||||
}
|
||||
is_boolean :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Boolean);
|
||||
return ok;
|
||||
}
|
||||
is_pointer :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Pointer);
|
||||
return ok;
|
||||
}
|
||||
is_procedure :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Procedure);
|
||||
return ok;
|
||||
}
|
||||
is_array :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_array :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.DynamicArray);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_map :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Map);
|
||||
return ok;
|
||||
}
|
||||
is_slice :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Slice);
|
||||
return ok;
|
||||
}
|
||||
is_vector :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Vector);
|
||||
return ok;
|
||||
}
|
||||
is_tuple :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Tuple);
|
||||
return ok;
|
||||
}
|
||||
is_struct :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Struct);
|
||||
return ok;
|
||||
}
|
||||
is_union :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Union);
|
||||
return ok;
|
||||
}
|
||||
is_raw_union :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.RawUnion);
|
||||
return ok;
|
||||
}
|
||||
is_enum :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^TypeInfo.Enum);
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package types
|
||||
|
||||
import rt "core:runtime"
|
||||
|
||||
are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
if a == b do return true;
|
||||
|
||||
if (a == nil && b != nil) ||
|
||||
(a != nil && b == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
switch {
|
||||
case a.size != b.size, a.align != b.align:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch x in a.variant {
|
||||
case rt.Type_Info_Named:
|
||||
y, ok := b.variant.(rt.Type_Info_Named);
|
||||
if !ok do return false;
|
||||
return x.base == y.base;
|
||||
|
||||
case rt.Type_Info_Integer:
|
||||
y, ok := b.variant.(rt.Type_Info_Integer);
|
||||
if !ok do return false;
|
||||
return x.signed == y.signed;
|
||||
|
||||
case rt.Type_Info_Rune:
|
||||
_, ok := b.variant.(rt.Type_Info_Rune);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Float:
|
||||
_, ok := b.variant.(rt.Type_Info_Float);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Complex:
|
||||
_, ok := b.variant.(rt.Type_Info_Complex);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_String:
|
||||
_, ok := b.variant.(rt.Type_Info_String);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Boolean:
|
||||
_, ok := b.variant.(rt.Type_Info_Boolean);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Any:
|
||||
_, ok := b.variant.(rt.Type_Info_Any);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Pointer:
|
||||
y, ok := b.variant.(rt.Type_Info_Pointer);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Procedure:
|
||||
y, ok := b.variant.(rt.Type_Info_Procedure);
|
||||
if !ok do return false;
|
||||
switch {
|
||||
case x.variadic != y.variadic,
|
||||
x.convention != y.convention:
|
||||
return false;
|
||||
}
|
||||
|
||||
return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
|
||||
|
||||
case rt.Type_Info_Array:
|
||||
y, ok := b.variant.(rt.Type_Info_Array);
|
||||
if !ok do return false;
|
||||
if x.count != y.count do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Dynamic_Array:
|
||||
y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Slice:
|
||||
y, ok := b.variant.(rt.Type_Info_Slice);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Tuple:
|
||||
y, ok := b.variant.(rt.Type_Info_Tuple);
|
||||
if !ok do return false;
|
||||
if len(x.types) != len(y.types) do return false;
|
||||
for _, i in x.types {
|
||||
xt, yt := x.types[i], y.types[i];
|
||||
if !are_types_identical(xt, yt) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case rt.Type_Info_Struct:
|
||||
y, ok := b.variant.(rt.Type_Info_Struct);
|
||||
if !ok do return false;
|
||||
switch {
|
||||
case len(x.types) != len(y.types),
|
||||
x.is_packed != y.is_packed,
|
||||
x.is_raw_union != y.is_raw_union,
|
||||
x.custom_align != y.custom_align:
|
||||
return false;
|
||||
}
|
||||
for _, i in x.types {
|
||||
xn, yn := x.names[i], y.names[i];
|
||||
xt, yt := x.types[i], y.types[i];
|
||||
|
||||
if xn != yn do return false;
|
||||
if !are_types_identical(xt, yt) do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case rt.Type_Info_Union:
|
||||
y, ok := b.variant.(rt.Type_Info_Union);
|
||||
if !ok do return false;
|
||||
if len(x.variants) != len(y.variants) do return false;
|
||||
|
||||
for _, i in x.variants {
|
||||
xv, yv := x.variants[i], y.variants[i];
|
||||
if !are_types_identical(xv, yv) do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case rt.Type_Info_Enum:
|
||||
// NOTE(bill): Should be handled above
|
||||
return false;
|
||||
|
||||
case rt.Type_Info_Map:
|
||||
y, ok := b.variant.(rt.Type_Info_Map);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
|
||||
|
||||
case rt.Type_Info_Bit_Field:
|
||||
y, ok := b.variant.(rt.Type_Info_Bit_Field);
|
||||
if !ok do return false;
|
||||
if len(x.names) != len(y.names) do return false;
|
||||
|
||||
for _, i in x.names {
|
||||
xb, yb := x.bits[i], y.bits[i];
|
||||
xo, yo := x.offsets[i], y.offsets[i];
|
||||
xn, yn := x.names[i], y.names[i];
|
||||
|
||||
if xb != yb do return false;
|
||||
if xo != yo do return false;
|
||||
if xn != yn do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case rt.Type_Info_Bit_Set:
|
||||
y, ok := b.variant.(rt.Type_Info_Bit_Set);
|
||||
if !ok do return false;
|
||||
return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper;
|
||||
|
||||
case rt.Type_Info_Opaque:
|
||||
y, ok := b.variant.(rt.Type_Info_Opaque);
|
||||
if !ok do return false;
|
||||
return x.elem == y.elem;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
is_signed :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
switch i in rt.type_info_base(info).variant {
|
||||
case rt.Type_Info_Integer: return i.signed;
|
||||
case rt.Type_Info_Float: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_integer :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Integer);
|
||||
return ok;
|
||||
}
|
||||
is_rune :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Rune);
|
||||
return ok;
|
||||
}
|
||||
is_float :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Float);
|
||||
return ok;
|
||||
}
|
||||
is_complex :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Complex);
|
||||
return ok;
|
||||
}
|
||||
is_any :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Any);
|
||||
return ok;
|
||||
}
|
||||
is_string :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_String);
|
||||
return ok;
|
||||
}
|
||||
is_boolean :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Boolean);
|
||||
return ok;
|
||||
}
|
||||
is_pointer :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Pointer);
|
||||
return ok;
|
||||
}
|
||||
is_procedure :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Procedure);
|
||||
return ok;
|
||||
}
|
||||
is_array :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_array :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Dynamic_Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_map :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Map);
|
||||
return ok;
|
||||
}
|
||||
is_slice :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Slice);
|
||||
return ok;
|
||||
}
|
||||
is_tuple :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Tuple);
|
||||
return ok;
|
||||
}
|
||||
is_struct :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
|
||||
return ok && !s.is_raw_union;
|
||||
}
|
||||
is_raw_union :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
|
||||
return ok && s.is_raw_union;
|
||||
}
|
||||
is_union :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Union);
|
||||
return ok;
|
||||
}
|
||||
is_enum :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Enum);
|
||||
return ok;
|
||||
}
|
||||
is_opaque :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Opaque);
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package utf16
|
||||
|
||||
REPLACEMENT_CHAR :: '\ufffd';
|
||||
MAX_RUNE :: '\U0010ffff';
|
||||
|
||||
_surr1 :: 0xd800;
|
||||
_surr2 :: 0xdc00;
|
||||
_surr3 :: 0xe000;
|
||||
_surr_self :: 0x10000;
|
||||
|
||||
|
||||
is_surrogate :: proc(r: rune) -> bool {
|
||||
return _surr1 <= r && r < _surr3;
|
||||
}
|
||||
|
||||
decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
|
||||
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
|
||||
return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self;
|
||||
}
|
||||
return REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
|
||||
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
|
||||
if r < _surr_self || r > MAX_RUNE {
|
||||
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
|
||||
}
|
||||
r -= _surr_self;
|
||||
return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff;
|
||||
}
|
||||
|
||||
encode :: proc(d: []u16, s: []rune) -> int {
|
||||
n, m := 0, len(d);
|
||||
loop: for r in s {
|
||||
switch r {
|
||||
case 0.._surr1-1, _surr3 .. _surr_self-1:
|
||||
if m+1 < n do break loop;
|
||||
d[n] = u16(r);
|
||||
n += 1;
|
||||
|
||||
case _surr_self .. MAX_RUNE:
|
||||
if m+2 < n do break loop;
|
||||
r1, r2 := encode_surrogate_pair(r);
|
||||
d[n] = u16(r1);
|
||||
d[n+1] = u16(r2);
|
||||
n += 2;
|
||||
|
||||
case:
|
||||
if m+1 < n do break loop;
|
||||
d[n] = u16(REPLACEMENT_CHAR);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
encode_string :: proc(d: []u16, s: string) -> int {
|
||||
n, m := 0, len(d);
|
||||
loop: for r in s {
|
||||
switch r {
|
||||
case 0.._surr1-1, _surr3 .. _surr_self-1:
|
||||
if m+1 < n do break loop;
|
||||
d[n] = u16(r);
|
||||
n += 1;
|
||||
|
||||
case _surr_self .. MAX_RUNE:
|
||||
if m+2 < n do break loop;
|
||||
r1, r2 := encode_surrogate_pair(r);
|
||||
d[n] = u16(r1);
|
||||
d[n+1] = u16(r2);
|
||||
n += 2;
|
||||
|
||||
case:
|
||||
if m+1 < n do break loop;
|
||||
d[n] = u16(REPLACEMENT_CHAR);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
package utf8
|
||||
|
||||
RUNE_ERROR :: '\ufffd';
|
||||
RUNE_SELF :: 0x80;
|
||||
RUNE_BOM :: 0xfeff;
|
||||
@@ -28,9 +30,9 @@ RUNE3_MAX :: 1<<16 - 1;
|
||||
LOCB :: 0b1000_0000;
|
||||
HICB :: 0b1011_1111;
|
||||
|
||||
AcceptRange :: struct { lo, hi: u8 }
|
||||
Accept_Range :: struct {lo, hi: u8};
|
||||
|
||||
immutable accept_ranges := [5]AcceptRange{
|
||||
accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0xbf},
|
||||
{0xa0, 0xbf},
|
||||
{0x80, 0x9f},
|
||||
@@ -38,7 +40,7 @@ immutable accept_ranges := [5]AcceptRange{
|
||||
{0x80, 0x8f},
|
||||
};
|
||||
|
||||
immutable accept_sizes := [256]u8{
|
||||
accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
@@ -61,7 +63,7 @@ immutable accept_sizes := [256]u8{
|
||||
encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||
buf: [4]u8;
|
||||
i := u32(r);
|
||||
mask: u8 : 0x3f;
|
||||
mask :: u8(0x3f);
|
||||
if i <= 1<<7-1 {
|
||||
buf[0] = u8(r);
|
||||
return buf, 1;
|
||||
@@ -92,7 +94,7 @@ encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||
return buf, 4;
|
||||
}
|
||||
|
||||
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune([]u8(s)); }
|
||||
decode_rune_from_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
|
||||
decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||
n := len(s);
|
||||
if n < 1 {
|
||||
@@ -132,7 +134,7 @@ decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||
|
||||
|
||||
|
||||
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune([]u8(s)); }
|
||||
decode_last_rune_from_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
|
||||
decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||
r: rune;
|
||||
size: int;
|
||||
@@ -151,16 +153,12 @@ decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||
|
||||
limit = max(end - UTF_MAX, 0);
|
||||
|
||||
start--;
|
||||
for start >= limit {
|
||||
if rune_start(s[start]) {
|
||||
break;
|
||||
}
|
||||
start--;
|
||||
for start-=1; start >= limit; start-=1 {
|
||||
if rune_start(s[start]) do break;
|
||||
}
|
||||
|
||||
start = max(start, 0);
|
||||
r, size = decode_rune(s[start..<end]);
|
||||
r, size = decode_rune(s[start:end]);
|
||||
if start+size != end {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
@@ -187,7 +185,7 @@ valid_string :: proc(s: string) -> bool {
|
||||
for i := 0; i < n; {
|
||||
si := s[i];
|
||||
if si < RUNE_SELF { // ascii
|
||||
i++;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
x := accept_sizes[si];
|
||||
@@ -215,28 +213,28 @@ valid_string :: proc(s: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
rune_start :: proc(b: u8) -> bool #inline { return b&0xc0 != 0x80; }
|
||||
rune_start :: inline proc(b: u8) -> bool do return b&0xc0 != 0x80;
|
||||
|
||||
rune_count :: proc(s: string) -> int #inline { return rune_count([]u8(s)); }
|
||||
rune_count_from_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
|
||||
rune_count :: proc(s: []u8) -> int {
|
||||
count := 0;
|
||||
n := len(s);
|
||||
|
||||
for i := 0; i < n; {
|
||||
defer count++;
|
||||
defer count += 1;
|
||||
si := s[i];
|
||||
if si < RUNE_SELF { // ascii
|
||||
i++;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
x := accept_sizes[si];
|
||||
if x == 0xf1 {
|
||||
i++;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
size := int(x & 7);
|
||||
if i+size > n {
|
||||
i++;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
ar := accept_ranges[x>>4];
|
||||
@@ -258,7 +256,7 @@ rune_count :: proc(s: []u8) -> int {
|
||||
|
||||
|
||||
rune_size :: proc(r: rune) -> int {
|
||||
match {
|
||||
switch {
|
||||
case r < 0: return -1;
|
||||
case r <= 1<<7 - 1: return 1;
|
||||
case r <= 1<<11 - 1: return 2;
|
||||
@@ -1,58 +0,0 @@
|
||||
REPLACEMENT_CHAR :: '\uFFFD';
|
||||
MAX_RUNE :: '\U0010FFFF';
|
||||
|
||||
_surr1 :: 0xd800;
|
||||
_surr2 :: 0xdc00;
|
||||
_surr3 :: 0xe000;
|
||||
_surr_self :: 0x10000;
|
||||
|
||||
|
||||
is_surrogate :: proc(r: rune) -> bool {
|
||||
return _surr1 <= r && r < _surr3;
|
||||
}
|
||||
|
||||
decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
|
||||
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
|
||||
return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self;
|
||||
}
|
||||
return REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
|
||||
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
|
||||
if r < _surr_self || r > MAX_RUNE {
|
||||
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
|
||||
}
|
||||
r -= _surr_self;
|
||||
return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff;
|
||||
}
|
||||
|
||||
encode :: proc(d: []u16, s: []rune) {
|
||||
n := len(s);
|
||||
for r in s {
|
||||
if r >= _surr_self {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
max_n := min(len(d), n);
|
||||
n = 0;
|
||||
|
||||
for r in s {
|
||||
match r {
|
||||
case 0..<_surr1, _surr3..<_surr_self:
|
||||
d[n] = u16(r);
|
||||
n++;
|
||||
|
||||
case _surr_self..MAX_RUNE:
|
||||
r1, r2 := encode_surrogate_pair(r);
|
||||
d[n] = u16(r1);
|
||||
d[n+1] = u16(r2);
|
||||
n += 2;
|
||||
|
||||
case:
|
||||
d[n] = u16(REPLACEMENT_CHAR);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,845 @@
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
import "core:mem"
|
||||
import "core:bits"
|
||||
import "core:hash"
|
||||
import "core:math"
|
||||
import "core:math/rand"
|
||||
import "core:os"
|
||||
import "core:sort"
|
||||
import "core:strings"
|
||||
import "core:types"
|
||||
import "core:unicode/utf16"
|
||||
import "core:unicode/utf8"
|
||||
import "core:c"
|
||||
import "core:runtime"
|
||||
|
||||
when os.OS == "windows" {
|
||||
import "core:thread"
|
||||
import "core:sys/win32"
|
||||
}
|
||||
|
||||
@(link_name="general_stuff")
|
||||
general_stuff :: proc() {
|
||||
fmt.println("# general_stuff");
|
||||
{ // `do` for inline statements rather than block
|
||||
foo :: proc() do fmt.println("Foo!");
|
||||
if false do foo();
|
||||
for false do foo();
|
||||
when false do foo();
|
||||
|
||||
if false do foo();
|
||||
else do foo();
|
||||
}
|
||||
|
||||
{ // Removal of `++` and `--` (again)
|
||||
x: int;
|
||||
x += 1;
|
||||
x -= 1;
|
||||
}
|
||||
{ // Casting syntaxes
|
||||
i := i32(137);
|
||||
ptr := &i;
|
||||
|
||||
_ = (^f32)(ptr);
|
||||
// ^f32(ptr) == ^(f32(ptr))
|
||||
_ = cast(^f32)ptr;
|
||||
|
||||
_ = (^f32)(ptr)^;
|
||||
_ = (cast(^f32)ptr)^;
|
||||
|
||||
// Questions: Should there be two ways to do it?
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove *_val_of built-in procedures
|
||||
* size_of, align_of, offset_of
|
||||
* type_of, type_info_of
|
||||
*/
|
||||
|
||||
{ // `expand_to_tuple` built-in procedure
|
||||
Foo :: struct {
|
||||
x: int,
|
||||
b: bool,
|
||||
}
|
||||
f := Foo{137, true};
|
||||
x, b := expand_to_tuple(f);
|
||||
fmt.println(f);
|
||||
fmt.println(x, b);
|
||||
fmt.println(expand_to_tuple(f));
|
||||
}
|
||||
|
||||
{
|
||||
// .. open range
|
||||
|
||||
for in 0..2 {} // 0, 1, 2
|
||||
}
|
||||
|
||||
{ // Multiple sized booleans
|
||||
|
||||
x0: bool; // default
|
||||
x1: b8 = true;
|
||||
x2: b16 = false;
|
||||
x3: b32 = true;
|
||||
x4: b64 = false;
|
||||
|
||||
fmt.printf("x0: %T = %v;\n", x0, x0);
|
||||
fmt.printf("x1: %T = %v;\n", x1, x1);
|
||||
fmt.printf("x2: %T = %v;\n", x2, x2);
|
||||
fmt.printf("x3: %T = %v;\n", x3, x3);
|
||||
fmt.printf("x4: %T = %v;\n", x4, x4);
|
||||
|
||||
// Having specific sized booleans is very useful when dealing with foreign code
|
||||
// and to enforce specific alignment for a boolean, especially within a struct
|
||||
}
|
||||
|
||||
{ // `distinct` types
|
||||
// Originally, all type declarations would create a distinct type unless #type_alias was present.
|
||||
// Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
|
||||
// If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct.
|
||||
|
||||
Int32 :: i32;
|
||||
#assert(Int32 == i32);
|
||||
|
||||
My_Int32 :: distinct i32;
|
||||
#assert(My_Int32 != i32);
|
||||
|
||||
My_Struct :: struct{x: int};
|
||||
#assert(My_Struct != struct{x: int});
|
||||
}
|
||||
|
||||
{
|
||||
X :: 123;
|
||||
when #defined(X) {
|
||||
fmt.println("X is defined");
|
||||
} else {
|
||||
fmt.println("X is not defined");
|
||||
}
|
||||
when #defined(Y) {
|
||||
fmt.println("Y is defined");
|
||||
} else {
|
||||
fmt.println("Y is not defined");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
union_type :: proc() {
|
||||
fmt.println("\n# union_type");
|
||||
{
|
||||
val: union{int, bool};
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
{
|
||||
// There is a duality between `any` and `union`
|
||||
// An `any` has a pointer to the data and allows for any type (open)
|
||||
// A `union` has as binary blob to store the data and allows only certain types (closed)
|
||||
// The following code is with `any` but has the same syntax
|
||||
val: any;
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 :: struct {x, y, z: f32};
|
||||
Quaternion :: struct {x, y, z, w: f32};
|
||||
|
||||
// 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!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
parametric_polymorphism :: proc() {
|
||||
fmt.println("# parametric_polymorphism");
|
||||
|
||||
print_value :: proc(value: $T) {
|
||||
fmt.printf("print_value: %T %v\n", value, value);
|
||||
}
|
||||
|
||||
v1: int = 1;
|
||||
v2: f32 = 2.1;
|
||||
v3: f64 = 3.14;
|
||||
v4: string = "message";
|
||||
|
||||
print_value(v1);
|
||||
print_value(v2);
|
||||
print_value(v3);
|
||||
print_value(v4);
|
||||
|
||||
fmt.println();
|
||||
|
||||
add :: proc(p, q: $T) -> T {
|
||||
x: T = p + q;
|
||||
return x;
|
||||
}
|
||||
|
||||
a := add(3, 4);
|
||||
fmt.printf("a: %T = %v\n", a, a);
|
||||
|
||||
b := add(3.2, 4.3);
|
||||
fmt.printf("b: %T = %v\n", b, b);
|
||||
|
||||
// This is how `new` is implemented
|
||||
alloc_type :: proc($T: 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));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = int(hash % u32(len(table.slots)));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
if index += 1; index >= len(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.count += 1;
|
||||
}
|
||||
|
||||
slot := &table.slots[index];
|
||||
slot.occupied = true;
|
||||
slot.hash = hash;
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
}
|
||||
|
||||
|
||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
||||
hash := get_hash(key);
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
return Value{}, false;
|
||||
}
|
||||
return table.slots[index].value, true;
|
||||
}
|
||||
|
||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
||||
if len(table.slots) <= 0 do return -1;
|
||||
|
||||
index := int(hash % u32(len(table.slots)));
|
||||
for table.slots[index].occupied {
|
||||
if table.slots[index].hash == hash {
|
||||
if table.slots[index].key == key {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if index += 1; index >= len(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_hash :: proc(s: string) -> u32 { // fnv32a
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..len(s)-1 {
|
||||
h = (h ~ u32(s[i])) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
table: Table(string, int);
|
||||
|
||||
for i in 0..36 do put(&table, "Hellope", i);
|
||||
for i in 0..42 do put(&table, "World!", i);
|
||||
|
||||
found, _ := find(&table, "Hellope");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
found, _ = find(&table, "World!");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
// I would not personally design a hash table like this in production
|
||||
// but this is a nice basic example
|
||||
// A better approach would either use a `u64` or equivalent for the key
|
||||
// and let the user specify the hashing function or make the user store
|
||||
// the hashing procedure with the table
|
||||
}
|
||||
|
||||
{ // Parametric polymorphic union
|
||||
Error :: enum {
|
||||
Foo0,
|
||||
Foo1,
|
||||
Foo2,
|
||||
Foo3,
|
||||
}
|
||||
Para_Union :: union(T: typeid) {T, Error};
|
||||
r: Para_Union(int);
|
||||
fmt.println(typeid_of(type_of(r)));
|
||||
|
||||
fmt.println(r);
|
||||
r = 123;
|
||||
fmt.println(r);
|
||||
r = Error.Foo0;
|
||||
fmt.println(r);
|
||||
}
|
||||
|
||||
{ // Polymorphic names
|
||||
foo :: proc($N: $I, $T: typeid) -> (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..N-1 {
|
||||
res[i] = i*i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
T :: int;
|
||||
array := foo(4, T);
|
||||
for v, i in array {
|
||||
assert(v == T(i*i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
prefix_table := [?]string{
|
||||
"White",
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Octarine",
|
||||
"Black",
|
||||
};
|
||||
|
||||
threading_example :: proc() {
|
||||
when os.OS == "windows" {
|
||||
fmt.println("# threading_example");
|
||||
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, len(array));
|
||||
n := len(array)-1;
|
||||
if index != n {
|
||||
array[index] = array[n];
|
||||
}
|
||||
pop(array);
|
||||
}
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, len(array));
|
||||
copy(array[index:], array[index+1:]);
|
||||
pop(array);
|
||||
}
|
||||
|
||||
worker_proc :: proc(t: ^thread.Thread) -> int {
|
||||
for iteration in 1..5 {
|
||||
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
|
||||
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
|
||||
// win32.sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
|
||||
defer delete(threads);
|
||||
|
||||
for in prefix_table {
|
||||
if t := thread.create(worker_proc); t != nil {
|
||||
t.init_context = context;
|
||||
t.use_init_context = true;
|
||||
t.user_index = len(threads);
|
||||
append(&threads, t);
|
||||
thread.start(t);
|
||||
}
|
||||
}
|
||||
|
||||
for len(threads) > 0 {
|
||||
for i := 0; i < len(threads); /**/ {
|
||||
if t := threads[i]; thread.is_done(t) {
|
||||
fmt.printf("Thread %d is done\n", t.user_index);
|
||||
thread.destroy(t);
|
||||
|
||||
ordered_remove(&threads, i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array_programming :: proc() {
|
||||
fmt.println("# array_programming");
|
||||
{
|
||||
a := [3]f32{1, 2, 3};
|
||||
b := [3]f32{5, 6, 7};
|
||||
c := a * b;
|
||||
d := a + b;
|
||||
e := 1 + (c - d) / 2;
|
||||
fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
|
||||
}
|
||||
|
||||
{
|
||||
a := [3]f32{1, 2, 3};
|
||||
b := swizzle(a, 2, 1, 0);
|
||||
assert(b == [3]f32{3, 2, 1});
|
||||
|
||||
c := swizzle(a, 0, 0);
|
||||
assert(c == [2]f32{1, 1});
|
||||
assert(c == 1);
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: distinct [3]f32;
|
||||
a := Vector3{1, 2, 3};
|
||||
b := Vector3{5, 6, 7};
|
||||
c := (a * b)/2 + 1;
|
||||
d := c.x + c.y + c.z;
|
||||
fmt.printf("%.1f\n", d); // 22.0
|
||||
|
||||
cross :: proc(a, b: Vector3) -> Vector3 {
|
||||
i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
|
||||
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
|
||||
return i - j;
|
||||
}
|
||||
|
||||
blah :: proc(a: Vector3) -> f32 {
|
||||
return a.x + a.y + a.z;
|
||||
}
|
||||
|
||||
x := cross(a, b);
|
||||
fmt.println(x);
|
||||
fmt.println(blah(x));
|
||||
}
|
||||
}
|
||||
|
||||
named_proc_return_parameters :: proc() {
|
||||
fmt.println("# named proc return parameters");
|
||||
|
||||
foo0 :: proc() -> int {
|
||||
return 123;
|
||||
}
|
||||
foo1 :: proc() -> (a: int) {
|
||||
a = 123;
|
||||
return;
|
||||
}
|
||||
foo2 :: proc() -> (a, b: int) {
|
||||
// Named return values act like variables within the scope
|
||||
a = 321;
|
||||
b = 567;
|
||||
return b, a;
|
||||
}
|
||||
fmt.println("foo0 =", foo0()); // 123
|
||||
fmt.println("foo1 =", foo1()); // 123
|
||||
fmt.println("foo2 =", foo2()); // 567 321
|
||||
}
|
||||
|
||||
|
||||
using_enum :: proc() {
|
||||
fmt.println("# using enum");
|
||||
|
||||
using Foo :: enum {A, B, C};
|
||||
|
||||
f0 := A;
|
||||
f1 := B;
|
||||
f2 := C;
|
||||
fmt.println(f0, f1, f2);
|
||||
fmt.println(len(Foo));
|
||||
|
||||
// Non-comparsion operations are not allowed with enum
|
||||
// You must convert to an integer if you want to do this
|
||||
// x := f0 + f1;
|
||||
y := int(f0) + int(f1);
|
||||
}
|
||||
|
||||
explicit_procedure_overloading :: proc() {
|
||||
fmt.println("# explicit procedure overloading");
|
||||
|
||||
add_ints :: proc(a, b: int) -> int {
|
||||
x := a + b;
|
||||
fmt.println("add_ints", x);
|
||||
return x;
|
||||
}
|
||||
add_floats :: proc(a, b: f32) -> f32 {
|
||||
x := a + b;
|
||||
fmt.println("add_floats", x);
|
||||
return x;
|
||||
}
|
||||
add_numbers :: proc(a: int, b: f32, c: u8) -> int {
|
||||
x := int(a) + int(b) + int(c);
|
||||
fmt.println("add_numbers", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
add :: proc[add_ints, add_floats, add_numbers];
|
||||
|
||||
add(int(1), int(2));
|
||||
add(f32(1), f32(2));
|
||||
add(int(1), f32(2), u8(3));
|
||||
|
||||
add(1, 2); // untyped ints coerce to int tighter than f32
|
||||
add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
|
||||
add(1, 2, 3); // three parameters
|
||||
|
||||
// Ambiguous answers
|
||||
// add(1.0, 2);
|
||||
// add(1, 2.0);
|
||||
}
|
||||
|
||||
complete_switch :: proc() {
|
||||
fmt.println("# complete_switch");
|
||||
{ // enum
|
||||
using Foo :: enum {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
}
|
||||
|
||||
b := Foo.B;
|
||||
f := Foo.A;
|
||||
#complete switch f {
|
||||
case A: fmt.println("A");
|
||||
case B: fmt.println("B");
|
||||
case C: fmt.println("C");
|
||||
case D: fmt.println("D");
|
||||
case: fmt.println("?");
|
||||
}
|
||||
}
|
||||
{ // union
|
||||
Foo :: union {int, bool};
|
||||
f: Foo = 123;
|
||||
#complete switch in f {
|
||||
case int: fmt.println("int");
|
||||
case bool: fmt.println("bool");
|
||||
case:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cstring_example :: proc() {
|
||||
W :: "Hellope";
|
||||
X :: cstring(W);
|
||||
Y :: string(X);
|
||||
|
||||
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(cstring)string is O(N)
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bit_set_type :: proc() {
|
||||
{
|
||||
using Day :: enum {
|
||||
Sunday,
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday,
|
||||
}
|
||||
|
||||
Days :: distinct bit_set[Day];
|
||||
WEEKEND :: Days{Sunday, Saturday};
|
||||
|
||||
d: Days;
|
||||
d = {Sunday, Monday};
|
||||
x := Tuesday;
|
||||
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);
|
||||
}
|
||||
{
|
||||
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' in x));
|
||||
|
||||
y |= {1, 4, 2};
|
||||
assert(2 in y);
|
||||
}
|
||||
}
|
||||
|
||||
diverging_procedures :: proc() {
|
||||
// Diverging procedures may never return
|
||||
foo :: proc() -> ! {
|
||||
fmt.println("I'm a diverging procedure");
|
||||
}
|
||||
|
||||
foo();
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
general_stuff();
|
||||
union_type();
|
||||
parametric_polymorphism();
|
||||
threading_example();
|
||||
array_programming();
|
||||
named_proc_return_parameters();
|
||||
using_enum();
|
||||
explicit_procedure_overloading();
|
||||
complete_switch();
|
||||
cstring_example();
|
||||
deprecated_attribute();
|
||||
bit_set_type();
|
||||
diverging_procedures();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
// #import "http_test.odin" as ht;
|
||||
// #import "game.odin" as game;
|
||||
// #import "punity.odin" as pn;
|
||||
import "core:fmt.odin";
|
||||
import "core:os.odin";
|
||||
import "core:mem.odin";
|
||||
// import "http_test.odin" as ht;
|
||||
// import "game.odin" as game;
|
||||
// import "punity.odin" as pn;
|
||||
|
||||
main :: proc() {
|
||||
struct_padding();
|
||||
@@ -160,21 +160,21 @@ type_introspection :: proc() {
|
||||
info: ^Type_Info;
|
||||
x: int;
|
||||
|
||||
info = type_info(int); // by type
|
||||
info = type_info_of_val(x); // by value
|
||||
info = type_info_of(int); // by type
|
||||
info = type_info_of(x); // by value
|
||||
// See: runtime.odin
|
||||
|
||||
match i in info {
|
||||
case Type_Info.Integer:
|
||||
match i in info.variant {
|
||||
case Type_Info_Integer:
|
||||
fmt.println("integer!");
|
||||
case Type_Info.Float:
|
||||
case Type_Info_Float:
|
||||
fmt.println("float!");
|
||||
default:
|
||||
case:
|
||||
fmt.println("potato!");
|
||||
}
|
||||
|
||||
// Unsafe cast
|
||||
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
|
||||
integer_info := cast(^Type_Info_Integer)cast(rawptr)info;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -185,9 +185,9 @@ type_introspection :: proc() {
|
||||
v2: Vector3;
|
||||
v3: Vector3;
|
||||
|
||||
t1 := type_info_of_val(v1);
|
||||
t2 := type_info_of_val(v2);
|
||||
t3 := type_info_of_val(v3);
|
||||
t1 := type_info_of(v1);
|
||||
t2 := type_info_of(v2);
|
||||
t3 := type_info_of(v3);
|
||||
|
||||
fmt.println();
|
||||
fmt.print("Type of v1 is:\n\t", t1);
|
||||
@@ -262,12 +262,12 @@ crazy_introspection :: proc() {
|
||||
TOMATO,
|
||||
}
|
||||
|
||||
fruit_ti := type_info(Fruit);
|
||||
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
|
||||
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
|
||||
fruit_ti := type_info_of(Fruit);
|
||||
name := fruit_ti.variant.(Type_Info_Named).name;
|
||||
info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum);
|
||||
|
||||
fmt.printf("%s :: enum %T {\n", name, info.base);
|
||||
for i := 0; i < len(info.values); i++ {
|
||||
for _, i in info.values {
|
||||
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
|
||||
}
|
||||
fmt.printf("}\n");
|
||||
@@ -328,7 +328,7 @@ miscellany :: proc() {
|
||||
*/
|
||||
|
||||
// assert(false)
|
||||
// compile_assert(false)
|
||||
// #assert(false)
|
||||
// panic("Panic message goes here")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Demo 002
|
||||
#load "fmt.odin";
|
||||
#load "math.odin";
|
||||
// #load "game.odin"
|
||||
export "core:fmt.odin";
|
||||
export "core:math.odin";
|
||||
export "core:mem.odin";
|
||||
// export "game.odin"
|
||||
|
||||
#thread_local tls_int: int;
|
||||
|
||||
@@ -96,9 +97,7 @@ enumerations :: proc() {
|
||||
variadic_procedures :: proc() {
|
||||
print_ints :: proc(args: ..int) {
|
||||
for arg, i in args {
|
||||
if i > 0 {
|
||||
print(", ");
|
||||
}
|
||||
if i > 0 do print(", ");
|
||||
print(arg);
|
||||
}
|
||||
}
|
||||
@@ -111,9 +110,7 @@ variadic_procedures :: proc() {
|
||||
print(prefix);
|
||||
print(": ");
|
||||
for arg, i in args {
|
||||
if i > 0 {
|
||||
print(", ");
|
||||
}
|
||||
if i > 0 do print(", ");
|
||||
print(arg);
|
||||
}
|
||||
}
|
||||
@@ -147,13 +144,7 @@ new_builtins :: proc() {
|
||||
|
||||
// Q: Should this be `free` rather than `free` and should I overload it for slices too?
|
||||
|
||||
{
|
||||
prev_context := context;
|
||||
defer __context = prev_context;
|
||||
// Q: Should I add a `push_context` feature to the language?
|
||||
|
||||
__context.allocator = default_allocator();
|
||||
|
||||
push_allocator default_allocator() {
|
||||
a := new(int);
|
||||
defer free(a);
|
||||
|
||||
@@ -164,7 +155,7 @@ new_builtins :: proc() {
|
||||
|
||||
{
|
||||
a: int = 123;
|
||||
b: type_of_val(a) = 321;
|
||||
b: type_of(a) = 321;
|
||||
|
||||
// NOTE(bill): This matches the current naming scheme
|
||||
// size_of
|
||||
@@ -180,8 +171,8 @@ new_builtins :: proc() {
|
||||
{
|
||||
// Compile time assert
|
||||
COND :: true;
|
||||
compile_assert(COND);
|
||||
// compile_assert(!COND)
|
||||
#assert(COND);
|
||||
// #assert(!COND)
|
||||
|
||||
// Runtime assert
|
||||
x := true;
|
||||
@@ -205,7 +196,7 @@ new_builtins :: proc() {
|
||||
|
||||
a: [16]int;
|
||||
a[1] = 1;
|
||||
b := ^a;
|
||||
b := &a;
|
||||
// Auto pointer deref
|
||||
// consistent with record members
|
||||
assert(b[1] == 1);
|
||||
@@ -255,7 +246,7 @@ match_statement :: proc() {
|
||||
print("5!\n");
|
||||
fallthrough; // explicit fallthrough
|
||||
|
||||
default:
|
||||
case:
|
||||
print("default!\n");
|
||||
}
|
||||
|
||||
@@ -267,7 +258,7 @@ match_statement :: proc() {
|
||||
// break by default
|
||||
case TAU:
|
||||
print("τ!\n");
|
||||
default:
|
||||
case:
|
||||
print("default!\n");
|
||||
}
|
||||
|
||||
@@ -279,7 +270,7 @@ match_statement :: proc() {
|
||||
// break by default
|
||||
case "Goodbye":
|
||||
print("farewell\n");
|
||||
default:
|
||||
case:
|
||||
print("???\n");
|
||||
}
|
||||
|
||||
@@ -302,7 +293,7 @@ match_statement :: proc() {
|
||||
print("dozens\n");
|
||||
case a >= 100 && a < 1000:
|
||||
print("hundreds\n");
|
||||
default:
|
||||
case:
|
||||
print("a fuck ton\n");
|
||||
}
|
||||
|
||||
@@ -334,9 +325,7 @@ Vector3 :: struct {x, y, z: f32}
|
||||
|
||||
print_floats :: proc(args: ..f32) {
|
||||
for arg, i in args {
|
||||
if i > 0 {
|
||||
print(", ");
|
||||
}
|
||||
if i > 0 do print(", ");
|
||||
print(arg);
|
||||
}
|
||||
println();
|
||||
@@ -355,7 +344,7 @@ namespacing :: proc() {
|
||||
Thing :: #type struct {
|
||||
y: int,
|
||||
test: bool,
|
||||
}
|
||||
};
|
||||
|
||||
b: Thing; // Uses this scope's Thing
|
||||
b.test = true;
|
||||
@@ -473,10 +462,10 @@ namespacing :: proc() {
|
||||
}
|
||||
|
||||
e := Entity{position = Vector3{1, 2, 3}};
|
||||
print_pos_1(^e);
|
||||
print_pos_2(^e);
|
||||
print_pos_3(^e);
|
||||
print_pos_4(^e);
|
||||
print_pos_1(&e);
|
||||
print_pos_2(&e);
|
||||
print_pos_3(&e);
|
||||
print_pos_4(&e);
|
||||
|
||||
// This is similar to C++'s `this` pointer that is implicit and only available in methods
|
||||
}
|
||||
@@ -574,20 +563,20 @@ subtyping :: proc() {
|
||||
entity_count := 0;
|
||||
|
||||
next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
|
||||
e := ^entities[entity_count^];
|
||||
entity_count^++;
|
||||
e := &entities[entity_count^];
|
||||
entity_count^ += 1;
|
||||
return e;
|
||||
}
|
||||
|
||||
f: Frog;
|
||||
f.entity = next_entity(entities[..], ^entity_count);
|
||||
f.entity = next_entity(entities[..], &entity_count);
|
||||
f.position = Vector3{3, 4, 6};
|
||||
|
||||
using f.position;
|
||||
print_floats(x, y, z);
|
||||
}
|
||||
|
||||
{
|
||||
/*{
|
||||
// Down casting
|
||||
|
||||
Entity :: struct {
|
||||
@@ -609,7 +598,7 @@ subtyping :: proc() {
|
||||
|
||||
// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
|
||||
// Q: Should I completely remove `down_cast` as I added it in about 30 minutes
|
||||
}
|
||||
}*/
|
||||
|
||||
{
|
||||
// Multiple "inheritance"/subclassing
|
||||
@@ -630,7 +619,7 @@ subtyping :: proc() {
|
||||
|
||||
tagged_unions :: proc() {
|
||||
{
|
||||
EntityKind :: enum {
|
||||
Entity_Kind :: enum {
|
||||
INVALID,
|
||||
FROG,
|
||||
GIRAFFE,
|
||||
@@ -638,8 +627,8 @@ tagged_unions :: proc() {
|
||||
}
|
||||
|
||||
Entity :: struct {
|
||||
kind: EntityKind
|
||||
using data: raw_union {
|
||||
kind: Entity_Kind
|
||||
using data: struct #raw_union {
|
||||
frog: struct {
|
||||
jump_height: f32,
|
||||
colour: u32,
|
||||
@@ -657,33 +646,31 @@ tagged_unions :: proc() {
|
||||
}
|
||||
|
||||
e: Entity;
|
||||
e.kind = EntityKind.FROG;
|
||||
e.kind = Entity_Kind.FROG;
|
||||
e.frog.jump_height = 12;
|
||||
|
||||
f: type_of_val(e.frog);
|
||||
f: type_of(e.frog);
|
||||
|
||||
// But this is very unsafe and extremely cumbersome to write
|
||||
// In C++, I use macros to alleviate this but it's not a solution
|
||||
}
|
||||
|
||||
{
|
||||
Entity :: union {
|
||||
Frog{
|
||||
jump_height: f32,
|
||||
colour: u32,
|
||||
},
|
||||
Giraffe{
|
||||
neck_length: f32,
|
||||
spot_count: int,
|
||||
},
|
||||
Helicopter{
|
||||
blade_count: int,
|
||||
weight: f32,
|
||||
pilot_name: string,
|
||||
},
|
||||
Frog :: struct {
|
||||
jump_height: f32,
|
||||
colour: u32,
|
||||
}
|
||||
Giraffe :: struct {
|
||||
neck_length: f32,
|
||||
spot_count: int,
|
||||
}
|
||||
Helicopter :: struct {
|
||||
blade_count: int,
|
||||
weight: f32,
|
||||
pilot_name: string,
|
||||
}
|
||||
Entity :: union {Frog, Giraffe, Helicopter};
|
||||
|
||||
using Entity;
|
||||
f1: Frog = Frog{12, 0xff9900};
|
||||
f2: Entity = Frog{12, 0xff9900}; // Implicit cast
|
||||
f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast
|
||||
@@ -703,7 +690,7 @@ tagged_unions :: proc() {
|
||||
// Requires a pointer to the union
|
||||
// `x` will be a pointer to type of the case
|
||||
|
||||
match x in ^f {
|
||||
match x in &f {
|
||||
case Frog:
|
||||
print("Frog!\n");
|
||||
print(x.jump_height); nl();
|
||||
@@ -713,7 +700,7 @@ tagged_unions :: proc() {
|
||||
print("Giraffe!\n");
|
||||
case Helicopter:
|
||||
print("ROFLCOPTER!\n");
|
||||
default:
|
||||
case:
|
||||
print("invalid entity\n");
|
||||
}
|
||||
|
||||
@@ -755,11 +742,11 @@ tagged_unions :: proc() {
|
||||
AstNode :: struct {};
|
||||
ExactValue :: struct {};
|
||||
|
||||
EntityKind :: enum {
|
||||
Entity_Kind :: enum {
|
||||
Invalid,
|
||||
Constant,
|
||||
Variable,
|
||||
UsingVariable,
|
||||
Using_Variable,
|
||||
TypeName,
|
||||
Procedure,
|
||||
Builtin,
|
||||
@@ -769,14 +756,14 @@ tagged_unions :: proc() {
|
||||
Guid :: i64;
|
||||
Entity :: struct {
|
||||
|
||||
kind: EntityKind,
|
||||
kind: Entity_Kind,
|
||||
guid: Guid,
|
||||
|
||||
scope: ^Scope,
|
||||
token: Token,
|
||||
type_: ^Type,
|
||||
|
||||
using data: raw_union {
|
||||
using data: struct #raw_union {
|
||||
Constant: struct {
|
||||
value: ExactValue,
|
||||
},
|
||||
@@ -786,7 +773,7 @@ tagged_unions :: proc() {
|
||||
is_field: bool, // Is struct field
|
||||
anonymous: bool, // Variable is an anonymous
|
||||
},
|
||||
UsingVariable: struct {
|
||||
Using_Variable: struct {
|
||||
},
|
||||
TypeName: struct {
|
||||
},
|
||||
@@ -813,44 +800,44 @@ tagged_unions :: proc() {
|
||||
|
||||
Guid :: i64;
|
||||
Entity_Base :: struct {
|
||||
|
||||
}
|
||||
|
||||
Entity :: union {
|
||||
|
||||
Constant :: struct {
|
||||
value: ExactValue,
|
||||
}
|
||||
Variable :: struct {
|
||||
visited: bool, // Cycle detection
|
||||
used: bool, // Variable is used
|
||||
is_field: bool, // Is struct field
|
||||
anonymous: bool, // Variable is an anonymous
|
||||
}
|
||||
Using_Variable :: struct {
|
||||
}
|
||||
TypeName :: struct {
|
||||
}
|
||||
Procedure :: struct {
|
||||
used: bool,
|
||||
}
|
||||
Builtin :: struct {
|
||||
id: int,
|
||||
}
|
||||
|
||||
Entity :: struct {
|
||||
guid: Guid,
|
||||
|
||||
scope: ^Scope,
|
||||
token: Token,
|
||||
type_: ^Type,
|
||||
|
||||
Constant{
|
||||
value: ExactValue,
|
||||
},
|
||||
Variable{
|
||||
visited: bool, // Cycle detection
|
||||
used: bool, // Variable is used
|
||||
is_field: bool, // Is struct field
|
||||
anonymous: bool, // Variable is an anonymous
|
||||
},
|
||||
UsingVariable{
|
||||
},
|
||||
TypeName{
|
||||
},
|
||||
Procedure{
|
||||
used: bool,
|
||||
},
|
||||
Builtin{
|
||||
id: int,
|
||||
},
|
||||
variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin},
|
||||
}
|
||||
|
||||
using Entity;
|
||||
|
||||
e: Entity;
|
||||
|
||||
e = Variable{
|
||||
used = true,
|
||||
anonymous = false,
|
||||
e := Entity{
|
||||
variant = Variable{
|
||||
used = true,
|
||||
anonymous = false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -863,13 +850,13 @@ tagged_unions :: proc() {
|
||||
{
|
||||
// `Raw` unions still have uses, especially for mathematic types
|
||||
|
||||
Vector2 :: raw_union {
|
||||
Vector2 :: struct #raw_union {
|
||||
using xy_: struct { x, y: f32 },
|
||||
e: [2]f32,
|
||||
v: [vector 2]f32,
|
||||
}
|
||||
|
||||
Vector3 :: raw_union {
|
||||
Vector3 :: struct #raw_union {
|
||||
using xyz_: struct { x, y, z: f32 },
|
||||
xy: Vector2,
|
||||
e: [3]f32,
|
||||
@@ -1,14 +1,14 @@
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
#import "hash.odin";
|
||||
#import "mem.odin";
|
||||
import "core:fmt.odin";
|
||||
import "core:utf8.odin";
|
||||
import "core:hash.odin";
|
||||
import "core:mem.odin";
|
||||
|
||||
main :: proc() {
|
||||
{ // New Standard Library stuff
|
||||
s := "Hello";
|
||||
fmt.println(s,
|
||||
utf8.valid_string(s),
|
||||
hash.murmur64(cast([]byte)s));
|
||||
hash.murmur64(cast([]u8)s));
|
||||
|
||||
// utf8.odin
|
||||
// hash.odin
|
||||
@@ -20,10 +20,10 @@ main :: proc() {
|
||||
|
||||
{
|
||||
arena: mem.Arena;
|
||||
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
|
||||
defer mem.free_arena(^arena);
|
||||
mem.init_arena_from_context(&arena, mem.megabytes(16)); // Uses default allocator
|
||||
defer mem.destroy_arena(&arena);
|
||||
|
||||
push_allocator mem.arena_allocator(^arena) {
|
||||
push_allocator mem.arena_allocator(&arena) {
|
||||
x := new(int);
|
||||
x^ = 1337;
|
||||
|
||||
@@ -32,7 +32,7 @@ main :: proc() {
|
||||
|
||||
/*
|
||||
push_allocator x {
|
||||
...
|
||||
..
|
||||
}
|
||||
|
||||
is equivalent to:
|
||||
@@ -42,14 +42,14 @@ main :: proc() {
|
||||
__context.allocator = x
|
||||
defer __context.allocator = prev_allocator
|
||||
|
||||
...
|
||||
..
|
||||
}
|
||||
*/
|
||||
|
||||
// You can also "push" a context
|
||||
|
||||
c := context; // Create copy of the allocator
|
||||
c.allocator = mem.arena_allocator(^arena);
|
||||
c.allocator = mem.arena_allocator(&arena);
|
||||
|
||||
push_context c {
|
||||
x := new(int);
|
||||
@@ -1,13 +1,13 @@
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
// #import "atomic.odin";
|
||||
// #import "hash.odin";
|
||||
// #import "math.odin";
|
||||
// #import "mem.odin";
|
||||
// #import "opengl.odin";
|
||||
// #import "os.odin";
|
||||
// #import "sync.odin";
|
||||
// #import win32 "sys/windows.odin";
|
||||
import "core:fmt.odin";
|
||||
import "core:utf8.odin";
|
||||
// import "core:atomic.odin";
|
||||
// import "core:hash.odin";
|
||||
// import "core:math.odin";
|
||||
// import "core:mem.odin";
|
||||
// import "core:opengl.odin";
|
||||
// import "core:os.odin";
|
||||
// import "core:sync.odin";
|
||||
// import win32 "core:sys/windows.odin";
|
||||
|
||||
main :: proc() {
|
||||
// syntax();
|
||||
@@ -43,7 +43,7 @@ syntax :: proc() {
|
||||
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
||||
|
||||
// Slice interals are now just a `ptr+len+cap`
|
||||
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
|
||||
slice: []int; #assert(size_of(slice) == 3*size_of(int));
|
||||
|
||||
// Helper type - Help the reader understand what it is quicker
|
||||
My_Int :: #type int;
|
||||
@@ -90,22 +90,17 @@ Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
||||
|
||||
prefixes :: proc() {
|
||||
using var: Prefix_Type;
|
||||
immutable const := Prefix_Type{1, 2, nil};
|
||||
var.x = 123;
|
||||
x = 123;
|
||||
// const.x = 123; // const is immutable
|
||||
|
||||
|
||||
|
||||
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
|
||||
// int_ptr = nil; // Not valid
|
||||
// int_ptr^ = 123; // Not valid
|
||||
foo :: proc(using pt: Prefix_Type) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Same as C99's `restrict`
|
||||
bar :: proc(no_alias a, b: ^int) {
|
||||
bar :: proc(#no_alias a, b: ^int) {
|
||||
// Assumes a never equals b so it can perform optimizations with that fact
|
||||
}
|
||||
|
||||
@@ -138,14 +133,18 @@ when_statements :: proc() {
|
||||
foreign_procedures();
|
||||
}
|
||||
|
||||
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
|
||||
when ODIN_OS == "windows" {
|
||||
foreign_system_library win32_user "user32.lib";
|
||||
}
|
||||
// NOTE: This is done on purpose for two reasons:
|
||||
// * Makes it clear where the platform specific stuff is
|
||||
// * Removes the need to solve the travelling salesman problem when importing files :P
|
||||
|
||||
foreign_procedures :: proc() {
|
||||
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
|
||||
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
|
||||
foreign win32_user {
|
||||
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---;
|
||||
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "ShowWindow" ---;
|
||||
}
|
||||
// NOTE: If that library doesn't get used, it doesn't get linked with
|
||||
// NOTE: There is not link checking yet to see if that procedure does come from that library
|
||||
|
||||
@@ -203,14 +202,14 @@ loops :: proc() {
|
||||
fmt.println(val, idx);
|
||||
}
|
||||
|
||||
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
primes := [?]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
|
||||
for p in primes {
|
||||
fmt.println(p);
|
||||
}
|
||||
|
||||
// Pointers to arrays, slices, or strings are allowed
|
||||
for _ in ^primes {
|
||||
for _ in &primes {
|
||||
// ignore the value and just iterate across it
|
||||
}
|
||||
|
||||
@@ -219,7 +218,7 @@ loops :: proc() {
|
||||
name := "你好,世界";
|
||||
fmt.println(name);
|
||||
for r in name {
|
||||
compile_assert(type_of_val(r) == rune);
|
||||
#assert(type_of(r) == rune);
|
||||
fmt.printf("%r\n", r);
|
||||
}
|
||||
|
||||
@@ -270,8 +269,8 @@ procedure_overloading :: proc() {
|
||||
a: i32 = 123;
|
||||
b: f32;
|
||||
c: rawptr;
|
||||
fmt.println(foo(^a));
|
||||
foo(^b);
|
||||
fmt.println(foo(&a));
|
||||
foo(&b);
|
||||
foo(c);
|
||||
// foo(nil); // nil could go to numerous types thus the ambiguity
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
// import "core:atomic.odin";
|
||||
import "core:hash.odin";
|
||||
import "core:mem.odin";
|
||||
import "core:opengl.odin";
|
||||
import "core:strconv.odin";
|
||||
import "core:sync.odin";
|
||||
import win32 "core:sys/windows.odin";
|
||||
|
||||
import "core:fmt.odin";
|
||||
import "core:os.odin";
|
||||
import "core:math.odin";
|
||||
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
/*
|
||||
Added:
|
||||
* Unexported entities and fields using an underscore prefix
|
||||
- See `sync.odin` and explain
|
||||
|
||||
Removed:
|
||||
* Maybe/option types
|
||||
* Remove `type` keyword and other "reserved" keywords
|
||||
* ..< and .. removed and replace with .. (half-closed range)
|
||||
|
||||
Changed:
|
||||
* `#assert` and `assert` return the value of the condition for semantic reasons
|
||||
* thread_local -> #thread_local
|
||||
* #include -> #load
|
||||
* Files only get checked if they are actually used
|
||||
* match x in y {} // For type match statements
|
||||
* Version numbering now starts from 0.1.0 and uses the convention:
|
||||
- major.minor.patch
|
||||
* Core library additions to Windows specific stuff
|
||||
*/
|
||||
|
||||
{
|
||||
Fruit :: enum {
|
||||
APPLE,
|
||||
BANANA,
|
||||
COCONUT,
|
||||
}
|
||||
fmt.println(Fruit.names);
|
||||
}
|
||||
|
||||
{
|
||||
A :: struct {x, y: f32};
|
||||
B :: struct #align 16 {x, y: f32};
|
||||
fmt.println("align_of(A) =", align_of(A));
|
||||
fmt.println("align_of(B) =", align_of(B));
|
||||
}
|
||||
|
||||
{
|
||||
// Removal of ..< and ..
|
||||
for i in 0..16 {
|
||||
}
|
||||
// Is similar to
|
||||
for i := 0; i < 16; i += 1 {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
thing: for i in 0..10 {
|
||||
for j in i+1..10 {
|
||||
if j == 2 {
|
||||
fmt.println(i, j);
|
||||
continue thing;
|
||||
}
|
||||
if j == 3 {
|
||||
break thing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Works with, `for`, `for in`, `match`, `match in`
|
||||
// NOTE(bill): This solves most of the problems I need `goto` for
|
||||
}
|
||||
|
||||
{
|
||||
t := type_info_of(int);
|
||||
match i in t.variant {
|
||||
case Type_Info_Integer, Type_Info_Float:
|
||||
fmt.println("It's a number");
|
||||
}
|
||||
|
||||
|
||||
x: any = 123;
|
||||
foo: match i in x {
|
||||
case int, f32:
|
||||
fmt.println("It's an int or f32");
|
||||
break foo;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
cond := true;
|
||||
x: int;
|
||||
if cond {
|
||||
x = 3;
|
||||
} else {
|
||||
x = 4;
|
||||
}
|
||||
|
||||
|
||||
// Ternary operator
|
||||
y := cond ? 3 : 4;
|
||||
|
||||
FOO :: true ? 123 : 432; // Constant ternary expression
|
||||
fmt.println("Ternary values:", y, FOO);
|
||||
}
|
||||
|
||||
{
|
||||
// Slices now store a capacity
|
||||
buf: [256]u8;
|
||||
s: []u8;
|
||||
s = buf[..0]; // == buf[0..0];
|
||||
fmt.println("count =", len(s));
|
||||
fmt.println("capacity =", cap(s));
|
||||
append(&s, 1, 2, 3);
|
||||
fmt.println(s);
|
||||
|
||||
s = buf[1..2..3];
|
||||
fmt.println("count =", len(s));
|
||||
fmt.println("capacity =", cap(s));
|
||||
fmt.println(s);
|
||||
|
||||
clear(&s); // Sets count to zero
|
||||
}
|
||||
|
||||
{
|
||||
Foo :: struct {
|
||||
x, y, z: f32,
|
||||
ok: bool,
|
||||
flags: u32,
|
||||
}
|
||||
foo_array: [256]Foo;
|
||||
foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]);
|
||||
// Useful for things like
|
||||
// os.write(handle, foo_as_bytes);
|
||||
|
||||
foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/size_of(Foo));
|
||||
// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
|
||||
// And if so what would the syntax be?
|
||||
// slice_transmute([]Foo, foo_as_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
Vec3 :: [vector 3]f32;
|
||||
|
||||
x := Vec3{1, 2, 3};
|
||||
y := Vec3{4, 5, 6};
|
||||
fmt.println(x < y);
|
||||
fmt.println(x + y);
|
||||
fmt.println(x - y);
|
||||
fmt.println(x * y);
|
||||
fmt.println(x / y);
|
||||
|
||||
for i in x {
|
||||
fmt.println(i);
|
||||
}
|
||||
|
||||
#assert(size_of([vector 7]bool) >= size_of([7]bool));
|
||||
#assert(size_of([vector 7]i32) >= size_of([7]i32));
|
||||
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
|
||||
}
|
||||
|
||||
{
|
||||
// fmt.* changes
|
||||
// bprint* returns `string`
|
||||
|
||||
data: [256]u8;
|
||||
str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!');
|
||||
fmt.println(str);
|
||||
}
|
||||
|
||||
{
|
||||
x: [dynamic]f64;
|
||||
reserve(&x, 16);
|
||||
defer free(x); // `free` is overloaded for numerous types
|
||||
// Number literals can have underscores in them for readability
|
||||
append(&x, 2_000_000.500_000, 123, 5, 7); // variadic append
|
||||
|
||||
for p, i in x {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.print(p);
|
||||
}
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
{
|
||||
// Dynamic array "literals"
|
||||
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
|
||||
defer free(x);
|
||||
fmt.println(x); // fmt.print* supports printing of dynamic types
|
||||
clear(&x);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
{
|
||||
m: map[f32]int;
|
||||
reserve(&m, 16);
|
||||
defer free(m);
|
||||
|
||||
m[1.0] = 1278;
|
||||
m[2.0] = 7643;
|
||||
m[3.0] = 564;
|
||||
_, ok := m[3.0];
|
||||
c := m[3.0];
|
||||
assert(ok && c == 564);
|
||||
|
||||
fmt.print("map[");
|
||||
i := 0;
|
||||
for val, key in m {
|
||||
if i > 0 {
|
||||
fmt.print(", ");
|
||||
}
|
||||
fmt.printf("%v=%v", key, val);
|
||||
i += 1;
|
||||
}
|
||||
fmt.println("]");
|
||||
}
|
||||
{
|
||||
m := map[string]u32{
|
||||
"a" = 56,
|
||||
"b" = 13453,
|
||||
"c" = 7654,
|
||||
};
|
||||
defer free(m);
|
||||
|
||||
c := m["c"];
|
||||
_, ok := m["c"];
|
||||
assert(ok && c == 7654);
|
||||
fmt.println(m);
|
||||
|
||||
delete(&m, "c"); // deletes entry with key "c"
|
||||
_, found := m["c"];
|
||||
assert(!found);
|
||||
|
||||
fmt.println(m);
|
||||
clear(&m);
|
||||
fmt.println(m);
|
||||
|
||||
// NOTE: Fixed size maps are planned but we have not yet implemented
|
||||
// them as we have had no need for them as of yet
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: struct{x, y, z: f32};
|
||||
Quaternion :: struct{x, y, z, w: f32};
|
||||
|
||||
// Variants
|
||||
Frog :: struct {
|
||||
ribbit_volume: f32,
|
||||
jump_height: f32,
|
||||
}
|
||||
Door :: struct {
|
||||
openness: f32,
|
||||
}
|
||||
Map :: struct {
|
||||
width, height: f32,
|
||||
place_positions: []Vector3,
|
||||
place_names: []string,
|
||||
}
|
||||
|
||||
Entity :: struct {
|
||||
// Common Fields
|
||||
id: u64,
|
||||
name: string,
|
||||
using position: Vector3,
|
||||
orientation: Quaternion,
|
||||
flags: u32,
|
||||
|
||||
variant: union { Frog, Door, Map },
|
||||
}
|
||||
|
||||
entity: Entity;
|
||||
entity.id = 1337;
|
||||
// implicit conversion from variant to base type
|
||||
entity.variant = Frog{
|
||||
ribbit_volume = 0.5,
|
||||
jump_height = 2.1,
|
||||
/*other data */
|
||||
};
|
||||
|
||||
entity.name = "Frank";
|
||||
entity.position = Vector3{1, 4, 9};
|
||||
|
||||
match e in entity.variant {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Door:
|
||||
fmt.println("Creak");
|
||||
case Map:
|
||||
fmt.println("Rustle");
|
||||
case:
|
||||
fmt.println("Just a normal entity");
|
||||
}
|
||||
|
||||
if frog, ok := entity.variant.(Frog); ok {
|
||||
fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position);
|
||||
}
|
||||
|
||||
// Panics if not the correct type
|
||||
frog: Frog;
|
||||
frog = entity.variant.(Frog);
|
||||
frog, _ = entity.variant.(Frog); // ignore error and force cast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,570 @@
|
||||
import "core:fmt.odin"
|
||||
import "core:strconv.odin"
|
||||
import "core:mem.odin"
|
||||
import "core:bits.odin"
|
||||
import "core:hash.odin"
|
||||
import "core:math.odin"
|
||||
import "core:os.odin"
|
||||
import "core:raw.odin"
|
||||
import "core:sort.odin"
|
||||
import "core:strings.odin"
|
||||
import "core:types.odin"
|
||||
import "core:utf16.odin"
|
||||
import "core:utf8.odin"
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
import "core:atomics.odin"
|
||||
import "core:opengl.odin"
|
||||
import "core:thread.odin"
|
||||
import win32 "core:sys/windows.odin"
|
||||
}
|
||||
|
||||
general_stuff :: proc() {
|
||||
{ // `do` for inline statmes rather than block
|
||||
foo :: proc() do fmt.println("Foo!");
|
||||
if false do foo();
|
||||
for false do foo();
|
||||
when false do foo();
|
||||
|
||||
if false do foo();
|
||||
else do foo();
|
||||
}
|
||||
|
||||
{ // Removal of `++` and `--` (again)
|
||||
x: int;
|
||||
x += 1;
|
||||
x -= 1;
|
||||
}
|
||||
{ // Casting syntaxes
|
||||
i := i32(137);
|
||||
ptr := &i;
|
||||
|
||||
fp1 := (^f32)(ptr);
|
||||
// ^f32(ptr) == ^(f32(ptr))
|
||||
fp2 := cast(^f32)ptr;
|
||||
|
||||
f1 := (^f32)(ptr)^;
|
||||
f2 := (cast(^f32)ptr)^;
|
||||
|
||||
// Questions: Should there be two ways to do it?
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove *_val_of built-in procedures
|
||||
* size_of, align_of, offset_of
|
||||
* type_of, type_info_of
|
||||
*/
|
||||
|
||||
{ // `expand_to_tuple` built-in procedure
|
||||
Foo :: struct {
|
||||
x: int,
|
||||
b: bool,
|
||||
}
|
||||
f := Foo{137, true};
|
||||
x, b := expand_to_tuple(f);
|
||||
fmt.println(f);
|
||||
fmt.println(x, b);
|
||||
fmt.println(expand_to_tuple(f));
|
||||
}
|
||||
|
||||
{
|
||||
// .. half-closed range
|
||||
// .. open range
|
||||
|
||||
for in 0..2 {} // 0, 1
|
||||
for in 0..2 {} // 0, 1, 2
|
||||
}
|
||||
}
|
||||
|
||||
default_struct_values :: proc() {
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
}
|
||||
{
|
||||
// Default values must be constants
|
||||
Vector3 :: struct {
|
||||
x: f32 = 1,
|
||||
y: f32 = 4,
|
||||
z: f32 = 9,
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{};
|
||||
fmt.println(v);
|
||||
|
||||
// Uses the same semantics as a default values in a procedure
|
||||
v = Vector3{137};
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{z = 137};
|
||||
fmt.println(v);
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x := 1.0,
|
||||
y := 4.0,
|
||||
z := 9.0,
|
||||
}
|
||||
stack_default: Vector3;
|
||||
stack_literal := Vector3{};
|
||||
heap_one := new(Vector3); defer free(heap_one);
|
||||
heap_two := new_clone(Vector3{}); defer free(heap_two);
|
||||
|
||||
fmt.println("stack_default - ", stack_default);
|
||||
fmt.println("stack_literal - ", stack_literal);
|
||||
fmt.println("heap_one - ", heap_one^);
|
||||
fmt.println("heap_two - ", heap_two^);
|
||||
|
||||
|
||||
N :: 4;
|
||||
stack_array: [N]Vector3;
|
||||
heap_array := new([N]Vector3); defer free(heap_array);
|
||||
heap_slice := make([]Vector3, N); defer free(heap_slice);
|
||||
fmt.println("stack_array[1] - ", stack_array[1]);
|
||||
fmt.println("heap_array[1] - ", heap_array[1]);
|
||||
fmt.println("heap_slice[1] - ", heap_slice[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
union_type :: proc() {
|
||||
{
|
||||
val: union{int, bool};
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
{
|
||||
// There is a duality between `any` and `union`
|
||||
// An `any` has a pointer to the data and allows for any type (open)
|
||||
// A `union` has as binary blob to store the data and allows only certain types (closed)
|
||||
// The following code is with `any` but has the same syntax
|
||||
val: any;
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 :: struct {x, y, z: f32};
|
||||
Quaternion :: struct {x, y, z: f32, w: f32 = 1};
|
||||
|
||||
// More realistic examples
|
||||
{
|
||||
// NOTE(bill): For the above basic examples, you may not have any
|
||||
// particular use for it. However, my main use for them is not for these
|
||||
// simple cases. My main use is for hierarchical types. Many prefer
|
||||
// subtyping, embedding the base data into the derived types. Below is
|
||||
// an example of this for a basic game Entity.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64,
|
||||
name: string,
|
||||
position: Vector3,
|
||||
orientation: Quaternion,
|
||||
|
||||
derived: any,
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity,
|
||||
jump_height: f32,
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: Entity,
|
||||
is_robot: bool,
|
||||
is_zombie: bool,
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t^;
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
switch e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// NOTE(bill): A union can be used to achieve something similar. Instead
|
||||
// of embedding the base data into the derived types, the derived data
|
||||
// in embedded into the base type. Below is the same example of the
|
||||
// basic game Entity but using an union.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64,
|
||||
name: string,
|
||||
position: Vector3,
|
||||
orientation: Quaternion,
|
||||
|
||||
derived: union {Frog, Monster},
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: ^Entity,
|
||||
jump_height: f32,
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: ^Entity,
|
||||
is_robot: bool,
|
||||
is_zombie: bool,
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(Entity);
|
||||
t.derived = T{entity = t};
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
switch e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
|
||||
// NOTE(bill): As you can see, the usage code has not changed, only its
|
||||
// memory layout. Both approaches have their own advantages but they can
|
||||
// be used together to achieve different results. The subtyping approach
|
||||
// can allow for a greater control of the memory layout and memory
|
||||
// allocation, e.g. storing the derivatives together. However, this is
|
||||
// also its disadvantage. You must either preallocate arrays for each
|
||||
// derivative separation (which can be easily missed) or preallocate a
|
||||
// bunch of "raw" memory; determining the maximum size of the derived
|
||||
// types would require the aid of metaprogramming. Unions solve this
|
||||
// particular problem as the data is stored with the base data.
|
||||
// Therefore, it is possible to preallocate, e.g. [100]Entity.
|
||||
|
||||
// It should be noted that the union approach can have the same memory
|
||||
// layout as the any and with the same type restrictions by using a
|
||||
// pointer type for the derivatives.
|
||||
|
||||
/*
|
||||
Entity :: struct {
|
||||
..
|
||||
derived: union{^Frog, ^Monster};
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity;
|
||||
..
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: Entity;
|
||||
..
|
||||
|
||||
}
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t;
|
||||
return t;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
parametric_polymorphism :: proc() {
|
||||
print_value :: proc(value: $T) {
|
||||
fmt.printf("print_value: %T %v\n", value, value);
|
||||
}
|
||||
|
||||
v1: int = 1;
|
||||
v2: f32 = 2.1;
|
||||
v3: f64 = 3.14;
|
||||
v4: string = "message";
|
||||
|
||||
print_value(v1);
|
||||
print_value(v2);
|
||||
print_value(v3);
|
||||
print_value(v4);
|
||||
|
||||
fmt.println();
|
||||
|
||||
add :: proc(p, q: $T) -> T {
|
||||
x: T = p + q;
|
||||
return x;
|
||||
}
|
||||
|
||||
a := add(3, 4);
|
||||
fmt.printf("a: %T = %v\n", a, a);
|
||||
|
||||
b := add(3.2, 4.3);
|
||||
fmt.printf("b: %T = %v\n", b, b);
|
||||
|
||||
// This is how `new` is implemented
|
||||
alloc_type :: proc(T: type) -> ^T {
|
||||
t := cast(^T)alloc(size_of(T), align_of(T));
|
||||
t^ = T{}; // Use default initialization value
|
||||
return t;
|
||||
}
|
||||
|
||||
copy_slice :: proc(dst, src: []$T) -> int {
|
||||
n := min(len(dst), len(src));
|
||||
if n > 0 {
|
||||
mem.copy(&dst[0], &src[0], n*size_of(T));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
double_params :: proc(a: $A, b: $B) -> A {
|
||||
return a + A(b);
|
||||
}
|
||||
|
||||
fmt.println(double_params(12, 1.345));
|
||||
|
||||
|
||||
|
||||
{ // Polymorphic Types and Type Specialization
|
||||
Table_Slot :: struct(Key, Value: type) {
|
||||
occupied: bool,
|
||||
hash: u32,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
TABLE_SIZE_MIN :: 32;
|
||||
Table :: struct(Key, Value: type) {
|
||||
count: int,
|
||||
allocator: Allocator,
|
||||
slots: []Table_Slot(Key, Value),
|
||||
}
|
||||
|
||||
// Only allow types that are specializations of a (polymorphic) slice
|
||||
make_slice :: proc(T: type/[]$E, len: int) -> T {
|
||||
return make(T, len);
|
||||
}
|
||||
|
||||
|
||||
// Only allow types that are specializations of `Table`
|
||||
allocate :: proc(table: ^$T/Table, capacity: int) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
expand :: proc(table: ^$T/Table) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
old_slots := table.slots;
|
||||
|
||||
cap := max(2*cap(table.slots), TABLE_SIZE_MIN);
|
||||
allocate(table, cap);
|
||||
|
||||
for s in old_slots do if s.occupied {
|
||||
put(table, s.key, s.value);
|
||||
}
|
||||
|
||||
free(old_slots);
|
||||
}
|
||||
}
|
||||
|
||||
// Polymorphic determination of a polymorphic struct
|
||||
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
|
||||
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
|
||||
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
if f64(table.count) >= 0.75*f64(cap(table.slots)) {
|
||||
expand(table);
|
||||
}
|
||||
assert(table.count <= cap(table.slots));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = int(hash % u32(cap(table.slots)));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
if index += 1; index >= cap(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.count += 1;
|
||||
}
|
||||
|
||||
slot := &table.slots[index];
|
||||
slot.occupied = true;
|
||||
slot.hash = hash;
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
}
|
||||
|
||||
|
||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
||||
hash := get_hash(key);
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
return Value{}, false;
|
||||
}
|
||||
return table.slots[index].value, true;
|
||||
}
|
||||
|
||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
||||
if cap(table.slots) <= 0 do return -1;
|
||||
|
||||
index := int(hash % u32(cap(table.slots)));
|
||||
for table.slots[index].occupied {
|
||||
if table.slots[index].hash == hash {
|
||||
if table.slots[index].key == key {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if index += 1; index >= cap(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_hash :: proc(s: string) -> u32 { // fnv32a
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..len(s) {
|
||||
h = (h ~ u32(s[i])) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
table: Table(string, int);
|
||||
|
||||
for i in 0..36 do put(&table, "Hellope", i);
|
||||
for i in 0..42 do put(&table, "World!", i);
|
||||
|
||||
found, _ := find(&table, "Hellope");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
found, _ = find(&table, "World!");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
// I would not personally design a hash table like this in production
|
||||
// but this is a nice basic example
|
||||
// A better approach would either use a `u64` or equivalent for the key
|
||||
// and let the user specify the hashing function or make the user store
|
||||
// the hashing procedure with the table
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
prefix_table := [?]string{
|
||||
"White",
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Octarine",
|
||||
"Black",
|
||||
};
|
||||
|
||||
threading_example :: proc() {
|
||||
when ODIN_OS == "windows" {
|
||||
unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
array[index] = array[len(array)-1];
|
||||
pop(array);
|
||||
}
|
||||
ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
copy(array[index..], array[index+1..]);
|
||||
pop(array);
|
||||
}
|
||||
|
||||
worker_proc :: proc(t: ^thread.Thread) -> int {
|
||||
for iteration in 1..5 {
|
||||
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
|
||||
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
|
||||
// win32.sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
threads := make([]^thread.Thread, 0, len(prefix_table));
|
||||
defer free(threads);
|
||||
|
||||
for i in 0..len(prefix_table) {
|
||||
if t := thread.create(worker_proc); t != nil {
|
||||
t.init_context = context;
|
||||
t.use_init_context = true;
|
||||
t.user_index = len(threads);
|
||||
append(&threads, t);
|
||||
thread.start(t);
|
||||
}
|
||||
}
|
||||
|
||||
for len(threads) > 0 {
|
||||
for i := 0; i < len(threads); /**/ {
|
||||
if t := threads[i]; thread.is_done(t) {
|
||||
fmt.printf("Thread %d is done\n", t.user_index);
|
||||
thread.destroy(t);
|
||||
|
||||
ordered_remove(&threads, i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
when false {
|
||||
fmt.println("\n# general_stuff"); general_stuff();
|
||||
fmt.println("\n# default_struct_values"); default_struct_values();
|
||||
fmt.println("\n# union_type"); union_type();
|
||||
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
|
||||
fmt.println("\n# threading_example"); threading_example();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,778 @@
|
||||
import "core:fmt.odin"
|
||||
import "core:strconv.odin"
|
||||
import "core:mem.odin"
|
||||
import "core:bits.odin"
|
||||
import "core:hash.odin"
|
||||
import "core:math.odin"
|
||||
import "core:math/rand.odin"
|
||||
import "core:os.odin"
|
||||
import "core:raw.odin"
|
||||
import "core:sort.odin"
|
||||
import "core:strings.odin"
|
||||
import "core:types.odin"
|
||||
import "core:utf16.odin"
|
||||
import "core:utf8.odin"
|
||||
|
||||
// File scope `when` statements
|
||||
when ODIN_OS == "windows" {
|
||||
import "core:atomics.odin"
|
||||
import "core:thread.odin"
|
||||
import win32 "core:sys/windows.odin"
|
||||
}
|
||||
|
||||
@(link_name="general_stuff")
|
||||
general_stuff :: proc() {
|
||||
fmt.println("# general_stuff");
|
||||
{ // `do` for inline statements rather than block
|
||||
foo :: proc() do fmt.println("Foo!");
|
||||
if false do foo();
|
||||
for false do foo();
|
||||
when false do foo();
|
||||
|
||||
if false do foo();
|
||||
else do foo();
|
||||
}
|
||||
|
||||
{ // Removal of `++` and `--` (again)
|
||||
x: int;
|
||||
x += 1;
|
||||
x -= 1;
|
||||
}
|
||||
{ // Casting syntaxes
|
||||
i := i32(137);
|
||||
ptr := &i;
|
||||
|
||||
_ = (^f32)(ptr);
|
||||
// ^f32(ptr) == ^(f32(ptr))
|
||||
_ = cast(^f32)ptr;
|
||||
|
||||
_ = (^f32)(ptr)^;
|
||||
_ = (cast(^f32)ptr)^;
|
||||
|
||||
// Questions: Should there be two ways to do it?
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove *_val_of built-in procedures
|
||||
* size_of, align_of, offset_of
|
||||
* type_of, type_info_of
|
||||
*/
|
||||
|
||||
{ // `expand_to_tuple` built-in procedure
|
||||
Foo :: struct {
|
||||
x: int,
|
||||
b: bool,
|
||||
}
|
||||
f := Foo{137, true};
|
||||
x, b := expand_to_tuple(f);
|
||||
fmt.println(f);
|
||||
fmt.println(x, b);
|
||||
fmt.println(expand_to_tuple(f));
|
||||
}
|
||||
|
||||
{
|
||||
// .. half-closed range
|
||||
// .. open range
|
||||
|
||||
for in 0..2 {} // 0, 1
|
||||
for in 0..2 {} // 0, 1, 2
|
||||
}
|
||||
|
||||
{ // Multiple sized booleans
|
||||
|
||||
x0: bool; // default
|
||||
x1: b8 = true;
|
||||
x2: b16 = false;
|
||||
x3: b32 = true;
|
||||
x4: b64 = false;
|
||||
|
||||
fmt.printf("x1: %T = %v;\n", x1, x1);
|
||||
fmt.printf("x2: %T = %v;\n", x2, x2);
|
||||
fmt.printf("x3: %T = %v;\n", x3, x3);
|
||||
fmt.printf("x4: %T = %v;\n", x4, x4);
|
||||
|
||||
// Having specific sized booleans is very useful when dealing with foreign code
|
||||
// and to enforce specific alignment for a boolean, especially within a struct
|
||||
}
|
||||
|
||||
{ // `distinct` types
|
||||
// Originally, all type declarations would create a distinct type unless #type_alias was present.
|
||||
// Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
|
||||
// If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct.
|
||||
|
||||
Int32 :: i32;
|
||||
#assert(Int32 == i32);
|
||||
|
||||
My_Int32 :: distinct i32;
|
||||
#assert(My_Int32 != i32);
|
||||
|
||||
My_Struct :: struct{x: int};
|
||||
#assert(My_Struct != struct{x: int});
|
||||
}
|
||||
}
|
||||
|
||||
default_struct_values :: proc() {
|
||||
fmt.println("# default_struct_values");
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
}
|
||||
{
|
||||
// Default values must be constants
|
||||
Vector3 :: struct {
|
||||
x: f32 = 1,
|
||||
y: f32 = 4,
|
||||
z: f32 = 9,
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{};
|
||||
fmt.println(v);
|
||||
|
||||
// Uses the same semantics as a default values in a procedure
|
||||
v = Vector3{137};
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{z = 137};
|
||||
fmt.println(v);
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x := 1.0,
|
||||
y := 4.0,
|
||||
z := 9.0,
|
||||
}
|
||||
stack_default: Vector3;
|
||||
stack_literal := Vector3{};
|
||||
heap_one := new(Vector3); defer free(heap_one);
|
||||
heap_two := new_clone(Vector3{}); defer free(heap_two);
|
||||
|
||||
fmt.println("stack_default - ", stack_default);
|
||||
fmt.println("stack_literal - ", stack_literal);
|
||||
fmt.println("heap_one - ", heap_one^);
|
||||
fmt.println("heap_two - ", heap_two^);
|
||||
|
||||
|
||||
N :: 4;
|
||||
stack_array: [N]Vector3;
|
||||
heap_array := new([N]Vector3); defer free(heap_array);
|
||||
heap_slice := make([]Vector3, N); defer free(heap_slice);
|
||||
fmt.println("stack_array[1] - ", stack_array[1]);
|
||||
fmt.println("heap_array[1] - ", heap_array[1]);
|
||||
fmt.println("heap_slice[1] - ", heap_slice[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
union_type :: proc() {
|
||||
fmt.println("\n# union_type");
|
||||
{
|
||||
val: union{int, bool};
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
{
|
||||
// There is a duality between `any` and `union`
|
||||
// An `any` has a pointer to the data and allows for any type (open)
|
||||
// A `union` has as binary blob to store the data and allows only certain types (closed)
|
||||
// The following code is with `any` but has the same syntax
|
||||
val: any;
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
switch v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 :: struct {x, y, z: f32};
|
||||
Quaternion :: struct {x, y, z: f32, w: f32 = 1};
|
||||
|
||||
// More realistic examples
|
||||
{
|
||||
// NOTE(bill): For the above basic examples, you may not have any
|
||||
// particular use for it. However, my main use for them is not for these
|
||||
// simple cases. My main use is for hierarchical types. Many prefer
|
||||
// subtyping, embedding the base data into the derived types. Below is
|
||||
// an example of this for a basic game Entity.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64,
|
||||
name: string,
|
||||
position: Vector3,
|
||||
orientation: Quaternion,
|
||||
|
||||
derived: any,
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity,
|
||||
jump_height: f32,
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: Entity,
|
||||
is_robot: bool,
|
||||
is_zombie: bool,
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t^;
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
switch e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// NOTE(bill): A union can be used to achieve something similar. Instead
|
||||
// of embedding the base data into the derived types, the derived data
|
||||
// in embedded into the base type. Below is the same example of the
|
||||
// basic game Entity but using an union.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64,
|
||||
name: string,
|
||||
position: Vector3,
|
||||
orientation: Quaternion,
|
||||
|
||||
derived: union {Frog, Monster},
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: ^Entity,
|
||||
jump_height: f32,
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: ^Entity,
|
||||
is_robot: bool,
|
||||
is_zombie: bool,
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(Entity);
|
||||
t.derived = T{entity = t};
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
switch e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
|
||||
// NOTE(bill): As you can see, the usage code has not changed, only its
|
||||
// memory layout. Both approaches have their own advantages but they can
|
||||
// be used together to achieve different results. The subtyping approach
|
||||
// can allow for a greater control of the memory layout and memory
|
||||
// allocation, e.g. storing the derivatives together. However, this is
|
||||
// also its disadvantage. You must either preallocate arrays for each
|
||||
// derivative separation (which can be easily missed) or preallocate a
|
||||
// bunch of "raw" memory; determining the maximum size of the derived
|
||||
// types would require the aid of metaprogramming. Unions solve this
|
||||
// particular problem as the data is stored with the base data.
|
||||
// Therefore, it is possible to preallocate, e.g. [100]Entity.
|
||||
|
||||
// It should be noted that the union approach can have the same memory
|
||||
// layout as the any and with the same type restrictions by using a
|
||||
// pointer type for the derivatives.
|
||||
|
||||
/*
|
||||
Entity :: struct {
|
||||
..
|
||||
derived: union{^Frog, ^Monster},
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity,
|
||||
..
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: Entity,
|
||||
..
|
||||
|
||||
}
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t;
|
||||
return t;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
parametric_polymorphism :: proc() {
|
||||
fmt.println("# parametric_polymorphism");
|
||||
|
||||
print_value :: proc(value: $T) {
|
||||
fmt.printf("print_value: %T %v\n", value, value);
|
||||
}
|
||||
|
||||
v1: int = 1;
|
||||
v2: f32 = 2.1;
|
||||
v3: f64 = 3.14;
|
||||
v4: string = "message";
|
||||
|
||||
print_value(v1);
|
||||
print_value(v2);
|
||||
print_value(v3);
|
||||
print_value(v4);
|
||||
|
||||
fmt.println();
|
||||
|
||||
add :: proc(p, q: $T) -> T {
|
||||
x: T = p + q;
|
||||
return x;
|
||||
}
|
||||
|
||||
a := add(3, 4);
|
||||
fmt.printf("a: %T = %v\n", a, a);
|
||||
|
||||
b := add(3.2, 4.3);
|
||||
fmt.printf("b: %T = %v\n", b, b);
|
||||
|
||||
// This is how `new` is implemented
|
||||
alloc_type :: proc(T: type) -> ^T {
|
||||
t := cast(^T)alloc(size_of(T), align_of(T));
|
||||
t^ = T{}; // Use default initialization value
|
||||
return t;
|
||||
}
|
||||
|
||||
copy_slice :: proc(dst, src: []$T) -> int {
|
||||
return mem.copy(&dst[0], &src[0], n*size_of(T));
|
||||
}
|
||||
|
||||
double_params :: proc(a: $A, b: $B) -> A {
|
||||
return a + A(b);
|
||||
}
|
||||
|
||||
fmt.println(double_params(12, 1.345));
|
||||
|
||||
|
||||
|
||||
{ // Polymorphic Types and Type Specialization
|
||||
Table_Slot :: struct(Key, Value: type) {
|
||||
occupied: bool,
|
||||
hash: u32,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
TABLE_SIZE_MIN :: 32;
|
||||
Table :: struct(Key, Value: type) {
|
||||
count: int,
|
||||
allocator: Allocator,
|
||||
slots: []Table_Slot(Key, Value),
|
||||
}
|
||||
|
||||
// Only allow types that are specializations of a (polymorphic) slice
|
||||
make_slice :: proc(T: type/[]$E, len: int) -> T {
|
||||
return make(T, len);
|
||||
}
|
||||
|
||||
|
||||
// Only allow types that are specializations of `Table`
|
||||
allocate :: proc(table: ^$T/Table, capacity: int) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
context <- c {
|
||||
table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
expand :: proc(table: ^$T/Table) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
context <- c {
|
||||
old_slots := table.slots;
|
||||
|
||||
cap := max(2*len(table.slots), TABLE_SIZE_MIN);
|
||||
allocate(table, cap);
|
||||
|
||||
for s in old_slots do if s.occupied {
|
||||
put(table, s.key, s.value);
|
||||
}
|
||||
|
||||
free(old_slots);
|
||||
}
|
||||
}
|
||||
|
||||
// Polymorphic determination of a polymorphic struct
|
||||
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
|
||||
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
|
||||
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
if f64(table.count) >= 0.75*f64(len(table.slots)) {
|
||||
expand(table);
|
||||
}
|
||||
assert(table.count <= len(table.slots));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = int(hash % u32(len(table.slots)));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
if index += 1; index >= len(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.count += 1;
|
||||
}
|
||||
|
||||
slot := &table.slots[index];
|
||||
slot.occupied = true;
|
||||
slot.hash = hash;
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
}
|
||||
|
||||
|
||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
||||
hash := get_hash(key);
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
return Value{}, false;
|
||||
}
|
||||
return table.slots[index].value, true;
|
||||
}
|
||||
|
||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
||||
if len(table.slots) <= 0 do return -1;
|
||||
|
||||
index := int(hash % u32(len(table.slots)));
|
||||
for table.slots[index].occupied {
|
||||
if table.slots[index].hash == hash {
|
||||
if table.slots[index].key == key {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if index += 1; index >= len(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_hash :: proc(s: string) -> u32 { // fnv32a
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..len(s) {
|
||||
h = (h ~ u32(s[i])) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
table: Table(string, int);
|
||||
|
||||
for i in 0..36 do put(&table, "Hellope", i);
|
||||
for i in 0..42 do put(&table, "World!", i);
|
||||
|
||||
found, _ := find(&table, "Hellope");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
found, _ = find(&table, "World!");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
// I would not personally design a hash table like this in production
|
||||
// but this is a nice basic example
|
||||
// A better approach would either use a `u64` or equivalent for the key
|
||||
// and let the user specify the hashing function or make the user store
|
||||
// the hashing procedure with the table
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
prefix_table := [?]string{
|
||||
"White",
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Octarine",
|
||||
"Black",
|
||||
};
|
||||
|
||||
threading_example :: proc() {
|
||||
when ODIN_OS == "windows" {
|
||||
fmt.println("# threading_example");
|
||||
|
||||
unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
array[index] = array[len(array)-1];
|
||||
pop(array);
|
||||
}
|
||||
ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
copy(array[index..], array[index+1..]);
|
||||
pop(array);
|
||||
}
|
||||
|
||||
worker_proc :: proc(t: ^thread.Thread) -> int {
|
||||
for iteration in 1..5 {
|
||||
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
|
||||
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
|
||||
// win32.sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
|
||||
defer free(threads);
|
||||
|
||||
for in prefix_table {
|
||||
if t := thread.create(worker_proc); t != nil {
|
||||
t.init_context = context;
|
||||
t.use_init_context = true;
|
||||
t.user_index = len(threads);
|
||||
append(&threads, t);
|
||||
thread.start(t);
|
||||
}
|
||||
}
|
||||
|
||||
for len(threads) > 0 {
|
||||
for i := 0; i < len(threads); /**/ {
|
||||
if t := threads[i]; thread.is_done(t) {
|
||||
fmt.printf("Thread %d is done\n", t.user_index);
|
||||
thread.destroy(t);
|
||||
|
||||
ordered_remove(&threads, i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
array_programming :: proc() {
|
||||
fmt.println("# array_programming");
|
||||
{
|
||||
a := [3]f32{1, 2, 3};
|
||||
b := [3]f32{5, 6, 7};
|
||||
c := a * b;
|
||||
d := a + b;
|
||||
e := 1 + (c - d) / 2;
|
||||
fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
|
||||
}
|
||||
|
||||
{
|
||||
a := [3]f32{1, 2, 3};
|
||||
b := swizzle(a, 2, 1, 0);
|
||||
assert(b == [3]f32{3, 2, 1});
|
||||
|
||||
c := swizzle(a, 0, 0);
|
||||
assert(c == [2]f32{1, 1});
|
||||
assert(c == 1);
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: distinct [3]f32;
|
||||
a := Vector3{1, 2, 3};
|
||||
b := Vector3{5, 6, 7};
|
||||
c := (a * b)/2 + 1;
|
||||
d := c.x + c.y + c.z;
|
||||
fmt.printf("%.1f\n", d); // 22.0
|
||||
|
||||
cross :: proc(a, b: Vector3) -> Vector3 {
|
||||
i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
|
||||
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
|
||||
return i - j;
|
||||
}
|
||||
|
||||
blah :: proc(a: Vector3) -> f32 {
|
||||
return a.x + a.y + a.z;
|
||||
}
|
||||
|
||||
x := cross(a, b);
|
||||
fmt.println(x);
|
||||
fmt.println(blah(x));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using println in import "core:fmt.odin"
|
||||
|
||||
using_in :: proc() {
|
||||
fmt.println("# using in");
|
||||
using print in fmt;
|
||||
|
||||
println("Hellope1");
|
||||
print("Hellope2\n");
|
||||
|
||||
Foo :: struct {
|
||||
x, y: int,
|
||||
b: bool,
|
||||
}
|
||||
f: Foo;
|
||||
f.x, f.y = 123, 321;
|
||||
println(f);
|
||||
using x, y in f;
|
||||
x, y = 456, 654;
|
||||
println(f);
|
||||
}
|
||||
|
||||
named_proc_return_parameters :: proc() {
|
||||
fmt.println("# named proc return parameters");
|
||||
|
||||
foo0 :: proc() -> int {
|
||||
return 123;
|
||||
}
|
||||
foo1 :: proc() -> (a: int) {
|
||||
a = 123;
|
||||
return;
|
||||
}
|
||||
foo2 :: proc() -> (a, b: int) {
|
||||
// Named return values act like variables within the scope
|
||||
a = 321;
|
||||
b = 567;
|
||||
return b, a;
|
||||
}
|
||||
fmt.println("foo0 =", foo0()); // 123
|
||||
fmt.println("foo1 =", foo1()); // 123
|
||||
fmt.println("foo2 =", foo2()); // 567 321
|
||||
}
|
||||
|
||||
|
||||
enum_export :: proc() {
|
||||
fmt.println("# enum #export");
|
||||
|
||||
Foo :: enum #export {A, B, C};
|
||||
|
||||
f0 := A;
|
||||
f1 := B;
|
||||
f2 := C;
|
||||
fmt.println(f0, f1, f2);
|
||||
}
|
||||
|
||||
explicit_procedure_overloading :: proc() {
|
||||
fmt.println("# explicit procedure overloading");
|
||||
|
||||
add_ints :: proc(a, b: int) -> int {
|
||||
x := a + b;
|
||||
fmt.println("add_ints", x);
|
||||
return x;
|
||||
}
|
||||
add_floats :: proc(a, b: f32) -> f32 {
|
||||
x := a + b;
|
||||
fmt.println("add_floats", x);
|
||||
return x;
|
||||
}
|
||||
add_numbers :: proc(a: int, b: f32, c: u8) -> int {
|
||||
x := int(a) + int(b) + int(c);
|
||||
fmt.println("add_numbers", x);
|
||||
return x;
|
||||
}
|
||||
|
||||
add :: proc[add_ints, add_floats, add_numbers];
|
||||
|
||||
add(int(1), int(2));
|
||||
add(f32(1), f32(2));
|
||||
add(int(1), f32(2), u8(3));
|
||||
|
||||
add(1, 2); // untyped ints coerce to int tighter than f32
|
||||
add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
|
||||
add(1, 2, 3); // three parameters
|
||||
|
||||
// Ambiguous answers
|
||||
// add(1.0, 2);
|
||||
// add(1, 2.0);
|
||||
}
|
||||
|
||||
complete_switch :: proc() {
|
||||
fmt.println("# complete_switch");
|
||||
{ // enum
|
||||
Foo :: enum #export {
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
}
|
||||
|
||||
b := Foo.B;
|
||||
f := Foo.A;
|
||||
#complete switch f {
|
||||
case A: fmt.println("A");
|
||||
case B: fmt.println("B");
|
||||
case C: fmt.println("C");
|
||||
case D: fmt.println("D");
|
||||
case: fmt.println("?");
|
||||
}
|
||||
}
|
||||
{ // union
|
||||
Foo :: union {int, bool};
|
||||
f: Foo = 123;
|
||||
#complete switch in f {
|
||||
case int: fmt.println("int");
|
||||
case bool: fmt.println("bool");
|
||||
case:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
general_stuff();
|
||||
default_struct_values();
|
||||
union_type();
|
||||
parametric_polymorphism();
|
||||
threading_example();
|
||||
array_programming();
|
||||
using_in();
|
||||
named_proc_return_parameters();
|
||||
enum_export();
|
||||
explicit_procedure_overloading();
|
||||
complete_switch();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
|
||||
}
|
||||
|
||||
v128b :: type {4}u32
|
||||
compile_assert(align_of(v128b) == 16)
|
||||
#assert(align_of(v128b) == 16)
|
||||
|
||||
d, s: ^byte = dst, src
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
import (
|
||||
"fmt.odin";
|
||||
"atomics.odin";
|
||||
"bits.odin";
|
||||
"decimal.odin";
|
||||
"hash.odin";
|
||||
"math.odin";
|
||||
"mem.odin";
|
||||
"opengl.odin";
|
||||
"os.odin";
|
||||
"raw.odin";
|
||||
"strconv.odin";
|
||||
"strings.odin";
|
||||
"sync.odin";
|
||||
"sort.odin";
|
||||
"types.odin";
|
||||
"utf8.odin";
|
||||
"utf16.odin";
|
||||
/*
|
||||
*/
|
||||
)
|
||||
|
||||
|
||||
general_stuff :: proc() {
|
||||
// Complex numbers
|
||||
a := 3 + 4i;
|
||||
b: complex64 = 3 + 4i;
|
||||
c: complex128 = 3 + 4i;
|
||||
d := complex(2, 3);
|
||||
|
||||
e := a / conj(a);
|
||||
fmt.println("(3+4i)/(3-4i) =", e);
|
||||
fmt.println(real(e), "+", imag(e), "i");
|
||||
|
||||
|
||||
// C-style variadic procedures
|
||||
foreign __llvm_core {
|
||||
// The variadic part allows for extra type checking too which C does not provide
|
||||
c_printf :: proc(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf" ---;
|
||||
}
|
||||
str := "%d\n\x00";
|
||||
// c_printf(&str[0], i32(789456123));
|
||||
|
||||
|
||||
Foo :: struct {
|
||||
x: int;
|
||||
y: f32;
|
||||
z: string;
|
||||
}
|
||||
foo := Foo{123, 0.513, "A string"};
|
||||
x, y, z := expand_to_tuple(foo);
|
||||
fmt.println(x, y, z);
|
||||
#assert(type_of(x) == int);
|
||||
#assert(type_of(y) == f32);
|
||||
#assert(type_of(z) == string);
|
||||
|
||||
|
||||
// By default, all variables are zeroed
|
||||
// This can be overridden with the "uninitialized value"
|
||||
// This is similar to `nil` but applied to everything
|
||||
undef_int: int = ---;
|
||||
|
||||
|
||||
// Context system is now implemented using Implicit Parameter Passing (IPP)
|
||||
// The previous implementation was Thread Local Storage (TLS)
|
||||
// IPP has the advantage that it works on systems without TLS and that you can
|
||||
// link the context to the stack frame and thus look at previous contexts
|
||||
//
|
||||
// It does mean that a pointer is implicitly passed procedures with the default
|
||||
// Odin calling convention (#cc_odin)
|
||||
// This can be overridden with something like #cc_contextless or #cc_c if performance
|
||||
// is worried about
|
||||
|
||||
}
|
||||
|
||||
foreign_blocks :: proc() {
|
||||
// See sys/windows.odin
|
||||
}
|
||||
|
||||
default_arguments :: proc() {
|
||||
hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
|
||||
fmt.println("\nTesting default arguments:");
|
||||
hello(1, 2);
|
||||
hello(1);
|
||||
hello();
|
||||
}
|
||||
|
||||
named_arguments :: proc() {
|
||||
Colour :: enum {
|
||||
Red,
|
||||
Orange,
|
||||
Yellow,
|
||||
Green,
|
||||
Blue,
|
||||
Octarine,
|
||||
};
|
||||
using Colour;
|
||||
|
||||
make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
|
||||
fmt.println();
|
||||
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
|
||||
}
|
||||
|
||||
make_character("Frank", "¡Ay, caramba!", Blue, Green);
|
||||
|
||||
|
||||
// As the procedures have more and more parameters, it is very easy
|
||||
// to get many of the arguments in the wrong order especialy if the
|
||||
// types are the same
|
||||
make_character("¡Ay, caramba!", "Frank", Green, Blue);
|
||||
|
||||
// Named arguments help to disambiguate this problem
|
||||
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
|
||||
least_favourite_colour = Green, favourite_colour = Blue);
|
||||
|
||||
|
||||
// The named arguments can be specifed in any order.
|
||||
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
||||
least_favourite_colour = Green, name = "Dennis");
|
||||
|
||||
|
||||
// NOTE: You cannot mix named arguments with normal values
|
||||
/*
|
||||
make_character("Dennis",
|
||||
favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
||||
least_favourite_colour = Green);
|
||||
*/
|
||||
|
||||
|
||||
// Named arguments can also aid with default arguments
|
||||
numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
|
||||
d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
|
||||
g_str := g ? "true" : "false";
|
||||
fmt.printf("How many?! %s: %v\n", s, g_str);
|
||||
}
|
||||
|
||||
numerous_things("First");
|
||||
numerous_things(s = "Second", g = true);
|
||||
|
||||
|
||||
// Default values can be placed anywhere, not just at the end like in other languages
|
||||
weird :: proc(pre: string, mid: int = 0, post: string) {
|
||||
fmt.println(pre, mid, post);
|
||||
}
|
||||
|
||||
weird("How many things", 42, "huh?");
|
||||
weird(pre = "Prefix", post = "Pat");
|
||||
|
||||
}
|
||||
|
||||
|
||||
default_return_values :: proc() {
|
||||
foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
|
||||
match x {
|
||||
case 0: return;
|
||||
case 1: return "Goodbye";
|
||||
case 2: return "Goodbye", "cruel world..";
|
||||
case 3: return second = "cruel world..", first = "Goodbye";
|
||||
}
|
||||
|
||||
return second = "my old friend.";
|
||||
}
|
||||
|
||||
fmt.printf("%s %s\n", foo(0));
|
||||
fmt.printf("%s %s\n", foo(1));
|
||||
fmt.printf("%s %s\n", foo(2));
|
||||
fmt.printf("%s %s\n", foo(3));
|
||||
fmt.printf("%s %s\n", foo(4));
|
||||
fmt.println();
|
||||
|
||||
|
||||
// A more "real" example
|
||||
Error :: enum {
|
||||
None,
|
||||
WhyTheNumberThree,
|
||||
TenIsTooBig,
|
||||
};
|
||||
|
||||
Entity :: struct {
|
||||
name: string;
|
||||
id: u32;
|
||||
}
|
||||
|
||||
some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
|
||||
match {
|
||||
case input == 3: return err = Error.WhyTheNumberThree;
|
||||
case input >= 10: return err = Error.TenIsTooBig;
|
||||
}
|
||||
|
||||
e := new(Entity);
|
||||
e.id = u32(input);
|
||||
|
||||
return result = e;
|
||||
}
|
||||
}
|
||||
|
||||
call_location :: proc() {
|
||||
amazing :: proc(n: int, using loc := #caller_location) {
|
||||
fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
|
||||
fully_pathed_filename, line, column);
|
||||
fmt.printf("Normal -> %d\n", n);
|
||||
fmt.printf("Amazing -> %d\n", n+1);
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
loc := #location(main);
|
||||
fmt.println("`main` is located at", loc);
|
||||
|
||||
fmt.println("This line is located at", #location());
|
||||
fmt.println();
|
||||
|
||||
amazing(3);
|
||||
amazing(4, #location(call_location));
|
||||
|
||||
// See _preload.odin for the implementations of `assert` and `panic`
|
||||
|
||||
}
|
||||
|
||||
|
||||
explicit_parametric_polymorphic_procedures :: proc() {
|
||||
// This is how `new` is actually implemented, see _preload.odin
|
||||
alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
|
||||
|
||||
int_ptr := alloc_type(int);
|
||||
defer free(int_ptr);
|
||||
int_ptr^ = 137;
|
||||
fmt.println(int_ptr, int_ptr^);
|
||||
|
||||
// Named arguments work too!
|
||||
another_ptr := alloc_type(T = f32);
|
||||
defer free(another_ptr);
|
||||
|
||||
|
||||
add :: proc(T: type, args: ..T) -> T {
|
||||
res: T;
|
||||
for arg in args do res += arg;
|
||||
return res;
|
||||
}
|
||||
|
||||
fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
|
||||
|
||||
swap :: proc(T: type, a, b: ^T) {
|
||||
tmp := a^;
|
||||
a^ = b^;
|
||||
b^ = tmp;
|
||||
}
|
||||
|
||||
a, b: int = 3, 4;
|
||||
fmt.println("Pre-swap:", a, b);
|
||||
swap(int, &a, &b);
|
||||
fmt.println("Post-swap:", a, b);
|
||||
a, b = b, a; // Or use this syntax for this silly example case
|
||||
|
||||
|
||||
Vector2 :: struct {x, y: f32;};
|
||||
{
|
||||
// A more complicated example using subtyping
|
||||
// Something like this could be used in a game
|
||||
|
||||
Entity :: struct {
|
||||
using position: Vector2;
|
||||
flags: u64;
|
||||
id: u64;
|
||||
derived: any;
|
||||
}
|
||||
|
||||
Rock :: struct {
|
||||
using entity: Entity;
|
||||
heavy: bool;
|
||||
}
|
||||
Door :: struct {
|
||||
using entity: Entity;
|
||||
open: bool;
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
||||
result := new(T);
|
||||
result.derived = result^;
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
entities: [dynamic]^Entity;
|
||||
|
||||
rock := new_entity(Rock, 3, 5);
|
||||
|
||||
// Named arguments work too!
|
||||
door := new_entity(T = Door, x = 3, y = 6);
|
||||
|
||||
// And named arguments can be any order
|
||||
monster := new_entity(
|
||||
y = 1,
|
||||
x = 2,
|
||||
T = Monster,
|
||||
);
|
||||
|
||||
append(&entities, rock, door, monster);
|
||||
|
||||
fmt.println("Subtyping");
|
||||
for entity in entities {
|
||||
match e in entity.derived {
|
||||
case Rock: fmt.println("Rock", e.x, e.y);
|
||||
case Door: fmt.println("Door", e.x, e.y);
|
||||
case Monster: fmt.println("Monster", e.x, e.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Entity :: struct {
|
||||
using position: Vector2;
|
||||
flags: u64;
|
||||
id: u64;
|
||||
variant: union { Rock, Door, Monster };
|
||||
}
|
||||
|
||||
Rock :: struct {
|
||||
using entity: ^Entity;
|
||||
heavy: bool;
|
||||
}
|
||||
Door :: struct {
|
||||
using entity: ^Entity;
|
||||
open: bool;
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: ^Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
||||
result := new(Entity);
|
||||
result.variant = T{entity = result};
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
|
||||
return cast(^T)&result.variant;
|
||||
}
|
||||
|
||||
entities: [dynamic]^Entity;
|
||||
|
||||
rock := new_entity(Rock, 3, 5);
|
||||
|
||||
// Named arguments work too!
|
||||
door := new_entity(T = Door, x = 3, y = 6);
|
||||
|
||||
// And named arguments can be any order
|
||||
monster := new_entity(
|
||||
y = 1,
|
||||
x = 2,
|
||||
T = Monster,
|
||||
);
|
||||
|
||||
append(&entities, rock, door, monster);
|
||||
|
||||
fmt.println("Union");
|
||||
for entity in entities {
|
||||
match e in entity.variant {
|
||||
case Rock: fmt.println("Rock", e.x, e.y);
|
||||
case Door: fmt.println("Door", e.x, e.y);
|
||||
case Monster: fmt.println("Monster", e.x, e.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
implicit_polymorphic_assignment :: proc() {
|
||||
yep :: proc(p: proc(x: int)) {
|
||||
p(123);
|
||||
}
|
||||
|
||||
frank :: proc(x: $T) do fmt.println("frank ->", x);
|
||||
tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
|
||||
yep(frank);
|
||||
// yep(tim);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
main :: proc() {
|
||||
/*
|
||||
foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
|
||||
foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
|
||||
foo :: proc(x: type) do fmt.println("#3", type_info(x));
|
||||
|
||||
f :: foo;
|
||||
|
||||
f(y = 3785.1546, x = 123);
|
||||
f(x = int, y = 897.513);
|
||||
f(x = f32);
|
||||
|
||||
general_stuff();
|
||||
foreign_blocks();
|
||||
default_arguments();
|
||||
named_arguments();
|
||||
default_return_values();
|
||||
call_location();
|
||||
explicit_parametric_polymorphic_procedures();
|
||||
implicit_polymorphic_assignment();
|
||||
|
||||
|
||||
// Command line argument(s)!
|
||||
// -opt=0,1,2,3
|
||||
*/
|
||||
/*
|
||||
program := "+ + * - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
match token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
*/
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 72 KiB |
+4
-1
@@ -1,7 +1,10 @@
|
||||
@echo off
|
||||
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
|
||||
set _NO_DEBUG_HEAP=1
|
||||
|
||||
set path=w:\Odin\misc;%path%
|
||||
|
||||
-229
@@ -1,229 +0,0 @@
|
||||
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
|
||||
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
||||
|
||||
#define Array(Type_) struct { \
|
||||
gbAllocator allocator; \
|
||||
Type_ * e; \
|
||||
isize count; \
|
||||
isize capacity; \
|
||||
}
|
||||
|
||||
typedef Array(void) ArrayVoid;
|
||||
|
||||
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = 0; \
|
||||
(x_)->capacity = (init_capacity_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init_count(x_, allocator_, init_count_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = (init_count_); \
|
||||
(x_)->capacity = (init_count_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
|
||||
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
|
||||
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
|
||||
|
||||
#define array_grow(x_, min_capacity_) do { \
|
||||
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
|
||||
if (new_capacity < (min_capacity_)) { \
|
||||
new_capacity = (min_capacity_); \
|
||||
} \
|
||||
array_set_capacity(x_, new_capacity); \
|
||||
} while (0)
|
||||
|
||||
#define array_add(x_, item_) do { \
|
||||
if ((x_)->capacity < (x_)->count+1) { \
|
||||
array_grow(x_, 0); \
|
||||
} \
|
||||
(x_)->e[(x_)->count++] = item_; \
|
||||
} while (0)
|
||||
|
||||
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
|
||||
#define array_clear(x_) do { (x_)->count = 0; } while (0)
|
||||
|
||||
#define array_resize(x_, new_count_) do { \
|
||||
if ((x_)->capacity < (new_count_)) { \
|
||||
array_grow((x_), (new_count_)); \
|
||||
} \
|
||||
(x_)->count = (new_count_); \
|
||||
} while (0)
|
||||
|
||||
#define array_reserve(x_, new_capacity_) do { \
|
||||
if ((x_)->capacity < (new_capacity_)) { \
|
||||
array_set_capacity((x_), (new_capacity_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
ArrayVoid *x = cast(ArrayVoid *)ptr;
|
||||
GB_ASSERT(ptr != NULL);
|
||||
|
||||
GB_ASSERT(element_size > 0);
|
||||
|
||||
if (capacity == x->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < x->count) {
|
||||
if (x->capacity < capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
|
||||
if (new_capacity < capacity) {
|
||||
new_capacity = capacity;
|
||||
}
|
||||
array__set_capacity(ptr, new_capacity, element_size);
|
||||
}
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
|
||||
x->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
template <typename T>
|
||||
struct Array {
|
||||
gbAllocator allocator;
|
||||
T * data;
|
||||
isize count;
|
||||
isize capacity;
|
||||
|
||||
T &operator[](isize index) {
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
||||
return data[index];
|
||||
}
|
||||
|
||||
T const &operator[](isize index) const {
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
||||
return data[index];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> void array_init (Array<T> *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0));
|
||||
template <typename T> void array_init_count (Array<T> *array, gbAllocator a, isize count);
|
||||
template <typename T> Array<T> array_make (T *data, isize count, isize capacity);
|
||||
template <typename T> void array_free (Array<T> *array);
|
||||
template <typename T> void array_add (Array<T> *array, T const &t);
|
||||
template <typename T> T array_pop (Array<T> *array);
|
||||
template <typename T> void array_clear (Array<T> *array);
|
||||
template <typename T> void array_reserve (Array<T> *array, isize capacity);
|
||||
template <typename T> void array_resize (Array<T> *array, isize count);
|
||||
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
|
||||
|
||||
|
||||
template <typename T>
|
||||
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
||||
array->allocator = a;
|
||||
array->data = gb_alloc_array(a, T, init_capacity);
|
||||
array->count = 0;
|
||||
array->capacity = init_capacity;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_init_count(Array<T> *array, gbAllocator a, isize count) {
|
||||
array->allocator = a;
|
||||
array->data = gb_alloc_array(a, T, count);
|
||||
array->count = count;
|
||||
array->capacity = count;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
Array<T> array_make(T *data, isize count, isize capacity) {
|
||||
Array<T> a = {0};
|
||||
a.data = data;
|
||||
a.count = count;
|
||||
a.capacity = capacity;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void array_free(Array<T> *array) {
|
||||
if (array->allocator.proc != NULL) {
|
||||
gb_free(array->allocator, array->data);
|
||||
}
|
||||
array->count = 0;
|
||||
array->capacity = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array__grow(Array<T> *array, isize min_capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(array->capacity);
|
||||
if (new_capacity < min_capacity) {
|
||||
new_capacity = min_capacity;
|
||||
}
|
||||
array_set_capacity(array, new_capacity);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_add(Array<T> *array, T const &t) {
|
||||
if (array->capacity < array->count+1) {
|
||||
array__grow(array, 0);
|
||||
}
|
||||
array->data[array->count] = t;
|
||||
array->count++;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T array_pop(Array<T> *array) {
|
||||
GB_ASSERT(array->count > 0);
|
||||
array->count--;
|
||||
return array->data[array->count];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_clear(Array<T> *array) {
|
||||
array->count = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_reserve(Array<T> *array, isize capacity) {
|
||||
if (array->capacity < capacity) {
|
||||
array_set_capacity(array, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_resize(Array<T> *array, isize count) {
|
||||
if (array->capacity < count) {
|
||||
array__grow(array, count);
|
||||
}
|
||||
array->count = count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_set_capacity(Array<T> *array, isize capacity) {
|
||||
if (capacity == array->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < array->count) {
|
||||
array_resize(array, capacity);
|
||||
}
|
||||
|
||||
T *new_data = NULL;
|
||||
if (capacity > 0) {
|
||||
new_data = gb_alloc_array(array->allocator, T, capacity);
|
||||
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
|
||||
}
|
||||
gb_free(array->allocator, array->data);
|
||||
array->data = new_data;
|
||||
array->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
+336
@@ -0,0 +1,336 @@
|
||||
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
|
||||
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
||||
|
||||
#if 1
|
||||
template <typename T>
|
||||
struct Array {
|
||||
gbAllocator allocator;
|
||||
T * data;
|
||||
isize count;
|
||||
isize capacity;
|
||||
|
||||
T &operator[](isize index) {
|
||||
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||
#endif
|
||||
return data[index];
|
||||
}
|
||||
|
||||
T const &operator[](isize index) const {
|
||||
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||
#endif
|
||||
return data[index];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> void array_init (Array<T> *array, gbAllocator const &a);
|
||||
template <typename T> void array_init (Array<T> *array, gbAllocator const &a, isize count);
|
||||
template <typename T> void array_init (Array<T> *array, gbAllocator const &a, isize count, isize capacity);
|
||||
template <typename T> Array<T> array_make (gbAllocator const &a);
|
||||
template <typename T> Array<T> array_make (gbAllocator const &a, isize count);
|
||||
template <typename T> Array<T> array_make (gbAllocator const &a, isize count, isize capacity);
|
||||
template <typename T> Array<T> array_make_from_ptr (T *data, isize count, isize capacity);
|
||||
template <typename T> void array_free (Array<T> *array);
|
||||
template <typename T> void array_add (Array<T> *array, T const &t);
|
||||
template <typename T> T array_pop (Array<T> *array);
|
||||
template <typename T> void array_clear (Array<T> *array);
|
||||
template <typename T> void array_reserve (Array<T> *array, isize capacity);
|
||||
template <typename T> void array_resize (Array<T> *array, isize count);
|
||||
template <typename T> void array_set_capacity (Array<T> *array, isize capacity);
|
||||
template <typename T> Array<T> array_slice (Array<T> const &array, isize lo, isize hi);
|
||||
|
||||
|
||||
template <typename T> void array_ordered_remove (Array<T> *array, isize index);
|
||||
template <typename T> void array_unordered_remove(Array<T> *array, isize index);
|
||||
|
||||
|
||||
template <typename T>
|
||||
void array_copy(Array<T> *array, Array<T> const &data, isize offset) {
|
||||
gb_memmove(array->data+offset, data.data, gb_size_of(T)*data.count);
|
||||
}
|
||||
template <typename T>
|
||||
void array_copy(Array<T> *array, Array<T> const &data, isize offset, isize count) {
|
||||
gb_memmove(array->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
T *array_end_ptr(Array<T> *array) {
|
||||
if (array->count > 0) {
|
||||
return &array->data[array->count-1];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline void array_init(Array<T> *array, gbAllocator const &a) {
|
||||
isize cap = ARRAY_GROW_FORMULA(0);
|
||||
array_init(array, a, 0, cap);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count) {
|
||||
array_init(array, a, count, count);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void array_init(Array<T> *array, gbAllocator const &a, isize count, isize capacity) {
|
||||
array->allocator = a;
|
||||
array->data = nullptr;
|
||||
if (capacity > 0) {
|
||||
array->data = gb_alloc_array(a, T, capacity);
|
||||
}
|
||||
array->count = count;
|
||||
array->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline Array<T> array_make_from_ptr(T *data, isize count, isize capacity) {
|
||||
Array<T> a = {0};
|
||||
a.data = data;
|
||||
a.count = count;
|
||||
a.capacity = capacity;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline Array<T> array_make(gbAllocator const &a) {
|
||||
isize capacity = ARRAY_GROW_FORMULA(0);
|
||||
Array<T> array = {};
|
||||
array.allocator = a;
|
||||
array.data = gb_alloc_array(a, T, capacity);
|
||||
array.count = 0;
|
||||
array.capacity = capacity;
|
||||
return array;
|
||||
}
|
||||
template <typename T>
|
||||
gb_inline Array<T> array_make(gbAllocator const &a, isize count) {
|
||||
Array<T> array = {};
|
||||
array.allocator = a;
|
||||
array.data = gb_alloc_array(a, T, count);
|
||||
array.count = count;
|
||||
array.capacity = count;
|
||||
return array;
|
||||
}
|
||||
template <typename T>
|
||||
gb_inline Array<T> array_make(gbAllocator const &a, isize count, isize capacity) {
|
||||
Array<T> array = {};
|
||||
array.allocator = a;
|
||||
array.data = gb_alloc_array(a, T, capacity);
|
||||
array.count = count;
|
||||
array.capacity = capacity;
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline void array_free(Array<T> *array) {
|
||||
if (array->allocator.proc != nullptr) {
|
||||
gb_free(array->allocator, array->data);
|
||||
}
|
||||
array->count = 0;
|
||||
array->capacity = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array__grow(Array<T> *array, isize min_capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(array->capacity);
|
||||
if (new_capacity < min_capacity) {
|
||||
new_capacity = min_capacity;
|
||||
}
|
||||
array_set_capacity(array, new_capacity);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_add(Array<T> *array, T const &t) {
|
||||
if (array->capacity < array->count+1) {
|
||||
array__grow(array, 0);
|
||||
}
|
||||
array->data[array->count] = t;
|
||||
array->count++;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline T array_pop(Array<T> *array) {
|
||||
GB_ASSERT(array->count > 0);
|
||||
array->count--;
|
||||
return array->data[array->count];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_clear(Array<T> *array) {
|
||||
array->count = 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_reserve(Array<T> *array, isize capacity) {
|
||||
if (array->capacity < capacity) {
|
||||
array_set_capacity(array, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_resize(Array<T> *array, isize count) {
|
||||
if (array->capacity < count) {
|
||||
array__grow(array, count);
|
||||
}
|
||||
array->count = count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_set_capacity(Array<T> *array, isize capacity) {
|
||||
if (capacity == array->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < array->count) {
|
||||
array_resize(array, capacity);
|
||||
}
|
||||
|
||||
T *new_data = nullptr;
|
||||
if (capacity > 0) {
|
||||
new_data = gb_alloc_array(array->allocator, T, capacity);
|
||||
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
|
||||
}
|
||||
gb_free(array->allocator, array->data);
|
||||
array->data = new_data;
|
||||
array->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline Array<T> array_slice(Array<T> const &array, isize lo, isize hi) {
|
||||
GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
|
||||
Array<T> out = {};
|
||||
isize len = hi-lo;
|
||||
if (len > 0) {
|
||||
out.data = array.data+lo;
|
||||
out.count = len;
|
||||
out.capacity = len;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
template <typename T>
|
||||
void array_ordered_remove(Array<T> *array, isize index) {
|
||||
GB_ASSERT(0 <= index && index < array->count);
|
||||
|
||||
isize bytes = gb_size_of(T) * (array->count-(index+1));
|
||||
gb_memmove(array->data+index, array->data+index+1, bytes);
|
||||
array->count -= 1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void array_unordered_remove(Array<T> *array, isize index) {
|
||||
GB_ASSERT(0 <= index && index < array->count);
|
||||
|
||||
isize n = array->count-1;
|
||||
if (index != n) {
|
||||
gb_memmove(array->data+index, array->data+n, gb_size_of(T));
|
||||
}
|
||||
array_pop(array);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define Array(Type_) struct { \
|
||||
gbAllocator const &allocator; \
|
||||
Type_ * e; \
|
||||
isize count; \
|
||||
isize capacity; \
|
||||
}
|
||||
|
||||
typedef Array(void) ArrayVoid;
|
||||
|
||||
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != nullptr); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = 0; \
|
||||
(x_)->capacity = (init_capacity_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init_count(x_, allocator_, init_count_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != nullptr); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = (init_count_); \
|
||||
(x_)->capacity = (init_count_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
|
||||
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
|
||||
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
|
||||
|
||||
#define array_grow(x_, min_capacity_) do { \
|
||||
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
|
||||
if (new_capacity < (min_capacity_)) { \
|
||||
new_capacity = (min_capacity_); \
|
||||
} \
|
||||
array_set_capacity(x_, new_capacity); \
|
||||
} while (0)
|
||||
|
||||
#define array_add(x_, item_) do { \
|
||||
if ((x_)->capacity < (x_)->count+1) { \
|
||||
array_grow(x_, 0); \
|
||||
} \
|
||||
(x_)->e[(x_)->count++] = item_; \
|
||||
} while (0)
|
||||
|
||||
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
|
||||
#define array_clear(x_) do { (x_)->count = 0; } while (0)
|
||||
|
||||
#define array_resize(x_, new_count_) do { \
|
||||
if ((x_)->capacity < (new_count_)) { \
|
||||
array_grow((x_), (new_count_)); \
|
||||
} \
|
||||
(x_)->count = (new_count_); \
|
||||
} while (0)
|
||||
|
||||
#define array_reserve(x_, new_capacity_) do { \
|
||||
if ((x_)->capacity < (new_capacity_)) { \
|
||||
array_set_capacity((x_), (new_capacity_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
ArrayVoid *x = cast(ArrayVoid *)ptr;
|
||||
GB_ASSERT(ptr != nullptr);
|
||||
|
||||
GB_ASSERT(element_size > 0);
|
||||
|
||||
if (capacity == x->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < x->count) {
|
||||
if (x->capacity < capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
|
||||
if (new_capacity < capacity) {
|
||||
new_capacity = capacity;
|
||||
}
|
||||
array__set_capacity(ptr, new_capacity, element_size);
|
||||
}
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
|
||||
x->capacity = capacity;
|
||||
}
|
||||
#endif
|
||||
|
||||
+1434
File diff suppressed because it is too large
Load Diff
@@ -1,328 +0,0 @@
|
||||
// This stores the information for the specify architecture of this build
|
||||
typedef struct BuildContext {
|
||||
// Constants
|
||||
String ODIN_OS; // target operating system
|
||||
String ODIN_ARCH; // target architecture
|
||||
String ODIN_ENDIAN; // target endian
|
||||
String ODIN_VENDOR; // compiler vendor
|
||||
String ODIN_VERSION; // compiler version
|
||||
String ODIN_ROOT; // Odin ROOT
|
||||
|
||||
// In bytes
|
||||
i64 word_size; // Size of a pointer, must be >= 4
|
||||
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
|
||||
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
bool is_dll;
|
||||
} BuildContext;
|
||||
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
|
||||
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
// is_file
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(wchar_t) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
|
||||
if (len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(NULL, text, len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(char) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
int sz = path_buf.count;
|
||||
int res = _NSGetExecutablePath(&path_buf.e[0], &sz);
|
||||
if(res == 0) {
|
||||
len = sz;
|
||||
break;
|
||||
} else {
|
||||
array_resize(&path_buf, sz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
gb_memmove(text, &path_buf.e[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
// array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
|
||||
// NOTE: Linux / Unix is unfinished and not tested very well.
|
||||
#include <sys/stat.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(char) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
// This is not a 100% reliable system, but for the purposes
|
||||
// of this compiler, it should be _good enough_.
|
||||
// That said, there's no solid 100% method on Linux to get the program's
|
||||
// path without checking this link. Sorry.
|
||||
len = readlink("/proc/self/exe", &path_buf.e[0], path_buf.count);
|
||||
if(len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
gb_memmove(text, &path_buf.e[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
String result = {0};
|
||||
|
||||
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(string16.text, len, text, NULL);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return result;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
char *p = realpath(cast(char *)s.text, 0);
|
||||
if(p == NULL) return make_string_c("");
|
||||
|
||||
return make_string_c(p);
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
|
||||
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
|
||||
String res = {0};
|
||||
isize str_len = base_dir.len+path.len;
|
||||
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
|
||||
gb_memmove(str+i, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get_fullpath_core(gbAllocator a, String path) {
|
||||
String module_dir = odin_root_dir();
|
||||
String res = {0};
|
||||
|
||||
char core[] = "core/";
|
||||
isize core_len = gb_size_of(core)-1;
|
||||
|
||||
isize str_len = module_dir.len + core_len + path.len;
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
gb_memmove(str, module_dir.text, module_dir.len);
|
||||
gb_memmove(str+module_dir.len, core, core_len);
|
||||
gb_memmove(str+module_dir.len+core_len, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void init_build_context(void) {
|
||||
BuildContext *bc = &build_context;
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = str_lit("0.3.0");
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bc->ODIN_OS = str_lit("windows");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
bc->ODIN_ENDIAN = str_lit("little");
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
bc->ODIN_OS = str_lit("osx");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
bc->ODIN_ENDIAN = str_lit("little");
|
||||
#else
|
||||
bc->ODIN_OS = str_lit("linux");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
bc->ODIN_ENDIAN = str_lit("little");
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
// here, so I just #defined the linker flags to keep things concise.
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
#define LINK_FLAG_X64 "/machine:x64"
|
||||
#define LINK_FLAG_X86 "/machine:x86"
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
// NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
|
||||
// an architecture option. All compilation done on MacOS must be x64.
|
||||
GB_ASSERT(str_eq(bc->ODIN_ARCH, str_lit("amd64")));
|
||||
|
||||
#define LINK_FLAG_X64 ""
|
||||
#define LINK_FLAG_X86 ""
|
||||
#else
|
||||
// Linux, but also BSDs and the like.
|
||||
// NOTE(zangent): When clang is swapped out with ld as the linker,
|
||||
// the commented flags here should be used. Until then, we'll have
|
||||
// to use alternative build flags made for clang.
|
||||
/*
|
||||
#define LINK_FLAG_X64 "-m elf_x86_64"
|
||||
#define LINK_FLAG_X86 "-m elf_i386"
|
||||
*/
|
||||
#define LINK_FLAG_X64 "-arch x86-64"
|
||||
#define LINK_FLAG_X86 "-arch x86"
|
||||
#endif
|
||||
|
||||
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
|
||||
bc->word_size = 8;
|
||||
bc->max_align = 16;
|
||||
bc->llc_flags = str_lit("-march=x86-64 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X64 " ");
|
||||
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
|
||||
bc->word_size = 4;
|
||||
bc->max_align = 8;
|
||||
bc->llc_flags = str_lit("-march=x86 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
|
||||
}
|
||||
|
||||
#undef LINK_FLAG_X64
|
||||
#undef LINK_FLAG_X86
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
enum TargetOsKind {
|
||||
TargetOs_Invalid,
|
||||
|
||||
TargetOs_windows,
|
||||
TargetOs_osx,
|
||||
TargetOs_linux,
|
||||
TargetOs_essence,
|
||||
|
||||
TargetOs_COUNT,
|
||||
};
|
||||
|
||||
enum TargetArchKind {
|
||||
TargetArch_Invalid,
|
||||
|
||||
TargetArch_amd64,
|
||||
TargetArch_386,
|
||||
|
||||
TargetArch_COUNT,
|
||||
};
|
||||
|
||||
enum TargetEndianKind {
|
||||
TargetEndian_Invalid,
|
||||
|
||||
TargetEndian_Little,
|
||||
TargetEndian_Big,
|
||||
|
||||
TargetEndian_COUNT,
|
||||
};
|
||||
|
||||
String target_os_names[TargetOs_COUNT] = {
|
||||
str_lit(""),
|
||||
str_lit("windows"),
|
||||
str_lit("osx"),
|
||||
str_lit("linux"),
|
||||
str_lit("essence"),
|
||||
};
|
||||
|
||||
String target_arch_names[TargetArch_COUNT] = {
|
||||
str_lit(""),
|
||||
str_lit("amd64"),
|
||||
str_lit("386"),
|
||||
};
|
||||
|
||||
String target_endian_names[TargetEndian_COUNT] = {
|
||||
str_lit(""),
|
||||
str_lit("little"),
|
||||
str_lit("big"),
|
||||
};
|
||||
|
||||
TargetEndianKind target_endians[TargetArch_COUNT] = {
|
||||
TargetEndian_Invalid,
|
||||
TargetEndian_Little,
|
||||
TargetEndian_Little,
|
||||
};
|
||||
|
||||
|
||||
|
||||
String const ODIN_VERSION = str_lit("0.9.0");
|
||||
String cross_compile_target = str_lit("");
|
||||
String cross_compile_lib_dir = str_lit("");
|
||||
|
||||
|
||||
|
||||
struct TargetMetrics {
|
||||
TargetOsKind os;
|
||||
TargetArchKind arch;
|
||||
isize word_size;
|
||||
isize max_align;
|
||||
};
|
||||
|
||||
|
||||
// This stores the information for the specify architecture of this build
|
||||
struct BuildContext {
|
||||
// Constants
|
||||
String ODIN_OS; // target operating system
|
||||
String ODIN_ARCH; // target architecture
|
||||
String ODIN_ENDIAN; // target endian
|
||||
String ODIN_VENDOR; // compiler vendor
|
||||
String ODIN_VERSION; // compiler version
|
||||
String ODIN_ROOT; // Odin ROOT
|
||||
bool ODIN_DEBUG; // Odin in debug mode
|
||||
|
||||
// In bytes
|
||||
i64 word_size; // Size of a pointer, must be >= 4
|
||||
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
|
||||
|
||||
String command;
|
||||
|
||||
TargetMetrics metrics;
|
||||
|
||||
String out_filepath;
|
||||
String resource_filepath;
|
||||
bool has_resource;
|
||||
String opt_flags;
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
bool is_dll;
|
||||
bool generate_docs;
|
||||
i32 optimization_level;
|
||||
bool show_timings;
|
||||
bool keep_temp_files;
|
||||
bool no_bounds_check;
|
||||
bool no_output_files;
|
||||
bool no_crt;
|
||||
bool use_lld;
|
||||
|
||||
gbAffinity affinity;
|
||||
isize thread_count;
|
||||
};
|
||||
|
||||
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
|
||||
|
||||
|
||||
gb_global TargetMetrics target_windows_386 = {
|
||||
TargetOs_windows,
|
||||
TargetArch_386,
|
||||
4,
|
||||
8,
|
||||
};
|
||||
gb_global TargetMetrics target_windows_amd64 = {
|
||||
TargetOs_windows,
|
||||
TargetArch_amd64,
|
||||
8,
|
||||
16,
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_linux_386 = {
|
||||
TargetOs_linux,
|
||||
TargetArch_386,
|
||||
4,
|
||||
8,
|
||||
};
|
||||
gb_global TargetMetrics target_linux_amd64 = {
|
||||
TargetOs_linux,
|
||||
TargetArch_amd64,
|
||||
8,
|
||||
16,
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_osx_amd64 = {
|
||||
TargetOs_osx,
|
||||
TargetArch_amd64,
|
||||
8,
|
||||
16,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
TargetOsKind get_target_os_from_string(String str) {
|
||||
for (isize i = 0; i < TargetOs_COUNT; i++) {
|
||||
if (str_eq_ignore_case(target_os_names[i], str)) {
|
||||
return cast(TargetOsKind)i;
|
||||
}
|
||||
}
|
||||
return TargetOs_Invalid;
|
||||
}
|
||||
|
||||
TargetArchKind get_target_arch_from_string(String str) {
|
||||
for (isize i = 0; i < TargetArch_COUNT; i++) {
|
||||
if (str_eq_ignore_case(target_arch_names[i], str)) {
|
||||
return cast(TargetArchKind)i;
|
||||
}
|
||||
}
|
||||
return TargetArch_Invalid;
|
||||
}
|
||||
|
||||
|
||||
bool is_excluded_target_filename(String name) {
|
||||
String const ext = str_lit(".odin");
|
||||
String original_name = name;
|
||||
GB_ASSERT(string_ends_with(name, ext));
|
||||
name = substring(name, 0, name.len-ext.len);
|
||||
|
||||
String str1 = {};
|
||||
String str2 = {};
|
||||
isize n = 0;
|
||||
|
||||
str1 = name;
|
||||
n = str1.len;
|
||||
for (isize i = str1.len-1; i >= 0 && str1[i] != '_'; i--) {
|
||||
n -= 1;
|
||||
}
|
||||
str1 = substring(str1, n, str1.len);
|
||||
|
||||
str2 = substring(name, 0, gb_max(n-1, 0));
|
||||
n = str2.len;
|
||||
for (isize i = str2.len-1; i >= 0 && str2[i] != '_'; i--) {
|
||||
n -= 1;
|
||||
}
|
||||
str2 = substring(str2, n, str2.len);
|
||||
|
||||
if (str1 == name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TargetOsKind os1 = get_target_os_from_string(str1);
|
||||
TargetArchKind arch1 = get_target_arch_from_string(str1);
|
||||
TargetOsKind os2 = get_target_os_from_string(str2);
|
||||
TargetArchKind arch2 = get_target_arch_from_string(str2);
|
||||
|
||||
if (os1 != TargetOs_Invalid && arch2 != TargetArch_Invalid) {
|
||||
return os1 != build_context.metrics.os || arch2 != build_context.metrics.arch;
|
||||
} else if (arch1 != TargetArch_Invalid && os2 != TargetOs_Invalid) {
|
||||
return arch1 != build_context.metrics.arch || os2 != build_context.metrics.os;
|
||||
} else if (os1 != TargetOs_Invalid) {
|
||||
return os1 != build_context.metrics.os;
|
||||
} else if (arch1 != TargetArch_Invalid) {
|
||||
return arch1 != build_context.metrics.arch;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
struct LibraryCollections {
|
||||
String name;
|
||||
String path;
|
||||
};
|
||||
|
||||
gb_global Array<LibraryCollections> library_collections = {0};
|
||||
|
||||
void add_library_collection(String name, String path) {
|
||||
// TODO(bill): Check the path is valid and a directory
|
||||
LibraryCollections lc = {name, string_trim_whitespace(path)};
|
||||
array_add(&library_collections, lc);
|
||||
}
|
||||
|
||||
bool find_library_collection_path(String name, String *path) {
|
||||
for_array(i, library_collections) {
|
||||
if (library_collections[i].name == name) {
|
||||
if (path) *path = library_collections[i].path;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
// is_file
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<wchar_t>(heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count);
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
len += 1; // NOTE(bill): It needs an extra 1 for some reason
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(nullptr, text, cast(int)len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
u32 sz = path_buf.count;
|
||||
int res = _NSGetExecutablePath(&path_buf[0], &sz);
|
||||
if(res == 0) {
|
||||
len = sz;
|
||||
break;
|
||||
} else {
|
||||
array_resize(&path_buf, sz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
// array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
|
||||
// NOTE: Linux / Unix is unfinished and not tested very well.
|
||||
#include <sys/stat.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
// This is not a 100% reliable system, but for the purposes
|
||||
// of this compiler, it should be _good enough_.
|
||||
// That said, there's no solid 100% method on Linux to get the program's
|
||||
// path without checking this link. Sorry.
|
||||
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
|
||||
if(len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
String result = {};
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
|
||||
DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(&string16[0], len, text, nullptr);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
result = string_trim_whitespace(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
char *p;
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
p = realpath(cast(char *)s.text, 0);
|
||||
gb_mutex_unlock(&string_buffer_mutex);
|
||||
if(p == nullptr) return String{};
|
||||
return make_string_c(p);
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
|
||||
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, base_dir.len+1+path.len+1);
|
||||
defer (gb_free(heap_allocator(), str));
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
gb_memmove(str+i, path.text, path.len); i += path.len;
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return path_to_fullpath(a, res);
|
||||
}
|
||||
|
||||
|
||||
String get_fullpath_core(gbAllocator a, String path) {
|
||||
String module_dir = odin_root_dir();
|
||||
|
||||
String core = str_lit("core/");
|
||||
|
||||
isize str_len = module_dir.len + core.len + path.len;
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
defer (gb_free(heap_allocator(), str));
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, module_dir.text, module_dir.len); i += module_dir.len;
|
||||
gb_memmove(str+i, core.text, core.len); i += core.len;
|
||||
gb_memmove(str+i, path.text, path.len); i += path.len;
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return path_to_fullpath(a, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void init_build_context(void) {
|
||||
BuildContext *bc = &build_context;
|
||||
|
||||
gb_affinity_init(&bc->affinity);
|
||||
if (bc->thread_count == 0) {
|
||||
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
|
||||
}
|
||||
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = ODIN_VERSION;
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
TargetMetrics metrics = {};
|
||||
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
metrics = target_windows_amd64;
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
metrics = target_osx_amd64;
|
||||
#else
|
||||
metrics = target_linux_amd64;
|
||||
#endif
|
||||
#else
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
metrics = target_windows_386;
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
#error "Unsupported architecture"
|
||||
#else
|
||||
metrics = target_linux_386;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (cross_compile_target.len) {
|
||||
bc->ODIN_OS = cross_compile_target;
|
||||
}
|
||||
|
||||
GB_ASSERT(metrics.os != TargetOs_Invalid);
|
||||
GB_ASSERT(metrics.arch != TargetArch_Invalid);
|
||||
GB_ASSERT(metrics.word_size > 1);
|
||||
GB_ASSERT(metrics.max_align > 1);
|
||||
|
||||
|
||||
bc->metrics = metrics;
|
||||
bc->ODIN_OS = target_os_names[metrics.os];
|
||||
bc->ODIN_ARCH = target_arch_names[metrics.arch];
|
||||
bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics.arch]];
|
||||
bc->word_size = metrics.word_size;
|
||||
bc->max_align = metrics.max_align;
|
||||
bc->link_flags = str_lit(" ");
|
||||
bc->opt_flags = str_lit(" ");
|
||||
|
||||
|
||||
gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64);
|
||||
if (bc->ODIN_DEBUG) {
|
||||
// llc_flags = gb_string_appendc(llc_flags, "-debug-compile ");
|
||||
}
|
||||
|
||||
// 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
|
||||
// here, so I just #defined the linker flags to keep things concise.
|
||||
if (bc->metrics.arch == TargetArch_amd64) {
|
||||
llc_flags = gb_string_appendc(llc_flags, "-march=x86-64 ");
|
||||
|
||||
switch (bc->metrics.os) {
|
||||
case TargetOs_windows:
|
||||
bc->link_flags = str_lit("/machine:x64 ");
|
||||
break;
|
||||
case TargetOs_osx:
|
||||
break;
|
||||
case TargetOs_linux:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
}
|
||||
} else if (bc->metrics.arch == TargetArch_386) {
|
||||
llc_flags = gb_string_appendc(llc_flags, "-march=x86 ");
|
||||
|
||||
switch (bc->metrics.os) {
|
||||
case TargetOs_windows:
|
||||
bc->link_flags = str_lit("/machine:x86 ");
|
||||
break;
|
||||
case TargetOs_osx:
|
||||
gb_printf_err("Unsupported architecture\n");
|
||||
gb_exit(1);
|
||||
break;
|
||||
case TargetOs_linux:
|
||||
bc->link_flags = str_lit("-arch x86 ");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gb_printf_err("Unsupported architecture\n");;
|
||||
gb_exit(1);
|
||||
}
|
||||
|
||||
bc->llc_flags = make_string_c(llc_flags);
|
||||
|
||||
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
||||
|
||||
gbString opt_flags = gb_string_make_reserve(heap_allocator(), 64);
|
||||
if (bc->optimization_level != 0) {
|
||||
opt_flags = gb_string_append_fmt(opt_flags, "-O%d ", bc->optimization_level);
|
||||
// NOTE(lachsinc): The following options were previously passed during call
|
||||
// to opt in main.cpp:exec_llvm_opt().
|
||||
// -die: Dead instruction elimination
|
||||
// -memcpyopt: MemCpy optimization
|
||||
}
|
||||
if (bc->ODIN_DEBUG == false) {
|
||||
opt_flags = gb_string_appendc(opt_flags, "-memcpyopt -die ");
|
||||
}
|
||||
|
||||
// NOTE(lachsinc): This optimization option was previously required to get
|
||||
// around an issue in fmt.odin. Thank bp for tracking it down! Leaving for now until the issue
|
||||
// is resolved and confirmed by Bill. Maybe it should be readded in non-debug builds.
|
||||
// if (bc->ODIN_DEBUG == false) {
|
||||
// opt_flags = gb_string_appendc(opt_flags, "-mem2reg ");
|
||||
// }
|
||||
|
||||
bc->opt_flags = make_string_c(opt_flags);
|
||||
|
||||
|
||||
#undef LINK_FLAG_X64
|
||||
#undef LINK_FLAG_386
|
||||
}
|
||||
@@ -1,566 +0,0 @@
|
||||
bool check_is_terminating(AstNode *node);
|
||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
||||
|
||||
// NOTE(bill): `content_name` is for debugging and error messages
|
||||
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
error_node(operand->expr,
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
|
||||
gb_string_free(expr_str);
|
||||
}
|
||||
|
||||
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (e->type == NULL) {
|
||||
// NOTE(bill): Use the type of the operand
|
||||
Type *t = operand->type;
|
||||
if (is_type_untyped(t)) {
|
||||
if (t == t_invalid || is_type_untyped_nil(t)) {
|
||||
error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return NULL;
|
||||
}
|
||||
t = default_type(t);
|
||||
}
|
||||
if (is_type_bit_field_value(t)) {
|
||||
t = default_bit_field_value_type(t);
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
check_assignment(c, operand, e->type, context_name);
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return e->type;
|
||||
}
|
||||
|
||||
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
|
||||
if ((lhs == NULL || lhs_count == 0) && inits.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||
// an extra allocation
|
||||
ArrayOperand operands = {0};
|
||||
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
|
||||
check_unpack_arguments(c, lhs_count, &operands, inits, true);
|
||||
|
||||
isize rhs_count = operands.count;
|
||||
for_array(i, operands) {
|
||||
if (operands.e[i].mode == Addressing_Invalid) {
|
||||
rhs_count--;
|
||||
}
|
||||
}
|
||||
|
||||
isize max = gb_min(lhs_count, rhs_count);
|
||||
for (isize i = 0; i < max; i++) {
|
||||
check_init_variable(c, lhs[i], &operands.e[i], context_name);
|
||||
}
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
gbString str = expr_to_string(operand->expr);
|
||||
error_node(operand->expr, "`%s` is not a constant", str);
|
||||
gb_string_free(str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!is_type_constant_type(operand->type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error_node(operand->expr, "Invalid constant type: `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->type == NULL) { // NOTE(bill): type inference
|
||||
e->type = operand->type;
|
||||
}
|
||||
|
||||
check_assignment(c, operand, e->type, str_lit("constant declaration"));
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
e->Constant.value = operand->value;
|
||||
}
|
||||
|
||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
String name = e->token.string;
|
||||
Type *named = make_type_named(c->allocator, name, NULL, e);
|
||||
named->Named.type_name = e;
|
||||
if (def != NULL && def->kind == Type_Named) {
|
||||
def->Named.base = named;
|
||||
}
|
||||
e->type = named;
|
||||
|
||||
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
|
||||
|
||||
Type *bt = check_type_extra(c, type_expr, named);
|
||||
named->Named.base = base_type(bt);
|
||||
if (named->Named.base == t_invalid) {
|
||||
// gb_printf("check_type_decl: %s\n", type_to_string(named));
|
||||
}
|
||||
}
|
||||
|
||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Constant);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr) {
|
||||
Type *t = check_type(c, type_expr);
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
error_node(type_expr, "Invalid constant type `%s`", str);
|
||||
gb_string_free(str);
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
Operand operand = {0};
|
||||
if (init != NULL) {
|
||||
check_expr_or_type(c, &operand, init);
|
||||
}
|
||||
if (operand.mode == Addressing_Type) {
|
||||
e->kind = Entity_TypeName;
|
||||
|
||||
DeclInfo *d = c->context.decl;
|
||||
d->type_expr = d->init_expr;
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
return;
|
||||
}
|
||||
|
||||
check_init_constant(c, e, &operand);
|
||||
|
||||
if (operand.mode == Addressing_Invalid ||
|
||||
base_type(operand.type) == t_invalid) {
|
||||
error(e->token, "Invalid declaration type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool are_signatures_similar_enough(Type *a_, Type *b_) {
|
||||
GB_ASSERT(a_->kind == Type_Proc);
|
||||
GB_ASSERT(b_->kind == Type_Proc);
|
||||
TypeProc *a = &a_->Proc;
|
||||
TypeProc *b = &b_->Proc;
|
||||
|
||||
if (a->param_count != b->param_count) {
|
||||
return false;
|
||||
}
|
||||
if (a->result_count != b->result_count) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < a->param_count; i++) {
|
||||
Type *x = core_type(a->params->Tuple.variables[i]->type);
|
||||
Type *y = core_type(b->params->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_type_integer(x) && is_type_integer(y)) {
|
||||
GB_ASSERT(x->kind == Type_Basic);
|
||||
GB_ASSERT(y->kind == Type_Basic);
|
||||
if (x->Basic.size == y->Basic.size) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (isize i = 0; i < a->result_count; i++) {
|
||||
Type *x = base_type(a->results->Tuple.variables[i]->type);
|
||||
Type *y = base_type(b->results->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_type_integer(x) && is_type_integer(y)) {
|
||||
GB_ASSERT(x->kind == Type_Basic);
|
||||
GB_ASSERT(y->kind == Type_Basic);
|
||||
if (x->Basic.size == y->Basic.size) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
if (d->proc_lit->kind != AstNode_ProcLit) {
|
||||
// TOOD(bill): Better error message
|
||||
error_node(d->proc_lit, "Expected a procedure to check");
|
||||
return;
|
||||
}
|
||||
|
||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
|
||||
e->type = proc_type;
|
||||
ast_node(pd, ProcLit, d->proc_lit);
|
||||
|
||||
check_open_scope(c, pd->type);
|
||||
check_procedure_type(c, proc_type, pd->type);
|
||||
|
||||
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pd->tags & ProcTag_export) != 0;
|
||||
bool is_inline = (pd->tags & ProcTag_inline) != 0;
|
||||
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
|
||||
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
|
||||
|
||||
|
||||
if (d->scope->is_file && str_eq(e->token.string, str_lit("main"))) {
|
||||
if (proc_type != NULL) {
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
if (pt->param_count != 0 ||
|
||||
pt->result_count != 0) {
|
||||
gbString str = type_to_string(proc_type);
|
||||
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (is_foreign && is_link_name) {
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
|
||||
} else if (is_foreign && is_export) {
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
|
||||
}
|
||||
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
|
||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
|
||||
}
|
||||
|
||||
|
||||
if (proc_type != NULL && is_type_proc(proc_type)) {
|
||||
TypeProc *tp = &proc_type->Proc;
|
||||
if (tp->result_count == 0 && is_require_results) {
|
||||
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
|
||||
} else {
|
||||
tp->require_results = is_require_results;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_foreign) {
|
||||
MapEntity *fp = &c->info.foreigns;
|
||||
String name = e->token.string;
|
||||
if (pd->foreign_name.len > 0) {
|
||||
name = pd->foreign_name;
|
||||
}
|
||||
|
||||
AstNode *foreign_library = d->proc_lit->ProcLit.foreign_library;
|
||||
if (foreign_library == NULL) {
|
||||
error(e->token, "#foreign procedures must declare which library they are from");
|
||||
} else if (foreign_library->kind != AstNode_Ident) {
|
||||
error_node(foreign_library, "#foreign library names must be an identifier");
|
||||
} else {
|
||||
String name = foreign_library->Ident.string;
|
||||
Entity *found = scope_lookup_entity(c->context.scope, name);
|
||||
if (found == NULL) {
|
||||
if (str_eq(name, str_lit("_"))) {
|
||||
error_node(foreign_library, "`_` cannot be used as a value type");
|
||||
} else {
|
||||
error_node(foreign_library, "Undeclared name: %.*s", LIT(name));
|
||||
}
|
||||
} else if (found->kind != Entity_LibraryName) {
|
||||
error_node(foreign_library, "`_` cannot be used as a library name");
|
||||
} else {
|
||||
// TODO(bill): Extra stuff to do with library names?
|
||||
e->Procedure.foreign_library = found;
|
||||
add_entity_use(c, foreign_library, found);
|
||||
}
|
||||
}
|
||||
|
||||
e->Procedure.is_foreign = true;
|
||||
e->Procedure.foreign_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error_node(d->proc_lit,
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
} else {
|
||||
String name = e->token.string;
|
||||
if (is_link_name) {
|
||||
name = pd->link_name;
|
||||
}
|
||||
|
||||
if (is_link_name || is_export) {
|
||||
MapEntity *fp = &c->info.foreigns;
|
||||
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
// TODO(bill): Better error message?
|
||||
error_node(d->proc_lit,
|
||||
"Non unique linking name for procedure `%.*s`\n"
|
||||
"\tother at %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_close_scope(c);
|
||||
}
|
||||
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr != NULL) {
|
||||
e->type = check_type_extra(c, type_expr, NULL);
|
||||
}
|
||||
|
||||
if (init_expr == NULL) {
|
||||
if (type_expr == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entities == NULL || entity_count == 1) {
|
||||
GB_ASSERT(entities == NULL || entities[0] == e);
|
||||
Operand operand = {0};
|
||||
check_expr(c, &operand, init_expr);
|
||||
check_init_variable(c, e, &operand, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
if (type_expr != NULL) {
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
entities[i]->type = e->type;
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeArray inits;
|
||||
array_init_reserve(&inits, c->allocator, 1);
|
||||
array_add(&inits, init_expr);
|
||||
check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
if (e->type != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d == NULL) {
|
||||
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
|
||||
if (found) {
|
||||
d = *found;
|
||||
} else {
|
||||
// TODO(bill): Err here?
|
||||
e->type = t_invalid;
|
||||
set_base_type(named_type, t_invalid);
|
||||
return;
|
||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
CheckerContext prev = c->context;
|
||||
c->context.scope = d->scope;
|
||||
c->context.decl = d;
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_lit(c, e, d);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
||||
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
||||
|
||||
String proc_name = {0};
|
||||
if (token.kind == Token_Ident) {
|
||||
proc_name = token.string;
|
||||
} else {
|
||||
// TODO(bill): Better name
|
||||
proc_name = str_lit("(anonymous-procedure)");
|
||||
}
|
||||
|
||||
CheckerContext old_context = c->context;
|
||||
c->context.scope = decl->scope;
|
||||
c->context.decl = decl;
|
||||
c->context.proc_name = proc_name;
|
||||
c->context.curr_proc_decl = decl;
|
||||
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
if (type->Proc.param_count > 0) {
|
||||
TypeTuple *params = &type->Proc.params->Tuple;
|
||||
for (isize i = 0; i < params->variable_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
if (!(e->flags & EntityFlag_Using)) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
uvar->Variable.is_immutable = is_immutable;
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(e->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push_procedure(c, type);
|
||||
{
|
||||
ast_node(bs, BlockStmt, body);
|
||||
check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
if (token.kind == Token_Ident) {
|
||||
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
|
||||
} else {
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
|
||||
|
||||
check_scope_usage(c, c->context.scope);
|
||||
c->context = old_context;
|
||||
|
||||
if (decl->parent != NULL) {
|
||||
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||
for_array(i, decl->deps.entries) {
|
||||
HashKey key = decl->deps.entries.e[i].key;
|
||||
Entity *e = cast(Entity *)key.ptr;
|
||||
map_bool_set(&decl->parent->deps, key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1070
File diff suppressed because it is too large
Load Diff
-6430
File diff suppressed because it is too large
Load Diff
+6846
File diff suppressed because it is too large
Load Diff
-1662
File diff suppressed because it is too large
Load Diff
+1809
File diff suppressed because it is too large
Load Diff
+2544
File diff suppressed because it is too large
Load Diff
-2174
File diff suppressed because it is too large
Load Diff
+3425
File diff suppressed because it is too large
Load Diff
+551
@@ -0,0 +1,551 @@
|
||||
// checker.hpp
|
||||
|
||||
struct Type;
|
||||
struct Entity;
|
||||
struct Scope;
|
||||
struct DeclInfo;
|
||||
struct AstFile;
|
||||
struct Checker;
|
||||
struct CheckerInfo;
|
||||
struct CheckerContext;
|
||||
|
||||
enum AddressingMode;
|
||||
struct TypeAndValue;
|
||||
|
||||
// ExprInfo stores information used for "untyped" expressions
|
||||
struct ExprInfo {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
bool is_lhs; // Debug info
|
||||
};
|
||||
|
||||
gb_inline ExprInfo make_expr_info(AddressingMode mode, Type *type, ExactValue value, bool is_lhs) {
|
||||
ExprInfo ei = {};
|
||||
ei.mode = mode;
|
||||
ei.type = type;
|
||||
ei.value = value;
|
||||
ei.is_lhs = is_lhs;
|
||||
return ei;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
enum ExprKind {
|
||||
Expr_Expr,
|
||||
Expr_Stmt,
|
||||
};
|
||||
|
||||
// Statements and Declarations
|
||||
enum StmtFlag {
|
||||
Stmt_BreakAllowed = 1<<0,
|
||||
Stmt_ContinueAllowed = 1<<1,
|
||||
Stmt_FallthroughAllowed = 1<<2,
|
||||
|
||||
Stmt_CheckScopeDecls = 1<<5,
|
||||
};
|
||||
|
||||
enum BuiltinProcPkg {
|
||||
BuiltinProcPkg_builtin,
|
||||
BuiltinProcPkg_intrinsics,
|
||||
};
|
||||
|
||||
struct BuiltinProc {
|
||||
String name;
|
||||
isize arg_count;
|
||||
bool variadic;
|
||||
ExprKind kind;
|
||||
BuiltinProcPkg pkg;
|
||||
};
|
||||
|
||||
enum BuiltinProcId {
|
||||
BuiltinProc_Invalid,
|
||||
|
||||
BuiltinProc_len,
|
||||
BuiltinProc_cap,
|
||||
|
||||
BuiltinProc_size_of,
|
||||
BuiltinProc_align_of,
|
||||
BuiltinProc_offset_of,
|
||||
BuiltinProc_type_of,
|
||||
BuiltinProc_type_info_of,
|
||||
BuiltinProc_typeid_of,
|
||||
|
||||
BuiltinProc_swizzle,
|
||||
|
||||
BuiltinProc_complex,
|
||||
BuiltinProc_real,
|
||||
BuiltinProc_imag,
|
||||
BuiltinProc_conj,
|
||||
|
||||
BuiltinProc_expand_to_tuple,
|
||||
|
||||
BuiltinProc_min,
|
||||
BuiltinProc_max,
|
||||
BuiltinProc_abs,
|
||||
BuiltinProc_clamp,
|
||||
|
||||
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
||||
|
||||
// "Intrinsics"
|
||||
BuiltinProc_atomic_fence,
|
||||
BuiltinProc_atomic_fence_acq,
|
||||
BuiltinProc_atomic_fence_rel,
|
||||
BuiltinProc_atomic_fence_acqrel,
|
||||
|
||||
BuiltinProc_atomic_store,
|
||||
BuiltinProc_atomic_store_rel,
|
||||
BuiltinProc_atomic_store_relaxed,
|
||||
BuiltinProc_atomic_store_unordered,
|
||||
|
||||
BuiltinProc_atomic_load,
|
||||
BuiltinProc_atomic_load_acq,
|
||||
BuiltinProc_atomic_load_relaxed,
|
||||
BuiltinProc_atomic_load_unordered,
|
||||
|
||||
BuiltinProc_atomic_add,
|
||||
BuiltinProc_atomic_add_acq,
|
||||
BuiltinProc_atomic_add_rel,
|
||||
BuiltinProc_atomic_add_acqrel,
|
||||
BuiltinProc_atomic_add_relaxed,
|
||||
BuiltinProc_atomic_sub,
|
||||
BuiltinProc_atomic_sub_acq,
|
||||
BuiltinProc_atomic_sub_rel,
|
||||
BuiltinProc_atomic_sub_acqrel,
|
||||
BuiltinProc_atomic_sub_relaxed,
|
||||
BuiltinProc_atomic_and,
|
||||
BuiltinProc_atomic_and_acq,
|
||||
BuiltinProc_atomic_and_rel,
|
||||
BuiltinProc_atomic_and_acqrel,
|
||||
BuiltinProc_atomic_and_relaxed,
|
||||
BuiltinProc_atomic_nand,
|
||||
BuiltinProc_atomic_nand_acq,
|
||||
BuiltinProc_atomic_nand_rel,
|
||||
BuiltinProc_atomic_nand_acqrel,
|
||||
BuiltinProc_atomic_nand_relaxed,
|
||||
BuiltinProc_atomic_or,
|
||||
BuiltinProc_atomic_or_acq,
|
||||
BuiltinProc_atomic_or_rel,
|
||||
BuiltinProc_atomic_or_acqrel,
|
||||
BuiltinProc_atomic_or_relaxed,
|
||||
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_COUNT,
|
||||
};
|
||||
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT("min"), 2, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("max"), 2, true, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
{STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
|
||||
|
||||
|
||||
// "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},
|
||||
};
|
||||
|
||||
|
||||
// Operand is used as an intermediate value whilst checking
|
||||
// Operands store an addressing mode, the expression being evaluated,
|
||||
// its type and node, and other specific information for certain
|
||||
// addressing modes
|
||||
// Its zero-value is a valid "invalid operand"
|
||||
struct Operand {
|
||||
AddressingMode mode;
|
||||
Type * type;
|
||||
ExactValue value;
|
||||
Ast * expr;
|
||||
BuiltinProcId builtin_id;
|
||||
Entity * proc_group;
|
||||
};
|
||||
|
||||
|
||||
struct BlockLabel {
|
||||
String name;
|
||||
Ast *label; // Ast_Label;
|
||||
};
|
||||
|
||||
struct AttributeContext {
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
isize init_expr_list_count;
|
||||
String thread_local_model;
|
||||
String deprecated_message;
|
||||
};
|
||||
|
||||
AttributeContext make_attribute_context(String link_prefix) {
|
||||
AttributeContext ac = {};
|
||||
ac.link_prefix = link_prefix;
|
||||
return ac;
|
||||
}
|
||||
|
||||
#define DECL_ATTRIBUTE_PROC(_name) bool _name(CheckerContext *c, Ast *elem, String name, ExactValue value, AttributeContext *ac)
|
||||
typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
|
||||
|
||||
void check_decl_attributes(CheckerContext *c, Array<Ast *> const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
|
||||
|
||||
|
||||
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||
struct DeclInfo {
|
||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||
Scope * scope;
|
||||
|
||||
Entity *entity;
|
||||
|
||||
Ast * type_expr;
|
||||
Ast * init_expr;
|
||||
Array<Ast *> attributes;
|
||||
Ast * proc_lit; // Ast_ProcLit
|
||||
Type * gen_proc_type; // Precalculated
|
||||
bool is_using;
|
||||
|
||||
PtrSet<Entity *> deps;
|
||||
PtrSet<Type *> type_info_deps;
|
||||
Array<BlockLabel> labels;
|
||||
};
|
||||
|
||||
// ProcInfo stores the information needed for checking a procedure
|
||||
struct ProcInfo {
|
||||
AstFile * file;
|
||||
Token token;
|
||||
DeclInfo *decl;
|
||||
Type * type; // Type_Procedure
|
||||
Ast * body; // Ast_BlockStmt
|
||||
u64 tags;
|
||||
bool generated_from_polymorphic;
|
||||
Ast * poly_def_node;
|
||||
};
|
||||
|
||||
|
||||
|
||||
enum ScopeFlag {
|
||||
ScopeFlag_Pkg = 1<<1,
|
||||
ScopeFlag_Global = 1<<2,
|
||||
ScopeFlag_File = 1<<3,
|
||||
ScopeFlag_Init = 1<<4,
|
||||
ScopeFlag_Proc = 1<<5,
|
||||
ScopeFlag_Type = 1<<6,
|
||||
|
||||
|
||||
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
Ast * node;
|
||||
Scope * parent;
|
||||
Scope * prev;
|
||||
Scope * next;
|
||||
Scope * first_child;
|
||||
Scope * last_child;
|
||||
Map<Entity *> elements; // Key: String
|
||||
|
||||
Array<Ast *> delayed_directives;
|
||||
Array<Ast *> delayed_imports;
|
||||
PtrSet<Scope *> imported;
|
||||
|
||||
i32 flags; // ScopeFlag
|
||||
union {
|
||||
AstPackage *pkg;
|
||||
AstFile * file;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct EntityGraphNode;
|
||||
typedef PtrSet<EntityGraphNode *> EntityGraphNodeSet;
|
||||
|
||||
struct EntityGraphNode {
|
||||
Entity * entity; // Procedure, Variable, Constant
|
||||
EntityGraphNodeSet pred;
|
||||
EntityGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct ImportGraphNode;
|
||||
typedef PtrSet<ImportGraphNode *> ImportGraphNodeSet;
|
||||
|
||||
|
||||
struct ImportGraphNode {
|
||||
AstPackage * pkg;
|
||||
Scope * scope;
|
||||
ImportGraphNodeSet pred;
|
||||
ImportGraphNodeSet succ;
|
||||
isize index; // Index in array/queue
|
||||
isize dep_count;
|
||||
};
|
||||
|
||||
|
||||
struct ForeignContext {
|
||||
Ast * curr_library;
|
||||
ProcCallingConvention default_cc;
|
||||
String link_prefix;
|
||||
bool in_export;
|
||||
};
|
||||
|
||||
typedef Array<Entity *> CheckerTypePath;
|
||||
typedef Array<Type *> CheckerPolyPath;
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo
|
||||
// NOTE(bill): This needs to be a map and not on the Ast
|
||||
// as it needs to be iterated across
|
||||
Map<AstFile *> files; // Key: String (full path)
|
||||
Map<AstPackage *> packages; // Key: String (full path)
|
||||
Map<Entity *> foreigns; // Key: String
|
||||
Array<Entity *> definitions;
|
||||
Array<Entity *> entities;
|
||||
Array<DeclInfo *> variable_init_order;
|
||||
|
||||
Map<Array<Entity *> > gen_procs; // Key: Ast * | Identifier -> Entity
|
||||
Map<Array<Entity *> > gen_types; // Key: Type *
|
||||
|
||||
Array<Type *> type_info_types;
|
||||
Map<isize> type_info_map; // Key: Type *
|
||||
|
||||
|
||||
AstPackage * builtin_package;
|
||||
AstPackage * runtime_package;
|
||||
Scope * init_scope;
|
||||
Entity * entry_point;
|
||||
PtrSet<Entity *> minimum_dependency_set;
|
||||
PtrSet<isize> minimum_dependency_type_info_set;
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
Checker * checker;
|
||||
CheckerInfo * info;
|
||||
AstPackage * pkg;
|
||||
AstFile * file;
|
||||
Scope * scope;
|
||||
DeclInfo * decl;
|
||||
|
||||
u32 stmt_state_flags;
|
||||
bool in_defer; // TODO(bill): Actually handle correctly
|
||||
Type * type_hint;
|
||||
|
||||
String proc_name;
|
||||
DeclInfo * curr_proc_decl;
|
||||
Type * curr_proc_sig;
|
||||
bool in_proc_sig;
|
||||
ForeignContext foreign_context;
|
||||
gbAllocator allocator;
|
||||
|
||||
CheckerTypePath *type_path;
|
||||
isize type_level; // TODO(bill): Actually handle correctly
|
||||
CheckerPolyPath *poly_path;
|
||||
isize poly_level; // TODO(bill): Actually handle correctly
|
||||
|
||||
bool in_enum_type;
|
||||
bool collect_delayed_decls;
|
||||
bool allow_polymorphic_types;
|
||||
bool no_polymorphic_errors;
|
||||
bool in_polymorphic_specialization;
|
||||
Scope * polymorphic_scope;
|
||||
};
|
||||
|
||||
struct Checker {
|
||||
Parser * parser;
|
||||
CheckerInfo info;
|
||||
|
||||
Array<ProcInfo> procs_to_check;
|
||||
|
||||
gbAllocator allocator;
|
||||
CheckerContext init_ctx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
gb_global AstPackage *builtin_pkg = nullptr;
|
||||
gb_global AstPackage *intrinsics_pkg = nullptr;
|
||||
|
||||
|
||||
HashKey hash_node (Ast *node) { return hash_pointer(node); }
|
||||
HashKey hash_ast_file (AstFile *file) { return hash_pointer(file); }
|
||||
HashKey hash_entity (Entity *e) { return hash_pointer(e); }
|
||||
HashKey hash_type (Type *t) { return hash_pointer(t); }
|
||||
HashKey hash_decl_info(DeclInfo *decl) { return hash_pointer(decl); }
|
||||
|
||||
|
||||
// CheckerInfo API
|
||||
TypeAndValue type_and_value_of_expr (Ast *expr);
|
||||
Type * type_of_expr (Ast *expr);
|
||||
Entity * entity_of_ident (Ast *identifier);
|
||||
Entity * implicit_entity_of_node(Ast *clause);
|
||||
Scope * scope_of_node (Ast *node);
|
||||
DeclInfo * decl_info_of_ident (Ast *ident);
|
||||
DeclInfo * decl_info_of_entity (Entity * e);
|
||||
AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
|
||||
// IMPORTANT: Only to use once checking is done
|
||||
isize type_info_index (CheckerInfo *i, Type * type, bool error_on_failure = true);
|
||||
|
||||
// Will return nullptr if not found
|
||||
Entity *entity_of_node(Ast *expr);
|
||||
|
||||
|
||||
Entity *scope_lookup_current(Scope *s, String name);
|
||||
Entity *scope_lookup (Scope *s, String name);
|
||||
void scope_lookup_parent (Scope *s, String name, Scope **scope_, Entity **entity_);
|
||||
Entity *scope_insert (Scope *s, Entity *entity);
|
||||
|
||||
|
||||
ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr);
|
||||
void check_set_expr_info (CheckerInfo *i, Ast *expr, ExprInfo info);
|
||||
void check_remove_expr_info (CheckerInfo *i, Ast *expr);
|
||||
void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
|
||||
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
|
||||
void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
||||
void add_implicit_entity (CheckerContext *c, Ast *node, Entity *e);
|
||||
void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d);
|
||||
void add_type_info_type (CheckerContext *c, Type *t);
|
||||
|
||||
void check_add_import_decl(CheckerContext *c, Ast *decl);
|
||||
void check_add_foreign_import_decl(CheckerContext *c, Ast *decl);
|
||||
|
||||
|
||||
bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_global = false);
|
||||
void check_collect_entities(CheckerContext *c, Array<Ast *> const &nodes);
|
||||
void check_collect_entities_from_when_stmt(CheckerContext *c, AstWhenStmt *ws);
|
||||
void check_delayed_file_import_entity(CheckerContext *c, Ast *decl);
|
||||
|
||||
CheckerTypePath *new_checker_type_path();
|
||||
void destroy_checker_type_path(CheckerTypePath *tp);
|
||||
|
||||
void check_type_path_push(CheckerContext *c, Entity *e);
|
||||
Entity *check_type_path_pop (CheckerContext *c);
|
||||
|
||||
CheckerPolyPath *new_checker_poly_path();
|
||||
void destroy_checker_poly_path(CheckerPolyPath *);
|
||||
|
||||
void check_poly_path_push(CheckerContext *c, Type *t);
|
||||
Type *check_poly_path_pop (CheckerContext *c);
|
||||
-262
@@ -1,262 +0,0 @@
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
// Required for intrinsics on GCC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define GB_NO_DEFER
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
gbAllocator heap_allocator(void) {
|
||||
return gb_heap_allocator();
|
||||
}
|
||||
|
||||
#include "unicode.c"
|
||||
#include "string.c"
|
||||
#include "array.c"
|
||||
#include "integer128.c"
|
||||
#include "murmurhash3.c"
|
||||
|
||||
u128 fnv128a(void const *data, isize len) {
|
||||
u128 o = u128_lo_hi(0x13bull, 0x1000000ull);
|
||||
u128 h = u128_lo_hi(0x62b821756295c58dull, 0x6c62272e07bb0142ull);
|
||||
u8 const *bytes = cast(u8 const *)data;
|
||||
for (isize i = 0; i < len; i++) {
|
||||
h = u128_mul(u128_xor(h, u128_from_u64(bytes[i])), o);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_global String global_module_path = {0};
|
||||
gb_global bool global_module_path_set = false;
|
||||
|
||||
gb_global gbScratchMemory scratch_memory = {0};
|
||||
|
||||
void init_scratch_memory(isize size) {
|
||||
void *memory = gb_alloc(heap_allocator(), size);
|
||||
gb_scratch_memory_init(&scratch_memory, memory, size);
|
||||
}
|
||||
|
||||
gbAllocator scratch_allocator(void) {
|
||||
return gb_scratch_allocator(&scratch_memory);
|
||||
}
|
||||
|
||||
typedef struct DynamicArenaBlock DynamicArenaBlock;
|
||||
typedef struct DynamicArena DynamicArena;
|
||||
|
||||
struct DynamicArenaBlock {
|
||||
DynamicArenaBlock *prev;
|
||||
DynamicArenaBlock *next;
|
||||
u8 * start;
|
||||
isize count;
|
||||
isize capacity;
|
||||
|
||||
gbVirtualMemory vm;
|
||||
};
|
||||
|
||||
struct DynamicArena {
|
||||
DynamicArenaBlock *start_block;
|
||||
DynamicArenaBlock *current_block;
|
||||
isize block_size;
|
||||
};
|
||||
|
||||
DynamicArenaBlock *add_dynamic_arena_block(DynamicArena *a) {
|
||||
GB_ASSERT(a != NULL);
|
||||
GB_ASSERT(a->block_size > 0);
|
||||
|
||||
gbVirtualMemory vm = gb_vm_alloc(NULL, a->block_size);
|
||||
DynamicArenaBlock *block = cast(DynamicArenaBlock *)vm.data;
|
||||
|
||||
u8 *start = cast(u8 *)gb_align_forward(cast(u8 *)(block + 1), GB_DEFAULT_MEMORY_ALIGNMENT);
|
||||
u8 *end = cast(u8 *)vm.data + vm.size;
|
||||
|
||||
block->vm = vm;
|
||||
block->start = start;
|
||||
block->count = 0;
|
||||
block->capacity = end-start;
|
||||
|
||||
if (a->current_block != NULL) {
|
||||
a->current_block->next = block;
|
||||
block->prev = a->current_block;
|
||||
}
|
||||
a->current_block = block;
|
||||
return block;
|
||||
}
|
||||
|
||||
void init_dynamic_arena(DynamicArena *a, isize block_size) {
|
||||
isize size = gb_size_of(DynamicArenaBlock) + block_size;
|
||||
size = cast(isize)gb_align_forward(cast(void *)cast(uintptr)size, GB_DEFAULT_MEMORY_ALIGNMENT);
|
||||
a->block_size = size;
|
||||
a->start_block = add_dynamic_arena_block(a);
|
||||
}
|
||||
|
||||
void destroy_dynamic_arena(DynamicArena *a) {
|
||||
DynamicArenaBlock *b = a->current_block;
|
||||
while (b != NULL) {
|
||||
gbVirtualMemory vm = b->vm;
|
||||
b = b->prev;
|
||||
gb_vm_free(b->vm);
|
||||
}
|
||||
}
|
||||
|
||||
GB_ALLOCATOR_PROC(dynamic_arena_allocator_proc) {
|
||||
DynamicArena *a = cast(DynamicArena *)allocator_data;
|
||||
void *ptr = NULL;
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc: {
|
||||
|
||||
} break;
|
||||
|
||||
case gbAllocation_Free: {
|
||||
} break;
|
||||
|
||||
case gbAllocation_Resize: {
|
||||
} break;
|
||||
|
||||
case gbAllocation_FreeAll:
|
||||
GB_PANIC("free_all is not supported by this allocator");
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
gbAllocator dynamic_arena_allocator(DynamicArena *a) {
|
||||
gbAllocator allocator = {dynamic_arena_allocator_proc, a};
|
||||
return allocator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
i64 prev_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
i16 f32_to_f16(f32 value) {
|
||||
union { u32 i; f32 f; } v;
|
||||
i32 i, s, e, m;
|
||||
|
||||
v.f = value;
|
||||
i = (i32)v.i;
|
||||
|
||||
s = (i >> 16) & 0x00008000;
|
||||
e = ((i >> 23) & 0x000000ff) - (127 - 15);
|
||||
m = i & 0x007fffff;
|
||||
|
||||
|
||||
if (e <= 0) {
|
||||
if (e < -10) return cast(i16)s;
|
||||
m = (m | 0x00800000) >> (1 - e);
|
||||
|
||||
if (m & 0x00001000)
|
||||
m += 0x00002000;
|
||||
|
||||
return cast(i16)(s | (m >> 13));
|
||||
} else if (e == 0xff - (127 - 15)) {
|
||||
if (m == 0) {
|
||||
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
|
||||
} else {
|
||||
/* NOTE(bill): NAN */
|
||||
m >>= 13;
|
||||
return cast(i16)(s | 0x7c00 | m | (m == 0));
|
||||
}
|
||||
} else {
|
||||
if (m & 0x00001000) {
|
||||
m += 0x00002000;
|
||||
if (m & 0x00800000) {
|
||||
m = 0;
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (e > 30) {
|
||||
float volatile f = 1e12f;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
f *= f; /* NOTE(bill): Cause overflow */
|
||||
}
|
||||
|
||||
return cast(i16)(s | 0x7c00);
|
||||
}
|
||||
|
||||
return cast(i16)(s | (e << 10) | (m >> 13));
|
||||
}
|
||||
}
|
||||
|
||||
f64 gb_sqrt(f64 x) {
|
||||
return sqrt(x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||
|
||||
|
||||
// Doubly Linked Lists
|
||||
|
||||
#define DLIST_SET(curr_element, next_element) do { \
|
||||
(curr_element)->next = (next_element); \
|
||||
(curr_element)->next->prev = (curr_element); \
|
||||
(curr_element) = (curr_element)->next; \
|
||||
} while (0)
|
||||
|
||||
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
|
||||
if ((root_element) == NULL) { \
|
||||
(root_element) = (curr_element) = (next_element); \
|
||||
} else { \
|
||||
DLIST_SET(curr_element, next_element); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generic Data Structures
|
||||
//
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef Array(i32) Array_i32;
|
||||
typedef Array(isize) Array_isize;
|
||||
|
||||
|
||||
#define MAP_TYPE String
|
||||
#define MAP_PROC map_string_
|
||||
#define MAP_NAME MapString
|
||||
#include "map.c"
|
||||
|
||||
#define MAP_TYPE bool
|
||||
#define MAP_PROC map_bool_
|
||||
#define MAP_NAME MapBool
|
||||
#include "map.c"
|
||||
|
||||
#define MAP_TYPE isize
|
||||
#define MAP_PROC map_isize_
|
||||
#define MAP_NAME MapIsize
|
||||
#include "map.c"
|
||||
+936
@@ -0,0 +1,936 @@
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
// Required for intrinsics on GCC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(GB_COMPILER_MSVC)
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
template <typename U, typename V>
|
||||
gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); }
|
||||
|
||||
template <typename U, typename V>
|
||||
gb_inline U const &bit_cast(V const &v) { return reinterpret_cast<U const &>(v); }
|
||||
|
||||
|
||||
gb_inline i64 align_formula(i64 size, i64 align) {
|
||||
if (align > 0) {
|
||||
i64 result = size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
gb_inline isize align_formula_isize(isize size, isize align) {
|
||||
if (align > 0) {
|
||||
isize result = size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
GB_ALLOCATOR_PROC(heap_allocator_proc);
|
||||
|
||||
gbAllocator heap_allocator(void) {
|
||||
gbAllocator a;
|
||||
a.proc = heap_allocator_proc;
|
||||
a.data = nullptr;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
GB_ALLOCATOR_PROC(heap_allocator_proc) {
|
||||
void *ptr = nullptr;
|
||||
gb_unused(allocator_data);
|
||||
gb_unused(old_size);
|
||||
|
||||
|
||||
|
||||
// TODO(bill): Throughly test!
|
||||
switch (type) {
|
||||
#if defined(GB_COMPILER_MSVC)
|
||||
#if 0
|
||||
case gbAllocation_Alloc:
|
||||
ptr = _aligned_malloc(size, alignment);
|
||||
if (flags & gbAllocatorFlag_ClearToZero) {
|
||||
gb_zero_size(ptr, size);
|
||||
}
|
||||
break;
|
||||
case gbAllocation_Free:
|
||||
_aligned_free(old_memory);
|
||||
break;
|
||||
case gbAllocation_Resize:
|
||||
ptr = _aligned_realloc(old_memory, size, alignment);
|
||||
break;
|
||||
#else
|
||||
case gbAllocation_Alloc:
|
||||
// TODO(bill): Make sure this is aligned correctly
|
||||
ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, align_formula_isize(size, alignment));
|
||||
break;
|
||||
case gbAllocation_Free:
|
||||
HeapFree(GetProcessHeap(), 0, old_memory);
|
||||
break;
|
||||
case gbAllocation_Resize:
|
||||
ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, align_formula_isize(size, alignment));
|
||||
break;
|
||||
#endif
|
||||
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
// TODO(bill): *nix version that's decent
|
||||
case gbAllocation_Alloc: {
|
||||
ptr = aligned_alloc(alignment, size);
|
||||
// ptr = malloc(size+alignment);
|
||||
|
||||
if (flags & gbAllocatorFlag_ClearToZero) {
|
||||
gb_zero_size(ptr, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gbAllocation_Free: {
|
||||
free(old_memory);
|
||||
break;
|
||||
}
|
||||
|
||||
case gbAllocation_Resize: {
|
||||
// ptr = realloc(old_memory, size);
|
||||
ptr = gb_default_resize_align(heap_allocator(), old_memory, old_size, size, alignment);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
// TODO(bill): *nix version that's decent
|
||||
case gbAllocation_Alloc: {
|
||||
posix_memalign(&ptr, alignment, size);
|
||||
|
||||
if (flags & gbAllocatorFlag_ClearToZero) {
|
||||
gb_zero_size(ptr, size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case gbAllocation_Free: {
|
||||
free(old_memory);
|
||||
break;
|
||||
}
|
||||
|
||||
case gbAllocation_Resize: {
|
||||
ptr = gb_default_resize_align(heap_allocator(), old_memory, old_size, size, alignment);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case gbAllocation_FreeAll:
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#include "unicode.cpp"
|
||||
#include "string.cpp"
|
||||
#include "array.cpp"
|
||||
#include "murmurhash3.cpp"
|
||||
|
||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||
|
||||
|
||||
u64 fnv64a(void const *data, isize len) {
|
||||
u8 const *bytes = cast(u8 const *)data;
|
||||
u64 h = 0xcbf29ce484222325ull;
|
||||
for (isize i = 0; i < len; i++) {
|
||||
u64 b = cast(u64)bytes[i];
|
||||
h = (h ^ b) * 0x100000001b3ull;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
u64 u64_digit_value(Rune r) {
|
||||
if ('0' <= r && r <= '9') {
|
||||
return r - '0';
|
||||
} else if ('a' <= r && r <= 'f') {
|
||||
return r - 'a' + 10;
|
||||
} else if ('A' <= r && r <= 'F') {
|
||||
return r - 'A' + 10;
|
||||
}
|
||||
return 16; // NOTE(bill): Larger than highest possible
|
||||
}
|
||||
|
||||
|
||||
u64 u64_from_string(String string) {
|
||||
u64 base = 10;
|
||||
bool has_prefix = false;
|
||||
if (string.len > 2 && string[0] == '0') {
|
||||
switch (string[1]) {
|
||||
case 'b': base = 2; has_prefix = true; break;
|
||||
case 'o': base = 8; has_prefix = true; break;
|
||||
case 'd': base = 10; has_prefix = true; break;
|
||||
case 'z': base = 12; has_prefix = true; break;
|
||||
case 'x': base = 16; has_prefix = true; break;
|
||||
case 'h': base = 16; has_prefix = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *text = string.text;
|
||||
isize len = string.len;
|
||||
if (has_prefix) {
|
||||
text += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
u64 result = 0ull;
|
||||
for (isize i = 0; i < len; i++) {
|
||||
Rune r = cast(Rune)text[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u64 v = u64_digit_value(r);
|
||||
if (v >= base) {
|
||||
break;
|
||||
}
|
||||
result *= base;
|
||||
result += v;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
String u64_to_string(u64 v, char *out_buf, isize out_buf_len) {
|
||||
char buf[32] = {0};
|
||||
isize i = gb_size_of(buf);
|
||||
|
||||
u64 b = 10;
|
||||
while (v >= b) {
|
||||
buf[--i] = gb__num_to_char_table[v%b];
|
||||
v /= b;
|
||||
}
|
||||
buf[--i] = gb__num_to_char_table[v%b];
|
||||
|
||||
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||
gb_memmove(out_buf, &buf[i], len);
|
||||
return make_string(cast(u8 *)out_buf, len);
|
||||
}
|
||||
String i64_to_string(i64 a, char *out_buf, isize out_buf_len) {
|
||||
char buf[32] = {0};
|
||||
isize i = gb_size_of(buf);
|
||||
bool negative = false;
|
||||
if (a < 0) {
|
||||
negative = true;
|
||||
a = -a;
|
||||
}
|
||||
|
||||
u64 v = cast(u64)a;
|
||||
u64 b = 10;
|
||||
while (v >= b) {
|
||||
buf[--i] = gb__num_to_char_table[v%b];
|
||||
v /= b;
|
||||
}
|
||||
buf[--i] = gb__num_to_char_table[v%b];
|
||||
|
||||
if (negative) {
|
||||
buf[--i] = '-';
|
||||
}
|
||||
|
||||
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||
gb_memmove(out_buf, &buf[i], len);
|
||||
return make_string(cast(u8 *)out_buf, len);
|
||||
}
|
||||
|
||||
|
||||
gb_global i64 const signed_integer_mins[] = {
|
||||
0,
|
||||
-128ll,
|
||||
-32768ll,
|
||||
0,
|
||||
-2147483648ll,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
(-9223372036854775807ll - 1ll),
|
||||
};
|
||||
gb_global i64 const signed_integer_maxs[] = {
|
||||
0,
|
||||
127ll,
|
||||
32767ll,
|
||||
0,
|
||||
2147483647ll,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
9223372036854775807ll,
|
||||
};
|
||||
gb_global u64 const unsigned_integer_maxs[] = {
|
||||
0,
|
||||
255ull,
|
||||
65535ull,
|
||||
0,
|
||||
4294967295ull,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
18446744073709551615ull,
|
||||
};
|
||||
|
||||
|
||||
bool add_overflow_u64(u64 x, u64 y, u64 *result) {
|
||||
*result = x + y;
|
||||
return *result < x || *result < y;
|
||||
}
|
||||
|
||||
bool sub_overflow_u64(u64 x, u64 y, u64 *result) {
|
||||
*result = x - y;
|
||||
return *result > x;
|
||||
}
|
||||
|
||||
void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) {
|
||||
#if defined(GB_COMPILER_MSVC)
|
||||
*lo = _umul128(x, y, hi);
|
||||
#else
|
||||
// URL(bill): https://stackoverflow.com/questions/25095741/how-can-i-multiply-64-bit-operands-and-get-128-bit-result-portably#25096197
|
||||
u64 u1, v1, w1, t, w3, k;
|
||||
|
||||
u1 = (x & 0xffffffff);
|
||||
v1 = (y & 0xffffffff);
|
||||
t = (u1 * v1);
|
||||
w3 = (t & 0xffffffff);
|
||||
k = (t >> 32);
|
||||
|
||||
x >>= 32;
|
||||
t = (x * v1) + k;
|
||||
k = (t & 0xffffffff);
|
||||
w1 = (t >> 32);
|
||||
|
||||
y >>= 32;
|
||||
t = (u1 * y) + k;
|
||||
k = (t >> 32);
|
||||
|
||||
*hi = (x * y) + w1 + k;
|
||||
*lo = (t << 32) + w3;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "map.cpp"
|
||||
#include "ptr_set.cpp"
|
||||
#include "string_set.cpp"
|
||||
#include "priority_queue.cpp"
|
||||
|
||||
|
||||
|
||||
gb_global String global_module_path = {0};
|
||||
gb_global bool global_module_path_set = false;
|
||||
|
||||
// Arena from Per Vognsen
|
||||
#define ALIGN_DOWN(n, a) ((n) & ~((a) - 1))
|
||||
#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a) - 1, (a))
|
||||
#define ALIGN_DOWN_PTR(p, a) (cast(void *)ALIGN_DOWN(cast(uintptr)(p), (a)))
|
||||
#define ALIGN_UP_PTR(p, a) (cast(void *)ALIGN_UP(cast(uintptr)(p), (a)))
|
||||
|
||||
typedef struct Arena {
|
||||
u8 * ptr;
|
||||
u8 * end;
|
||||
u8 * prev;
|
||||
Array<u8 *> blocks;
|
||||
gbAllocator backing;
|
||||
isize block_size;
|
||||
gbMutex mutex;
|
||||
|
||||
isize total_used;
|
||||
} Arena;
|
||||
|
||||
#define ARENA_MIN_ALIGNMENT 16
|
||||
#define ARENA_DEFAULT_BLOCK_SIZE (8*1024*1024)
|
||||
|
||||
void arena_init(Arena *arena, gbAllocator backing, isize block_size=ARENA_DEFAULT_BLOCK_SIZE) {
|
||||
arena->backing = backing;
|
||||
arena->block_size = block_size;
|
||||
array_init(&arena->blocks, backing);
|
||||
gb_mutex_init(&arena->mutex);
|
||||
}
|
||||
|
||||
void arena_grow(Arena *arena, isize min_size) {
|
||||
gb_mutex_lock(&arena->mutex);
|
||||
defer (gb_mutex_unlock(&arena->mutex));
|
||||
|
||||
isize size = gb_max(arena->block_size, min_size);
|
||||
size = ALIGN_UP(size, ARENA_MIN_ALIGNMENT);
|
||||
void *new_ptr = gb_alloc(arena->backing, size);
|
||||
arena->ptr = cast(u8 *)new_ptr;
|
||||
gb_zero_size(arena->ptr, size);
|
||||
GB_ASSERT(arena->ptr == ALIGN_DOWN_PTR(arena->ptr, ARENA_MIN_ALIGNMENT));
|
||||
arena->end = arena->ptr + size;
|
||||
array_add(&arena->blocks, arena->ptr);
|
||||
}
|
||||
|
||||
void *arena_alloc(Arena *arena, isize size, isize alignment) {
|
||||
gb_mutex_lock(&arena->mutex);
|
||||
defer (gb_mutex_unlock(&arena->mutex));
|
||||
|
||||
arena->total_used += size;
|
||||
|
||||
if (size > (arena->end - arena->ptr)) {
|
||||
arena_grow(arena, size);
|
||||
GB_ASSERT(size <= (arena->end - arena->ptr));
|
||||
}
|
||||
|
||||
isize align = gb_max(alignment, ARENA_MIN_ALIGNMENT);
|
||||
void *ptr = arena->ptr;
|
||||
arena->prev = arena->ptr;
|
||||
arena->ptr = cast(u8 *)ALIGN_UP_PTR(arena->ptr + size, align);
|
||||
GB_ASSERT(arena->ptr <= arena->end);
|
||||
GB_ASSERT(ptr == ALIGN_DOWN_PTR(ptr, align));
|
||||
gb_zero_size(ptr, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void arena_free_all(Arena *arena) {
|
||||
gb_mutex_lock(&arena->mutex);
|
||||
defer (gb_mutex_unlock(&arena->mutex));
|
||||
|
||||
for_array(i, arena->blocks) {
|
||||
gb_free(arena->backing, arena->blocks[i]);
|
||||
}
|
||||
array_clear(&arena->blocks);
|
||||
arena->ptr = nullptr;
|
||||
arena->end = nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
GB_ALLOCATOR_PROC(arena_allocator_proc);
|
||||
|
||||
gbAllocator arena_allocator(Arena *arena) {
|
||||
gbAllocator a;
|
||||
a.proc = arena_allocator_proc;
|
||||
a.data = arena;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
GB_ALLOCATOR_PROC(arena_allocator_proc) {
|
||||
void *ptr = nullptr;
|
||||
Arena *arena = cast(Arena *)allocator_data;
|
||||
GB_ASSERT_NOT_NULL(arena);
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc:
|
||||
ptr = arena_alloc(arena, size, alignment);
|
||||
break;
|
||||
case gbAllocation_Free:
|
||||
GB_PANIC("gbAllocation_Free not supported");
|
||||
break;
|
||||
case gbAllocation_Resize:
|
||||
GB_PANIC("gbAllocation_Resize: not supported");
|
||||
break;
|
||||
case gbAllocation_FreeAll:
|
||||
arena_free_all(arena);
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
i32 next_pow2(i32 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
i32 bit_set_count(u32 x) {
|
||||
x -= ((x >> 1) & 0x55555555);
|
||||
x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
|
||||
x = (((x >> 4) + x) & 0x0f0f0f0f);
|
||||
x += (x >> 8);
|
||||
x += (x >> 16);
|
||||
|
||||
return cast(i32)(x & 0x0000003f);
|
||||
}
|
||||
|
||||
i64 bit_set_count(u64 x) {
|
||||
u32 a = *(cast(u32 *)&x);
|
||||
u32 b = *(cast(u32 *)&x + 1);
|
||||
return bit_set_count(a) + bit_set_count(b);
|
||||
}
|
||||
|
||||
u32 floor_log2(u32 x) {
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return cast(u32)(bit_set_count(x) - 1);
|
||||
}
|
||||
|
||||
u64 floor_log2(u64 x) {
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x |= x >> 32;
|
||||
return cast(u64)(bit_set_count(x) - 1);
|
||||
}
|
||||
|
||||
|
||||
u32 ceil_log2(u32 x) {
|
||||
i32 y = cast(i32)(x & (x-1));
|
||||
y |= -y;
|
||||
y >>= 32-1;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
return cast(u32)(bit_set_count(x) - 1 - y);
|
||||
}
|
||||
|
||||
u64 ceil_log2(u64 x) {
|
||||
i64 y = cast(i64)(x & (x-1));
|
||||
y |= -y;
|
||||
y >>= 64-1;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
x |= x >> 32;
|
||||
return cast(u64)(bit_set_count(x) - 1 - y);
|
||||
}
|
||||
|
||||
|
||||
i32 prev_pow2(i32 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
i64 prev_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
i16 f32_to_f16(f32 value) {
|
||||
union { u32 i; f32 f; } v;
|
||||
i32 i, s, e, m;
|
||||
|
||||
v.f = value;
|
||||
i = (i32)v.i;
|
||||
|
||||
s = (i >> 16) & 0x00008000;
|
||||
e = ((i >> 23) & 0x000000ff) - (127 - 15);
|
||||
m = i & 0x007fffff;
|
||||
|
||||
|
||||
if (e <= 0) {
|
||||
if (e < -10) return cast(i16)s;
|
||||
m = (m | 0x00800000) >> (1 - e);
|
||||
|
||||
if (m & 0x00001000)
|
||||
m += 0x00002000;
|
||||
|
||||
return cast(i16)(s | (m >> 13));
|
||||
} else if (e == 0xff - (127 - 15)) {
|
||||
if (m == 0) {
|
||||
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
|
||||
} else {
|
||||
/* NOTE(bill): NAN */
|
||||
m >>= 13;
|
||||
return cast(i16)(s | 0x7c00 | m | (m == 0));
|
||||
}
|
||||
} else {
|
||||
if (m & 0x00001000) {
|
||||
m += 0x00002000;
|
||||
if (m & 0x00800000) {
|
||||
m = 0;
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (e > 30) {
|
||||
float volatile f = 1e12f;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
f *= f; /* NOTE(bill): Cause overflow */
|
||||
}
|
||||
|
||||
return cast(i16)(s | 0x7c00);
|
||||
}
|
||||
|
||||
return cast(i16)(s | (e << 10) | (m >> 13));
|
||||
}
|
||||
}
|
||||
|
||||
f64 gb_sqrt(f64 x) {
|
||||
return sqrt(x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Doubly Linked Lists
|
||||
|
||||
#define DLIST_SET(curr_element, next_element) do { \
|
||||
(curr_element)->next = (next_element); \
|
||||
(curr_element)->next->prev = (curr_element); \
|
||||
(curr_element) = (curr_element)->next; \
|
||||
} while (0)
|
||||
|
||||
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
|
||||
if ((root_element) == nullptr) { \
|
||||
(root_element) = (curr_element) = (next_element); \
|
||||
} else { \
|
||||
DLIST_SET(curr_element, next_element); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
|
||||
u32 i, j;
|
||||
|
||||
u32 len = cast(u32)string16_len(cmd_line);
|
||||
i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
|
||||
|
||||
wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
|
||||
wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i);
|
||||
|
||||
u32 argc = 0;
|
||||
argv[argc] = _argv;
|
||||
bool in_quote = false;
|
||||
bool in_text = false;
|
||||
bool in_space = true;
|
||||
i = 0;
|
||||
j = 0;
|
||||
|
||||
for (;;) {
|
||||
wchar_t a = cmd_line[i];
|
||||
if (a == 0) {
|
||||
break;
|
||||
}
|
||||
if (in_quote) {
|
||||
if (a == '\"') {
|
||||
in_quote = false;
|
||||
} else {
|
||||
_argv[j++] = a;
|
||||
}
|
||||
} else {
|
||||
switch (a) {
|
||||
case '\"':
|
||||
in_quote = true;
|
||||
in_text = true;
|
||||
if (in_space) argv[argc++] = _argv+j;
|
||||
in_space = false;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (in_text) _argv[j++] = '\0';
|
||||
in_text = false;
|
||||
in_space = true;
|
||||
break;
|
||||
default:
|
||||
in_text = true;
|
||||
if (in_space) argv[argc++] = _argv+j;
|
||||
_argv[j++] = a;
|
||||
in_space = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
_argv[j] = '\0';
|
||||
argv[argc] = nullptr;
|
||||
|
||||
if (_argc) *_argc = argc;
|
||||
return argv;
|
||||
}
|
||||
|
||||
#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);
|
||||
return make_string_c(fullpath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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<FileInfo> *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)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
ReadDirectoryError read_directory(String path, Array<FileInfo> *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) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
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
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Generates Documentation
|
||||
|
||||
gbString expr_to_string(Ast *expression);
|
||||
|
||||
String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
|
||||
isize len = 0;
|
||||
for_array(i, g.list) {
|
||||
String comment = g.list[i].string;
|
||||
len += comment.len;
|
||||
len += 1; // for \n
|
||||
}
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
u8 *text = gb_alloc_array(a, u8, len+1);
|
||||
len = 0;
|
||||
for_array(i, g.list) {
|
||||
String comment = g.list[i].string;
|
||||
if (comment[1] == '/') {
|
||||
comment.text += 2;
|
||||
comment.len -= 2;
|
||||
} else if (comment[1] == '*') {
|
||||
comment.text += 2;
|
||||
comment.len -= 4;
|
||||
}
|
||||
comment = string_trim_whitespace(comment);
|
||||
gb_memmove(text+len, comment.text, comment.len);
|
||||
len += comment.len;
|
||||
text[len++] = '\n';
|
||||
}
|
||||
return make_string(text, len);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void print_type_spec(Ast *spec) {
|
||||
ast_node(ts, TypeSpec, spec);
|
||||
GB_ASSERT(ts->name->kind == Ast_Ident);
|
||||
String name = ts->name->Ident.string;
|
||||
if (name.len == 0) {
|
||||
return;
|
||||
}
|
||||
if (name[0] == '_') {
|
||||
return;
|
||||
}
|
||||
gb_printf("type %.*s\n", LIT(name));
|
||||
}
|
||||
|
||||
void print_proc_decl(AstProcDecl *pd) {
|
||||
GB_ASSERT(pd->name->kind == Ast_Ident);
|
||||
String name = pd->name->Ident.string;
|
||||
if (name.len == 0) {
|
||||
return;
|
||||
}
|
||||
if (name[0] == '_') {
|
||||
return;
|
||||
}
|
||||
|
||||
String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
|
||||
defer (gb_free(heap_allocator(), docs.text));
|
||||
|
||||
if (docs.len > 0) {
|
||||
gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
ast_node(proc_type, ProcType, pd->type);
|
||||
|
||||
gbString params = expr_to_string(proc_type->params);
|
||||
defer (gb_string_free(params));
|
||||
gb_printf("proc %.*s(%s)", LIT(name), params);
|
||||
if (proc_type->results != nullptr) {
|
||||
ast_node(fl, FieldList, proc_type->results);
|
||||
isize count = fl->list.count;
|
||||
if (count > 0) {
|
||||
gbString results = expr_to_string(proc_type->results);
|
||||
defer (gb_string_free(results));
|
||||
gb_printf(" -> ");
|
||||
if (count != 1) {
|
||||
gb_printf("(");
|
||||
}
|
||||
gb_printf("%s", results);
|
||||
if (count != 1) {
|
||||
gb_printf(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
gb_printf("\n\n");
|
||||
}
|
||||
#endif
|
||||
void print_declaration(Ast *decl) {
|
||||
}
|
||||
|
||||
void generate_documentation(Parser *parser) {
|
||||
// for_array(file_index, parser->files) {
|
||||
// AstFile *file = parser->files[file_index];
|
||||
// Tokenizer *tokenizer = &file->tokenizer;
|
||||
// String fullpath = tokenizer->fullpath;
|
||||
// gb_printf("%.*s\n", LIT(fullpath));
|
||||
|
||||
// for_array(decl_index, file->decls) {
|
||||
// Ast *decl = file->decls[decl_index];
|
||||
// print_declaration(decl);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
|
||||
Package: dyncall
|
||||
Library: dyncall
|
||||
File: dyncall/dyncall.h
|
||||
Description: public header for library dyncall
|
||||
License:
|
||||
|
||||
Copyright (c) 2007-2015 Daniel Adler <dadler@uni-goettingen.de>,
|
||||
Tassilo Philipp <tphilipp@potion-studios.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
|
||||
dyncall C API
|
||||
|
||||
REVISION
|
||||
2015/07/08 added SYS_PPC64 system call
|
||||
2015/01/16 added SYS_PPC32 system call
|
||||
2007/12/11 initial
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DYNCALL_H
|
||||
#define DYNCALL_H
|
||||
|
||||
#include "dyncall_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct DCCallVM_ DCCallVM;
|
||||
typedef struct DCstruct_ DCstruct;
|
||||
|
||||
/* Supported Calling Convention Modes */
|
||||
|
||||
#define DC_CALL_C_DEFAULT 0
|
||||
#define DC_CALL_C_ELLIPSIS 100
|
||||
#define DC_CALL_C_ELLIPSIS_VARARGS 101
|
||||
#define DC_CALL_C_X86_CDECL 1
|
||||
#define DC_CALL_C_X86_WIN32_STD 2
|
||||
#define DC_CALL_C_X86_WIN32_FAST_MS 3
|
||||
#define DC_CALL_C_X86_WIN32_FAST_GNU 4
|
||||
#define DC_CALL_C_X86_WIN32_THIS_MS 5
|
||||
#define DC_CALL_C_X86_WIN32_THIS_GNU 6
|
||||
#define DC_CALL_C_X64_WIN64 7
|
||||
#define DC_CALL_C_X64_SYSV 8
|
||||
#define DC_CALL_C_PPC32_DARWIN 9
|
||||
#define DC_CALL_C_PPC32_OSX DC_CALL_C_PPC32_DARWIN /* alias */
|
||||
#define DC_CALL_C_ARM_ARM_EABI 10
|
||||
#define DC_CALL_C_ARM_THUMB_EABI 11
|
||||
#define DC_CALL_C_ARM_ARMHF 30
|
||||
#define DC_CALL_C_MIPS32_EABI 12
|
||||
#define DC_CALL_C_MIPS32_PSPSDK DC_CALL_C_MIPS32_EABI /* alias - deprecated. */
|
||||
#define DC_CALL_C_PPC32_SYSV 13
|
||||
#define DC_CALL_C_PPC32_LINUX DC_CALL_C_PPC32_SYSV /* alias */
|
||||
#define DC_CALL_C_ARM_ARM 14
|
||||
#define DC_CALL_C_ARM_THUMB 15
|
||||
#define DC_CALL_C_MIPS32_O32 16
|
||||
#define DC_CALL_C_MIPS64_N32 17
|
||||
#define DC_CALL_C_MIPS64_N64 18
|
||||
#define DC_CALL_C_X86_PLAN9 19
|
||||
#define DC_CALL_C_SPARC32 20
|
||||
#define DC_CALL_C_SPARC64 21
|
||||
#define DC_CALL_C_ARM64 22
|
||||
#define DC_CALL_C_PPC64 23
|
||||
#define DC_CALL_C_PPC64_LINUX DC_CALL_C_PPC64 /* alias */
|
||||
#define DC_CALL_SYS_DEFAULT 200
|
||||
#define DC_CALL_SYS_X86_INT80H_LINUX 201
|
||||
#define DC_CALL_SYS_X86_INT80H_BSD 202
|
||||
#define DC_CALL_SYS_PPC32 210
|
||||
#define DC_CALL_SYS_PPC64 211
|
||||
|
||||
/* Error codes. */
|
||||
|
||||
#define DC_ERROR_NONE 0
|
||||
#define DC_ERROR_UNSUPPORTED_MODE -1
|
||||
|
||||
DC_API DCCallVM* dcNewCallVM (DCsize size);
|
||||
DC_API void dcFree (DCCallVM* vm);
|
||||
DC_API void dcReset (DCCallVM* vm);
|
||||
|
||||
DC_API void dcMode (DCCallVM* vm, DCint mode);
|
||||
|
||||
DC_API void dcArgBool (DCCallVM* vm, DCbool value);
|
||||
DC_API void dcArgChar (DCCallVM* vm, DCchar value);
|
||||
DC_API void dcArgShort (DCCallVM* vm, DCshort value);
|
||||
DC_API void dcArgInt (DCCallVM* vm, DCint value);
|
||||
DC_API void dcArgLong (DCCallVM* vm, DClong value);
|
||||
DC_API void dcArgLongLong (DCCallVM* vm, DClonglong value);
|
||||
DC_API void dcArgFloat (DCCallVM* vm, DCfloat value);
|
||||
DC_API void dcArgDouble (DCCallVM* vm, DCdouble value);
|
||||
DC_API void dcArgPointer (DCCallVM* vm, DCpointer value);
|
||||
DC_API void dcArgStruct (DCCallVM* vm, DCstruct* s, DCpointer value);
|
||||
|
||||
DC_API void dcCallVoid (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCbool dcCallBool (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCchar dcCallChar (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCshort dcCallShort (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCint dcCallInt (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DClong dcCallLong (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DClonglong dcCallLongLong (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCfloat dcCallFloat (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCdouble dcCallDouble (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API DCpointer dcCallPointer (DCCallVM* vm, DCpointer funcptr);
|
||||
DC_API void dcCallStruct (DCCallVM* vm, DCpointer funcptr, DCstruct* s, DCpointer returnValue);
|
||||
|
||||
DC_API DCint dcGetError (DCCallVM* vm);
|
||||
|
||||
#define DEFAULT_ALIGNMENT 0
|
||||
DC_API DCstruct* dcNewStruct (DCsize fieldCount, DCint alignment);
|
||||
DC_API void dcStructField (DCstruct* s, DCint type, DCint alignment, DCsize arrayLength);
|
||||
DC_API void dcSubStruct (DCstruct* s, DCsize fieldCount, DCint alignment, DCsize arrayLength);
|
||||
/* Each dcNewStruct or dcSubStruct call must be paired with a dcCloseStruct. */
|
||||
DC_API void dcCloseStruct (DCstruct* s);
|
||||
DC_API DCsize dcStructSize (DCstruct* s);
|
||||
DC_API DCsize dcStructAlignment(DCstruct* s);
|
||||
DC_API void dcFreeStruct (DCstruct* s);
|
||||
|
||||
DC_API DCstruct* dcDefineStruct (const char* signature);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DYNCALL_H */
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
|
||||
Package: dyncall
|
||||
Library: dyncallback
|
||||
File: dyncallback/dyncall_alloc_wx.h
|
||||
Description: Allocate write/executable memory - Interface
|
||||
License:
|
||||
|
||||
Copyright (c) 2007-2015 Daniel Adler <dadler@uni-goettingen.de>,
|
||||
Tassilo Philipp <tphilipp@potion-studios.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DYNCALL_ALLOC_WX_HPP
|
||||
#define DYNCALL_ALLOC_WX_HPP
|
||||
|
||||
#include "dyncall_types.h"
|
||||
|
||||
typedef int DCerror;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
DCerror dcAllocWX(DCsize size, void** p);
|
||||
void dcFreeWX (void* p, DCsize size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* DYNCALL_ALLOC_WX_HPP */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user