mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-09 21:11:36 -07:00
Compare commits
525 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 333924cce1 | |||
| 574b82c0c7 | |||
| f60c772c11 | |||
| 107740ca5e | |||
| 88b990eb63 | |||
| d2e7d730ac | |||
| 817e4b663e | |||
| 214bb73454 | |||
| eba2c74bff | |||
| 7c5e6c808b | |||
| ebe5beaafd | |||
| 029a6095d9 | |||
| 2c0e59ae06 | |||
| 9d1a4c304a | |||
| 13b8a1e348 | |||
| 0d4945dc87 | |||
| e0b9c4a275 | |||
| fec6df65b3 | |||
| 78494e84d5 | |||
| 60d7c833c0 | |||
| 98dbbf11f3 | |||
| f4924e39d4 | |||
| 826e05c96e | |||
| d3f63e5903 | |||
| 80c034ec7c | |||
| b41f09b730 | |||
| 06185e1769 | |||
| f8fa7fe380 | |||
| 45dbe8d354 | |||
| ddb99dd638 | |||
| 41aa4e606b | |||
| e025a828ca | |||
| 807e17207a | |||
| 3e18f5f057 | |||
| 9637cc5690 | |||
| ded99a2cab | |||
| 45eecc0905 | |||
| 87f1a62ca4 | |||
| c6d531df95 | |||
| 8677c81da7 | |||
| 5595daf5a3 | |||
| 64b5afd820 | |||
| 7692061eef | |||
| f7f2272c50 | |||
| 03fbdc3f75 | |||
| ea6a4859ed | |||
| 615fa82d1f | |||
| b60b310121 | |||
| c7f7e562a0 | |||
| a317237404 | |||
| 51ea59d76a | |||
| 789b297f32 | |||
| 3b25f924cb | |||
| cc6282a6e3 | |||
| 206a3e093c | |||
| 19bde275a3 | |||
| 634ee450f4 | |||
| 750d7256fc | |||
| fae5df2ed8 | |||
| 01d9161772 | |||
| aceabb2f2f | |||
| 04f5fff7fa | |||
| dc5587eae2 | |||
| 7057034b75 | |||
| 1430ca30a3 | |||
| e63393e394 | |||
| 784f3ecf7e | |||
| 54ea70df98 | |||
| d05ec5e484 | |||
| c7575164cc | |||
| 99125dc743 | |||
| b78e970698 | |||
| 5b8be25938 | |||
| 29efdc5fc1 | |||
| a80872b60d | |||
| 822bb51b55 | |||
| c2fa79012e | |||
| 3fd37c6dc5 | |||
| 0ea815db49 | |||
| 91ed51ff5c | |||
| 4d0afc55c3 | |||
| 9a1566d665 | |||
| a713e33007 | |||
| c5411a25a9 | |||
| 95692fda52 | |||
| 813a028ed0 | |||
| 0c22081e5f | |||
| 6d9fadf351 | |||
| a213061f33 | |||
| d1a0a46141 | |||
| 187b186112 | |||
| 5041a35b95 | |||
| 92d4fcedee | |||
| c69df7cd3a | |||
| 67d8f48553 | |||
| b4a339f2e3 | |||
| 0d7bf58b60 | |||
| abb9930725 | |||
| 169310a9f6 | |||
| 23a0a6de4b | |||
| 0d2dbee84e | |||
| d8d22e34dd | |||
| 627ee002e8 | |||
| 8e73d1ce1f | |||
| b53d16d1d5 | |||
| f5819eafa9 | |||
| 5916e71d4f | |||
| 913b9b6447 | |||
| 8e55bb2a6c | |||
| 98d493504b | |||
| 3a3202fbc6 | |||
| aaf355e750 | |||
| 0683d2b4f4 | |||
| d7fdd3d7b8 | |||
| 83ebb24015 | |||
| 70f9cacdce | |||
| 6b33b254e9 | |||
| c0019cc305 | |||
| c067a1f0ec | |||
| 63345cd0d8 | |||
| e41d6261c2 | |||
| 3e80411d37 | |||
| f952c7c747 | |||
| 642256f9ba | |||
| c9c82da1f3 | |||
| 382a5ca6a2 | |||
| 96e8bb5b6f | |||
| 22afac2b90 | |||
| 01da0d1377 | |||
| 8ce58573df | |||
| ce0d874efd | |||
| 2c8b99337b | |||
| 5008e2c88b | |||
| 90fc9abeae | |||
| dc303cde21 | |||
| 24b33374b7 | |||
| 3315dc7f25 | |||
| 77b3295de5 | |||
| 1349aa6f2c | |||
| a75ccb6fbc | |||
| 7a28827602 | |||
| c61015b1fe | |||
| e935f8e2ff | |||
| 690c682847 | |||
| f541dd40db | |||
| c7bb861d3c | |||
| 188b290dd5 | |||
| c6ff961088 | |||
| c26990c22d | |||
| c34d839f9f | |||
| 5562364a98 | |||
| 32150e401e | |||
| aaec8bf423 | |||
| 0fcbda951a | |||
| e2734a2dc6 | |||
| 5adfbec847 | |||
| 4ef4605d6d | |||
| 2aa402f462 | |||
| 00f6bee454 | |||
| 6e1864d21c | |||
| fb2d611dcd | |||
| d890731716 | |||
| 9e8c9be1ea | |||
| 231ea8b026 | |||
| 9bc37f4400 | |||
| f29e303ce7 | |||
| 3c9143957c | |||
| 18b3c0b2fc | |||
| c59f6b7d0b | |||
| 5bbdb3a3a3 | |||
| 67ed8a9a4a | |||
| 27aa07307b | |||
| 4cc4d604bc | |||
| eec709c545 | |||
| 9b2f5c359a | |||
| a982c51c30 | |||
| 20b9f1ff59 | |||
| 561c583b3f | |||
| 047c0e4bcc | |||
| 8d5896ab7e | |||
| a94dfdf21d | |||
| c0d5237b75 | |||
| 6fdcbefe5d | |||
| 3cec2550d9 | |||
| 758dd9ba16 | |||
| 0c37aa9ea0 | |||
| 9ff474f387 | |||
| d2f9d20833 | |||
| 802b1a70f8 | |||
| aaa4dd5c36 | |||
| 71100ed427 | |||
| 3ecf3505fd | |||
| daa1cd55a1 | |||
| 2722de65b7 | |||
| 8b5e3428a1 | |||
| d1f65097c4 | |||
| 74d15ab84b | |||
| 763cd2649d | |||
| 9d19ee7e4c | |||
| 8df3175f10 | |||
| ebb10e5597 | |||
| 047f883078 | |||
| 320c22e08a | |||
| bd27c24fab | |||
| 282f8bb06f | |||
| b9ed546ce0 | |||
| a9398bf30f | |||
| 7829421085 | |||
| c50aabd916 | |||
| 3f3122bccc | |||
| fc1a006de1 | |||
| e1fdd675ce | |||
| 754b368140 | |||
| a49e888ce6 | |||
| 4306345ff1 | |||
| 346aa5f71c | |||
| 99c663d9f3 | |||
| afac95e092 | |||
| 05486f9fa3 | |||
| cad46ae51c | |||
| 3424b2badd | |||
| 73d6a55f5c | |||
| f18ae89931 | |||
| 3445a28c4a | |||
| 7f6b83d50c | |||
| 72d4bfb32a | |||
| 37f7630a9e | |||
| 73c5c5d5d3 | |||
| 584869730a | |||
| 90ab448bca | |||
| 454d0b5cf5 | |||
| 8becbdc1b2 | |||
| eeeb90c441 | |||
| 219ca0ac46 | |||
| 5796c41357 | |||
| 8cfae17535 | |||
| 6efd400c98 | |||
| df78b8ad3e | |||
| f11d73ffaa | |||
| c126339090 | |||
| 5cfa4ba580 | |||
| 9f2d9b596d | |||
| 00c7489157 | |||
| b1562edccf | |||
| 2a5b674d33 | |||
| 7944b7714f | |||
| 205f4664f8 | |||
| c6133587d1 | |||
| 5516e80ab7 | |||
| 864310e3da | |||
| 4e7082a68d | |||
| 502e63b9c5 | |||
| 34150385d8 | |||
| 0ca1b4612c | |||
| 9e143a38ce | |||
| 43be91bca3 | |||
| 984e36a151 | |||
| ec9c8fb8a4 | |||
| 3e79ec4aef | |||
| 3e257ef8d0 | |||
| 626f91f307 | |||
| e86c990b75 | |||
| 31aacd5bf4 | |||
| 92453369c5 | |||
| 832009f33a | |||
| d3d3bfd455 | |||
| ce3582fd89 | |||
| e3e16f5d05 | |||
| f47f25f942 | |||
| e85458919c | |||
| b59a052e32 | |||
| 12498b2d39 | |||
| 6d93aa429f | |||
| 3f023509a7 | |||
| 563b1e2b28 | |||
| 4603d2525e | |||
| 2af9fb79dc | |||
| 367d307dc4 | |||
| cb59c1cf08 | |||
| 383f5b55ad | |||
| 6dc6b6f8aa | |||
| ac736aa4ec | |||
| 6fe25badf0 | |||
| c29d433e38 | |||
| ff473e8342 | |||
| 659e5359b2 | |||
| d9ce0b9da0 | |||
| 703e1aa2bc | |||
| b1e35b6da3 | |||
| fc1af0a04b | |||
| 4afb3f8fa4 | |||
| 207b252f23 | |||
| 1356dfeec2 | |||
| d025791462 | |||
| b07ee9ec23 | |||
| 915b5cdab7 | |||
| c8f99b360f | |||
| b76f6a8c27 | |||
| cff1b3dff6 | |||
| 883dd0642c | |||
| 40f5dd56f7 | |||
| 70d4ca00df | |||
| a86896e4d3 | |||
| a3883a178c | |||
| ce89a1428e | |||
| 9202bd1b06 | |||
| a48e0c7179 | |||
| 3f1195cd03 | |||
| 311b5cb6e2 | |||
| 6fef74317c | |||
| 0c6775ca14 | |||
| 6748f305db | |||
| 2ecafda1d3 | |||
| 23d32f34e5 | |||
| d714bece47 | |||
| 923b039cf6 | |||
| d0e1efe622 | |||
| 478d63424f | |||
| ac1566762b | |||
| f5eeecaca5 | |||
| 77e219d442 | |||
| 4c10fbdcd4 | |||
| e370337f97 | |||
| 5217eb55b4 | |||
| 062a2c63e1 | |||
| b957365b0d | |||
| 625b98eac4 | |||
| 0f809989e3 | |||
| d4457e9fa4 | |||
| 9634b28b07 | |||
| f567983260 | |||
| ad84314143 | |||
| a6f8c9d6e0 | |||
| de9016b7d0 | |||
| e8b4228833 | |||
| 0d69dfcde6 | |||
| fa89d2775a | |||
| 60b6538a7a | |||
| d9bd770992 | |||
| 517b34f798 | |||
| a16bdb215a | |||
| 88aa74bbb9 | |||
| 8ec9811d7a | |||
| c71b547cde | |||
| 76e724718c | |||
| 0b87313f08 | |||
| 4bb45700a5 | |||
| be8b9bda2f | |||
| ab2ca7cf59 | |||
| b76c8abe73 | |||
| d9c686b53d | |||
| b232b9d5ea |
+9
-3
@@ -251,7 +251,13 @@ paket-files/
|
|||||||
|
|
||||||
|
|
||||||
# Project Specific
|
# Project Specific
|
||||||
|
|
||||||
|
# - Windows
|
||||||
*.sln
|
*.sln
|
||||||
!misc/llvm-bim/lli.exe
|
builds/
|
||||||
!misc/llvm-bim/opt.exe
|
bin/
|
||||||
builds
|
*.exe
|
||||||
|
|
||||||
|
# - Linux/MacOS
|
||||||
|
odin
|
||||||
|
odin.dSYM
|
||||||
|
|||||||
@@ -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
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<img src="logo-slim.png" alt="Odin logo" height="74">
|
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
|
||||||
|
|
||||||
# The Odin Programming Language
|
# The Odin Programming Language
|
||||||
|
|
||||||
Odin is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
|
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
|
||||||
* simplicity
|
* simplicity
|
||||||
* high performance
|
* high performance
|
||||||
* built for modern systems
|
* built for modern systems
|
||||||
* joy of programming
|
* joy of programming
|
||||||
* metaprogramming
|
* metaprogramming
|
||||||
* designed for good programmers
|
|
||||||
|
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||||
|
|
||||||
## Demonstrations:
|
## Demonstrations:
|
||||||
* First Talk & Demo
|
* First Talk & Demo
|
||||||
@@ -18,14 +19,29 @@ Odin is fast, concise, readable, pragmatic and open sourced. It is designed with
|
|||||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
* [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)
|
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||||
|
* [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)
|
||||||
|
|
||||||
## Requirements to build and run
|
## Requirements to build and run
|
||||||
|
|
||||||
* Windows
|
- Windows
|
||||||
* x86-64
|
* x86-64
|
||||||
* MSVC 2015 installed (C99 support)
|
* MSVC 2015 installed (C++11 support)
|
||||||
* Requires MSVC's link.exe as the linker
|
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
|
||||||
- run `vcvarsall.bat` to setup the path
|
* Requires MSVC's link.exe as the linker
|
||||||
|
* run `vcvarsall.bat` to setup the path
|
||||||
|
|
||||||
|
- MacOS
|
||||||
|
* x86-64
|
||||||
|
* LLVM explicitly installed (`brew install llvm`)
|
||||||
|
* XCode installed (for the linker)
|
||||||
|
|
||||||
|
- GNU/Linux
|
||||||
|
* x86-64
|
||||||
|
* Build tools (ld)
|
||||||
|
* LLVM installed
|
||||||
|
* Clang installed (temporary - this is Calling the linker for now)
|
||||||
|
|
||||||
## Warnings
|
## Warnings
|
||||||
|
|
||||||
@@ -34,7 +50,7 @@ Odin is fast, concise, readable, pragmatic and open sourced. It is designed with
|
|||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
Not in any particular order
|
Not in any particular order and not be implemented
|
||||||
|
|
||||||
* Compile Time Execution (CTE)
|
* Compile Time Execution (CTE)
|
||||||
- More metaprogramming madness
|
- More metaprogramming madness
|
||||||
@@ -44,17 +60,11 @@ Not in any particular order
|
|||||||
* Replace LLVM backend with my own custom backend
|
* Replace LLVM backend with my own custom backend
|
||||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||||
* SSA optimizations
|
* SSA optimizations
|
||||||
* Parametric Polymorphism ("Generics")
|
|
||||||
* Documentation Generator for "Entities"
|
* Documentation Generator for "Entities"
|
||||||
* Multiple Architecture support
|
* Multiple Architecture support
|
||||||
* Language level atomics and concurrency support
|
|
||||||
* Linking Options
|
|
||||||
- Executable
|
|
||||||
- Static/Dynamic Library
|
|
||||||
* Debug Information
|
* Debug Information
|
||||||
- pdb format too
|
- pdb format too
|
||||||
* Command Line Tooling
|
* Command Line Tooling
|
||||||
* Compiler Internals:
|
* Compiler Internals:
|
||||||
- Big numbers library
|
- Big numbers library
|
||||||
- Cyclic Type Checking (at the moment will cause compiler to go into an infinite loop)
|
|
||||||
- Multithreading for performance increase
|
- Multithreading for performance increase
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
This file is a list of the people responsible for ensuring that patches for a
|
|
||||||
particular part of LLVM are reviewed, either by themself or by someone else.
|
|
||||||
They are also the gatekeepers for their part of LLVM, with the final word on
|
|
||||||
what goes in or not.
|
|
||||||
|
|
||||||
The list is sorted by surname and formatted to allow easy grepping and
|
|
||||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
|
||||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
|
||||||
(S). Each entry should contain at least the (N), (E) and (D) fields.
|
|
||||||
|
|
||||||
N: Joe Abbey
|
|
||||||
E: jabbey@arxan.com
|
|
||||||
D: LLVM Bitcode (lib/Bitcode/* include/llvm/Bitcode/*)
|
|
||||||
|
|
||||||
N: Owen Anderson
|
|
||||||
E: resistor@mac.com
|
|
||||||
D: SelectionDAG (lib/CodeGen/SelectionDAG/*)
|
|
||||||
|
|
||||||
N: Rafael Avila de Espindola
|
|
||||||
E: rafael.espindola@gmail.com
|
|
||||||
D: Gold plugin (tools/gold/*)
|
|
||||||
|
|
||||||
N: Justin Bogner
|
|
||||||
E: mail@justinbogner.com
|
|
||||||
D: InstrProfiling and related parts of ProfileData
|
|
||||||
|
|
||||||
N: Chandler Carruth
|
|
||||||
E: chandlerc@gmail.com
|
|
||||||
E: chandlerc@google.com
|
|
||||||
D: Config, ADT, Support, inlining & related passes, SROA/mem2reg & related passes, CMake, library layering
|
|
||||||
|
|
||||||
N: Evan Cheng
|
|
||||||
E: evan.cheng@apple.com
|
|
||||||
D: parts of code generator not covered by someone else
|
|
||||||
|
|
||||||
N: Eric Christopher
|
|
||||||
E: echristo@gmail.com
|
|
||||||
D: Debug Information, autotools/configure/make build, inline assembly
|
|
||||||
|
|
||||||
N: Greg Clayton
|
|
||||||
E: gclayton@apple.com
|
|
||||||
D: LLDB
|
|
||||||
|
|
||||||
N: Marshall Clow
|
|
||||||
E: mclow.lists@gmail.com
|
|
||||||
D: libc++
|
|
||||||
|
|
||||||
N: Peter Collingbourne
|
|
||||||
E: peter@pcc.me.uk
|
|
||||||
D: llgo
|
|
||||||
|
|
||||||
N: Quentin Colombet
|
|
||||||
E: qcolombet@apple.com
|
|
||||||
D: Register allocators
|
|
||||||
|
|
||||||
N: Duncan P. N. Exon Smith
|
|
||||||
E: dexonsmith@apple.com
|
|
||||||
D: Branch weights and BlockFrequencyInfo
|
|
||||||
|
|
||||||
N: Hal Finkel
|
|
||||||
E: hfinkel@anl.gov
|
|
||||||
D: BBVectorize, the loop reroller, alias analysis and the PowerPC target
|
|
||||||
|
|
||||||
N: Dan Gohman
|
|
||||||
E: sunfish@mozilla.com
|
|
||||||
D: WebAssembly Backend (lib/Target/WebAssembly/*)
|
|
||||||
|
|
||||||
N: Renato Golin
|
|
||||||
E: renato.golin@linaro.org
|
|
||||||
D: ARM Linux support
|
|
||||||
|
|
||||||
N: Venkatraman Govindaraju
|
|
||||||
E: venkatra@cs.wisc.edu
|
|
||||||
D: Sparc Backend (lib/Target/Sparc/*)
|
|
||||||
|
|
||||||
N: Tobias Grosser
|
|
||||||
E: tobias@grosser.es
|
|
||||||
D: Polly
|
|
||||||
|
|
||||||
N: James Grosbach
|
|
||||||
E: grosbach@apple.com
|
|
||||||
D: MC layer
|
|
||||||
|
|
||||||
N: Justin Holewinski
|
|
||||||
E: jholewinski@nvidia.com
|
|
||||||
D: NVPTX Target (lib/Target/NVPTX/*)
|
|
||||||
|
|
||||||
N: Lang Hames
|
|
||||||
E: lhames@gmail.com
|
|
||||||
D: MCJIT, RuntimeDyld and JIT event listeners
|
|
||||||
|
|
||||||
N: Galina Kistanova
|
|
||||||
E: gkistanova@gmail.com
|
|
||||||
D: LLVM Buildbot
|
|
||||||
|
|
||||||
N: Anton Korobeynikov
|
|
||||||
E: anton@korobeynikov.info
|
|
||||||
D: Exception handling, Windows codegen, ARM EABI
|
|
||||||
|
|
||||||
N: Benjamin Kramer
|
|
||||||
E: benny.kra@gmail.com
|
|
||||||
D: DWARF Parser
|
|
||||||
|
|
||||||
N: Sergei Larin
|
|
||||||
E: slarin@codeaurora.org
|
|
||||||
D: VLIW Instruction Scheduling, Packetization
|
|
||||||
|
|
||||||
N: Chris Lattner
|
|
||||||
E: sabre@nondot.org
|
|
||||||
W: http://nondot.org/~sabre/
|
|
||||||
D: Everything not covered by someone else
|
|
||||||
|
|
||||||
N: David Majnemer
|
|
||||||
E: david.majnemer@gmail.com
|
|
||||||
D: IR Constant Folder, InstCombine
|
|
||||||
|
|
||||||
N: Dylan McKay
|
|
||||||
E: dylanmckay34@gmail.com
|
|
||||||
D: AVR Backend
|
|
||||||
|
|
||||||
N: Tim Northover
|
|
||||||
E: t.p.northover@gmail.com
|
|
||||||
D: AArch64 backend, misc ARM backend
|
|
||||||
|
|
||||||
N: Diego Novillo
|
|
||||||
E: dnovillo@google.com
|
|
||||||
D: SampleProfile and related parts of ProfileData
|
|
||||||
|
|
||||||
N: Jakob Olesen
|
|
||||||
E: stoklund@2pi.dk
|
|
||||||
D: TableGen
|
|
||||||
|
|
||||||
N: Richard Osborne
|
|
||||||
E: richard@xmos.com
|
|
||||||
D: XCore Backend
|
|
||||||
|
|
||||||
N: Krzysztof Parzyszek
|
|
||||||
E: kparzysz@codeaurora.org
|
|
||||||
D: Hexagon Backend
|
|
||||||
|
|
||||||
N: Paul Robinson
|
|
||||||
E: paul_robinson@playstation.sony.com
|
|
||||||
D: Sony PlayStation®4 support
|
|
||||||
|
|
||||||
N: Chad Rosier
|
|
||||||
E: mcrosier@codeaurora.org
|
|
||||||
D: Fast-Isel
|
|
||||||
|
|
||||||
N: Nadav Rotem
|
|
||||||
E: nrotem@apple.com
|
|
||||||
D: X86 Backend, Loop Vectorizer
|
|
||||||
|
|
||||||
N: Daniel Sanders
|
|
||||||
E: daniel.sanders@imgtec.com
|
|
||||||
D: MIPS Backend (lib/Target/Mips/*)
|
|
||||||
|
|
||||||
N: Duncan Sands
|
|
||||||
E: baldrick@free.fr
|
|
||||||
D: DragonEgg
|
|
||||||
|
|
||||||
N: Kostya Serebryany
|
|
||||||
E: kcc@google.com
|
|
||||||
D: AddressSanitizer, ThreadSanitizer (LLVM parts)
|
|
||||||
|
|
||||||
N: Michael Spencer
|
|
||||||
E: bigcheesegs@gmail.com
|
|
||||||
D: Windows parts of Support, Object, ar, nm, objdump, ranlib, size
|
|
||||||
|
|
||||||
N: Alexei Starovoitov
|
|
||||||
E: alexei.starovoitov@gmail.com
|
|
||||||
D: BPF backend
|
|
||||||
|
|
||||||
N: Tom Stellard
|
|
||||||
E: thomas.stellard@amd.com
|
|
||||||
E: mesa-dev@lists.freedesktop.org
|
|
||||||
D: Release manager for the 3.5 and 3.6 branches, R600 Backend, libclc
|
|
||||||
|
|
||||||
N: Evgeniy Stepanov
|
|
||||||
E: eugenis@google.com
|
|
||||||
D: MemorySanitizer (LLVM part)
|
|
||||||
|
|
||||||
N: Andrew Trick
|
|
||||||
E: atrick@apple.com
|
|
||||||
D: IndVar Simplify, Loop Strength Reduction, Instruction Scheduling
|
|
||||||
|
|
||||||
N: Ulrich Weigand
|
|
||||||
E: uweigand@de.ibm.com
|
|
||||||
D: SystemZ Backend
|
|
||||||
|
|
||||||
N: Bill Wendling
|
|
||||||
E: isanbard@gmail.com
|
|
||||||
D: libLTO, IR Linker
|
|
||||||
|
|
||||||
N: Peter Zotov
|
|
||||||
E: whitequark@whitequark.org
|
|
||||||
D: OCaml bindings
|
|
||||||
|
|
||||||
N: Andrey Churbanov
|
|
||||||
E: andrey.churbanov@intel.com
|
|
||||||
D: OpenMP runtime library
|
|
||||||
-467
@@ -1,467 +0,0 @@
|
|||||||
This file is a partial list of people who have contributed to the LLVM
|
|
||||||
project. If you have contributed a patch or made some other contribution to
|
|
||||||
LLVM, please submit a patch to this file to add yourself, and it will be
|
|
||||||
done!
|
|
||||||
|
|
||||||
The list is sorted by surname and formatted to allow easy grepping and
|
|
||||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
|
||||||
(W), PGP key ID and fingerprint (P), description (D), snail-mail address
|
|
||||||
(S), and (I) IRC handle.
|
|
||||||
|
|
||||||
|
|
||||||
N: Vikram Adve
|
|
||||||
E: vadve@cs.uiuc.edu
|
|
||||||
W: http://www.cs.uiuc.edu/~vadve/
|
|
||||||
D: The Sparc64 backend, provider of much wisdom, and motivator for LLVM
|
|
||||||
|
|
||||||
N: Owen Anderson
|
|
||||||
E: resistor@mac.com
|
|
||||||
D: LCSSA pass and related LoopUnswitch work
|
|
||||||
D: GVNPRE pass, DataLayout refactoring, random improvements
|
|
||||||
|
|
||||||
N: Henrik Bach
|
|
||||||
D: MingW Win32 API portability layer
|
|
||||||
|
|
||||||
N: Aaron Ballman
|
|
||||||
E: aaron@aaronballman.com
|
|
||||||
D: __declspec attributes, Windows support, general bug fixing
|
|
||||||
|
|
||||||
N: Nate Begeman
|
|
||||||
E: natebegeman@mac.com
|
|
||||||
D: PowerPC backend developer
|
|
||||||
D: Target-independent code generator and analysis improvements
|
|
||||||
|
|
||||||
N: Daniel Berlin
|
|
||||||
E: dberlin@dberlin.org
|
|
||||||
D: ET-Forest implementation.
|
|
||||||
D: Sparse bitmap
|
|
||||||
|
|
||||||
N: David Blaikie
|
|
||||||
E: dblaikie@gmail.com
|
|
||||||
D: General bug fixing/fit & finish, mostly in Clang
|
|
||||||
|
|
||||||
N: Neil Booth
|
|
||||||
E: neil@daikokuya.co.uk
|
|
||||||
D: APFloat implementation.
|
|
||||||
|
|
||||||
N: Misha Brukman
|
|
||||||
E: brukman+llvm@uiuc.edu
|
|
||||||
W: http://misha.brukman.net
|
|
||||||
D: Portions of X86 and Sparc JIT compilers, PowerPC backend
|
|
||||||
D: Incremental bitcode loader
|
|
||||||
|
|
||||||
N: Cameron Buschardt
|
|
||||||
E: buschard@uiuc.edu
|
|
||||||
D: The `mem2reg' pass - promotes values stored in memory to registers
|
|
||||||
|
|
||||||
N: Brendon Cahoon
|
|
||||||
E: bcahoon@codeaurora.org
|
|
||||||
D: Loop unrolling with run-time trip counts.
|
|
||||||
|
|
||||||
N: Chandler Carruth
|
|
||||||
E: chandlerc@gmail.com
|
|
||||||
E: chandlerc@google.com
|
|
||||||
D: Hashing algorithms and interfaces
|
|
||||||
D: Inline cost analysis
|
|
||||||
D: Machine block placement pass
|
|
||||||
D: SROA
|
|
||||||
|
|
||||||
N: Casey Carter
|
|
||||||
E: ccarter@uiuc.edu
|
|
||||||
D: Fixes to the Reassociation pass, various improvement patches
|
|
||||||
|
|
||||||
N: Evan Cheng
|
|
||||||
E: evan.cheng@apple.com
|
|
||||||
D: ARM and X86 backends
|
|
||||||
D: Instruction scheduler improvements
|
|
||||||
D: Register allocator improvements
|
|
||||||
D: Loop optimizer improvements
|
|
||||||
D: Target-independent code generator improvements
|
|
||||||
|
|
||||||
N: Dan Villiom Podlaski Christiansen
|
|
||||||
E: danchr@gmail.com
|
|
||||||
E: danchr@cs.au.dk
|
|
||||||
W: http://villiom.dk
|
|
||||||
D: LLVM Makefile improvements
|
|
||||||
D: Clang diagnostic & driver tweaks
|
|
||||||
S: Aarhus, Denmark
|
|
||||||
|
|
||||||
N: Jeff Cohen
|
|
||||||
E: jeffc@jolt-lang.org
|
|
||||||
W: http://jolt-lang.org
|
|
||||||
D: Native Win32 API portability layer
|
|
||||||
|
|
||||||
N: John T. Criswell
|
|
||||||
E: criswell@uiuc.edu
|
|
||||||
D: Original Autoconf support, documentation improvements, bug fixes
|
|
||||||
|
|
||||||
N: Anshuman Dasgupta
|
|
||||||
E: adasgupt@codeaurora.org
|
|
||||||
D: Deterministic finite automaton based infrastructure for VLIW packetization
|
|
||||||
|
|
||||||
N: Stefanus Du Toit
|
|
||||||
E: stefanus.du.toit@intel.com
|
|
||||||
D: Bug fixes and minor improvements
|
|
||||||
|
|
||||||
N: Rafael Avila de Espindola
|
|
||||||
E: rafael.espindola@gmail.com
|
|
||||||
D: The ARM backend
|
|
||||||
|
|
||||||
N: Dave Estes
|
|
||||||
E: cestes@codeaurora.org
|
|
||||||
D: AArch64 machine description for Cortex-A53
|
|
||||||
|
|
||||||
N: Alkis Evlogimenos
|
|
||||||
E: alkis@evlogimenos.com
|
|
||||||
D: Linear scan register allocator, many codegen improvements, Java frontend
|
|
||||||
|
|
||||||
N: Hal Finkel
|
|
||||||
E: hfinkel@anl.gov
|
|
||||||
D: Basic-block autovectorization, PowerPC backend improvements
|
|
||||||
|
|
||||||
N: Eric Fiselier
|
|
||||||
E: eric@efcs.ca
|
|
||||||
D: LIT patches and documentation.
|
|
||||||
|
|
||||||
N: Ryan Flynn
|
|
||||||
E: pizza@parseerror.com
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
|
|
||||||
N: Brian Gaeke
|
|
||||||
E: gaeke@uiuc.edu
|
|
||||||
W: http://www.students.uiuc.edu/~gaeke/
|
|
||||||
D: Portions of X86 static and JIT compilers; initial SparcV8 backend
|
|
||||||
D: Dynamic trace optimizer
|
|
||||||
D: FreeBSD/X86 compatibility fixes, the llvm-nm tool
|
|
||||||
|
|
||||||
N: Nicolas Geoffray
|
|
||||||
E: nicolas.geoffray@lip6.fr
|
|
||||||
W: http://www-src.lip6.fr/homepages/Nicolas.Geoffray/
|
|
||||||
D: PPC backend fixes for Linux
|
|
||||||
|
|
||||||
N: Louis Gerbarg
|
|
||||||
E: lgg@apple.com
|
|
||||||
D: Portions of the PowerPC backend
|
|
||||||
|
|
||||||
N: Saem Ghani
|
|
||||||
E: saemghani@gmail.com
|
|
||||||
D: Callgraph class cleanups
|
|
||||||
|
|
||||||
N: Mikhail Glushenkov
|
|
||||||
E: foldr@codedgers.com
|
|
||||||
D: Author of llvmc2
|
|
||||||
|
|
||||||
N: Dan Gohman
|
|
||||||
E: sunfish@mozilla.com
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
D: WebAssembly Backend
|
|
||||||
|
|
||||||
N: David Goodwin
|
|
||||||
E: david@goodwinz.net
|
|
||||||
D: Thumb-2 code generator
|
|
||||||
|
|
||||||
N: David Greene
|
|
||||||
E: greened@obbligato.org
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
D: Register allocation refactoring
|
|
||||||
|
|
||||||
N: Gabor Greif
|
|
||||||
E: ggreif@gmail.com
|
|
||||||
D: Improvements for space efficiency
|
|
||||||
|
|
||||||
N: James Grosbach
|
|
||||||
E: grosbach@apple.com
|
|
||||||
I: grosbach
|
|
||||||
D: SjLj exception handling support
|
|
||||||
D: General fixes and improvements for the ARM back-end
|
|
||||||
D: MCJIT
|
|
||||||
D: ARM integrated assembler and assembly parser
|
|
||||||
D: Led effort for the backend formerly known as ARM64
|
|
||||||
|
|
||||||
N: Lang Hames
|
|
||||||
E: lhames@gmail.com
|
|
||||||
D: PBQP-based register allocator
|
|
||||||
|
|
||||||
N: Gordon Henriksen
|
|
||||||
E: gordonhenriksen@mac.com
|
|
||||||
D: Pluggable GC support
|
|
||||||
D: C interface
|
|
||||||
D: Ocaml bindings
|
|
||||||
|
|
||||||
N: Raul Fernandes Herbster
|
|
||||||
E: raul@dsc.ufcg.edu.br
|
|
||||||
D: JIT support for ARM
|
|
||||||
|
|
||||||
N: Paolo Invernizzi
|
|
||||||
E: arathorn@fastwebnet.it
|
|
||||||
D: Visual C++ compatibility fixes
|
|
||||||
|
|
||||||
N: Patrick Jenkins
|
|
||||||
E: patjenk@wam.umd.edu
|
|
||||||
D: Nightly Tester
|
|
||||||
|
|
||||||
N: Dale Johannesen
|
|
||||||
E: dalej@apple.com
|
|
||||||
D: ARM constant islands improvements
|
|
||||||
D: Tail merging improvements
|
|
||||||
D: Rewrite X87 back end
|
|
||||||
D: Use APFloat for floating point constants widely throughout compiler
|
|
||||||
D: Implement X87 long double
|
|
||||||
|
|
||||||
N: Brad Jones
|
|
||||||
E: kungfoomaster@nondot.org
|
|
||||||
D: Support for packed types
|
|
||||||
|
|
||||||
N: Rod Kay
|
|
||||||
E: rkay@auroraux.org
|
|
||||||
D: Author of LLVM Ada bindings
|
|
||||||
|
|
||||||
N: Eric Kidd
|
|
||||||
W: http://randomhacks.net/
|
|
||||||
D: llvm-config script
|
|
||||||
|
|
||||||
N: Anton Korobeynikov
|
|
||||||
E: asl@math.spbu.ru
|
|
||||||
D: Mingw32 fixes, cross-compiling support, stdcall/fastcall calling conv.
|
|
||||||
D: x86/linux PIC codegen, aliases, regparm/visibility attributes
|
|
||||||
D: Switch lowering refactoring
|
|
||||||
|
|
||||||
N: Sumant Kowshik
|
|
||||||
E: kowshik@uiuc.edu
|
|
||||||
D: Author of the original C backend
|
|
||||||
|
|
||||||
N: Benjamin Kramer
|
|
||||||
E: benny.kra@gmail.com
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
|
|
||||||
N: Sundeep Kushwaha
|
|
||||||
E: sundeepk@codeaurora.org
|
|
||||||
D: Implemented DFA-based target independent VLIW packetizer
|
|
||||||
|
|
||||||
N: Christopher Lamb
|
|
||||||
E: christopher.lamb@gmail.com
|
|
||||||
D: aligned load/store support, parts of noalias and restrict support
|
|
||||||
D: vreg subreg infrastructure, X86 codegen improvements based on subregs
|
|
||||||
D: address spaces
|
|
||||||
|
|
||||||
N: Jim Laskey
|
|
||||||
E: jlaskey@apple.com
|
|
||||||
D: Improvements to the PPC backend, instruction scheduling
|
|
||||||
D: Debug and Dwarf implementation
|
|
||||||
D: Auto upgrade mangler
|
|
||||||
D: llvm-gcc4 svn wrangler
|
|
||||||
|
|
||||||
N: Chris Lattner
|
|
||||||
E: sabre@nondot.org
|
|
||||||
W: http://nondot.org/~sabre/
|
|
||||||
D: Primary architect of LLVM
|
|
||||||
|
|
||||||
N: Tanya Lattner (Tanya Brethour)
|
|
||||||
E: tonic@nondot.org
|
|
||||||
W: http://nondot.org/~tonic/
|
|
||||||
D: The initial llvm-ar tool, converted regression testsuite to dejagnu
|
|
||||||
D: Modulo scheduling in the SparcV9 backend
|
|
||||||
D: Release manager (1.7+)
|
|
||||||
|
|
||||||
N: Sylvestre Ledru
|
|
||||||
E: sylvestre@debian.org
|
|
||||||
W: http://sylvestre.ledru.info/
|
|
||||||
W: http://llvm.org/apt/
|
|
||||||
D: Debian and Ubuntu packaging
|
|
||||||
D: Continuous integration with jenkins
|
|
||||||
|
|
||||||
N: Andrew Lenharth
|
|
||||||
E: alenhar2@cs.uiuc.edu
|
|
||||||
W: http://www.lenharth.org/~andrewl/
|
|
||||||
D: Alpha backend
|
|
||||||
D: Sampling based profiling
|
|
||||||
|
|
||||||
N: Nick Lewycky
|
|
||||||
E: nicholas@mxc.ca
|
|
||||||
D: PredicateSimplifier pass
|
|
||||||
|
|
||||||
N: Tony Linthicum, et. al.
|
|
||||||
E: tlinth@codeaurora.org
|
|
||||||
D: Backend for Qualcomm's Hexagon VLIW processor.
|
|
||||||
|
|
||||||
N: Bruno Cardoso Lopes
|
|
||||||
E: bruno.cardoso@gmail.com
|
|
||||||
I: bruno
|
|
||||||
W: http://brunocardoso.cc
|
|
||||||
D: Mips backend
|
|
||||||
D: Random ARM integrated assembler and assembly parser improvements
|
|
||||||
D: General X86 AVX1 support
|
|
||||||
|
|
||||||
N: Duraid Madina
|
|
||||||
E: duraid@octopus.com.au
|
|
||||||
W: http://kinoko.c.u-tokyo.ac.jp/~duraid/
|
|
||||||
D: IA64 backend, BigBlock register allocator
|
|
||||||
|
|
||||||
N: John McCall
|
|
||||||
E: rjmccall@apple.com
|
|
||||||
D: Clang semantic analysis and IR generation
|
|
||||||
|
|
||||||
N: Michael McCracken
|
|
||||||
E: michael.mccracken@gmail.com
|
|
||||||
D: Line number support for llvmgcc
|
|
||||||
|
|
||||||
N: Vladimir Merzliakov
|
|
||||||
E: wanderer@rsu.ru
|
|
||||||
D: Test suite fixes for FreeBSD
|
|
||||||
|
|
||||||
N: Scott Michel
|
|
||||||
E: scottm@aero.org
|
|
||||||
D: Added STI Cell SPU backend.
|
|
||||||
|
|
||||||
N: Kai Nacke
|
|
||||||
E: kai@redstar.de
|
|
||||||
D: Support for implicit TLS model used with MS VC runtime
|
|
||||||
D: Dumping of Win64 EH structures
|
|
||||||
|
|
||||||
N: Takumi Nakamura
|
|
||||||
E: geek4civic@gmail.com
|
|
||||||
E: chapuni@hf.rim.or.jp
|
|
||||||
D: Cygwin and MinGW support.
|
|
||||||
D: Win32 tweaks.
|
|
||||||
S: Yokohama, Japan
|
|
||||||
|
|
||||||
N: Edward O'Callaghan
|
|
||||||
E: eocallaghan@auroraux.org
|
|
||||||
W: http://www.auroraux.org
|
|
||||||
D: Add Clang support with various other improvements to utils/NewNightlyTest.pl
|
|
||||||
D: Fix and maintain Solaris & AuroraUX support for llvm, various build warnings
|
|
||||||
D: and error clean ups.
|
|
||||||
|
|
||||||
N: Morten Ofstad
|
|
||||||
E: morten@hue.no
|
|
||||||
D: Visual C++ compatibility fixes
|
|
||||||
|
|
||||||
N: Jakob Stoklund Olesen
|
|
||||||
E: stoklund@2pi.dk
|
|
||||||
D: Machine code verifier
|
|
||||||
D: Blackfin backend
|
|
||||||
D: Fast register allocator
|
|
||||||
D: Greedy register allocator
|
|
||||||
|
|
||||||
N: Richard Osborne
|
|
||||||
E: richard@xmos.com
|
|
||||||
D: XCore backend
|
|
||||||
|
|
||||||
N: Devang Patel
|
|
||||||
E: dpatel@apple.com
|
|
||||||
D: LTO tool, PassManager rewrite, Loop Pass Manager, Loop Rotate
|
|
||||||
D: GCC PCH Integration (llvm-gcc), llvm-gcc improvements
|
|
||||||
D: Optimizer improvements, Loop Index Split
|
|
||||||
|
|
||||||
N: Ana Pazos
|
|
||||||
E: apazos@codeaurora.org
|
|
||||||
D: Fixes and improvements to the AArch64 backend
|
|
||||||
|
|
||||||
N: Wesley Peck
|
|
||||||
E: peckw@wesleypeck.com
|
|
||||||
W: http://wesleypeck.com/
|
|
||||||
D: MicroBlaze backend
|
|
||||||
|
|
||||||
N: Francois Pichet
|
|
||||||
E: pichet2000@gmail.com
|
|
||||||
D: MSVC support
|
|
||||||
|
|
||||||
N: Vladimir Prus
|
|
||||||
W: http://vladimir_prus.blogspot.com
|
|
||||||
E: ghost@cs.msu.su
|
|
||||||
D: Made inst_iterator behave like a proper iterator, LowerConstantExprs pass
|
|
||||||
|
|
||||||
N: Kalle Raiskila
|
|
||||||
E: kalle.rasikila@nokia.com
|
|
||||||
D: Some bugfixes to CellSPU
|
|
||||||
|
|
||||||
N: Xerxes Ranby
|
|
||||||
E: xerxes@zafena.se
|
|
||||||
D: Cmake dependency chain and various bug fixes
|
|
||||||
|
|
||||||
N: Alex Rosenberg
|
|
||||||
E: alexr@leftfield.org
|
|
||||||
I: arosenberg
|
|
||||||
D: ARM calling conventions rewrite, hard float support
|
|
||||||
|
|
||||||
N: Chad Rosier
|
|
||||||
E: mcrosier@codeaurora.org
|
|
||||||
I: mcrosier
|
|
||||||
D: AArch64 fast instruction selection pass
|
|
||||||
D: Fixes and improvements to the ARM fast-isel pass
|
|
||||||
D: Fixes and improvements to the AArch64 backend
|
|
||||||
|
|
||||||
N: Nadav Rotem
|
|
||||||
E: nrotem@apple.com
|
|
||||||
D: X86 code generation improvements, Loop Vectorizer.
|
|
||||||
|
|
||||||
N: Roman Samoilov
|
|
||||||
E: roman@codedgers.com
|
|
||||||
D: MSIL backend
|
|
||||||
|
|
||||||
N: Duncan Sands
|
|
||||||
E: baldrick@free.fr
|
|
||||||
I: baldrick
|
|
||||||
D: Ada support in llvm-gcc
|
|
||||||
D: Dragonegg plugin
|
|
||||||
D: Exception handling improvements
|
|
||||||
D: Type legalizer rewrite
|
|
||||||
|
|
||||||
N: Ruchira Sasanka
|
|
||||||
E: sasanka@uiuc.edu
|
|
||||||
D: Graph coloring register allocator for the Sparc64 backend
|
|
||||||
|
|
||||||
N: Arnold Schwaighofer
|
|
||||||
E: arnold.schwaighofer@gmail.com
|
|
||||||
D: Tail call optimization for the x86 backend
|
|
||||||
|
|
||||||
N: Shantonu Sen
|
|
||||||
E: ssen@apple.com
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
|
|
||||||
N: Anand Shukla
|
|
||||||
E: ashukla@cs.uiuc.edu
|
|
||||||
D: The `paths' pass
|
|
||||||
|
|
||||||
N: Michael J. Spencer
|
|
||||||
E: bigcheesegs@gmail.com
|
|
||||||
D: Shepherding Windows COFF support into MC.
|
|
||||||
D: Lots of Windows stuff.
|
|
||||||
|
|
||||||
N: Reid Spencer
|
|
||||||
E: rspencer@reidspencer.com
|
|
||||||
W: http://reidspencer.com/
|
|
||||||
D: Lots of stuff, see: http://wiki.llvm.org/index.php/User:Reid
|
|
||||||
|
|
||||||
N: Alp Toker
|
|
||||||
E: alp@nuanti.com
|
|
||||||
W: http://atoker.com/
|
|
||||||
D: C++ frontend next generation standards implementation
|
|
||||||
|
|
||||||
N: Craig Topper
|
|
||||||
E: craig.topper@gmail.com
|
|
||||||
D: X86 codegen and disassembler improvements. AVX2 support.
|
|
||||||
|
|
||||||
N: Edwin Torok
|
|
||||||
E: edwintorok@gmail.com
|
|
||||||
D: Miscellaneous bug fixes
|
|
||||||
|
|
||||||
N: Adam Treat
|
|
||||||
E: manyoso@yahoo.com
|
|
||||||
D: C++ bugs filed, and C++ front-end bug fixes.
|
|
||||||
|
|
||||||
N: Lauro Ramos Venancio
|
|
||||||
E: lauro.venancio@indt.org.br
|
|
||||||
D: ARM backend improvements
|
|
||||||
D: Thread Local Storage implementation
|
|
||||||
|
|
||||||
N: Bill Wendling
|
|
||||||
I: wendling
|
|
||||||
E: isanbard@gmail.com
|
|
||||||
D: Release manager, IR Linker, LTO
|
|
||||||
D: Bunches of stuff
|
|
||||||
|
|
||||||
N: Bob Wilson
|
|
||||||
E: bob.wilson@acm.org
|
|
||||||
D: Advanced SIMD (NEON) support in the ARM backend.
|
|
||||||
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
==============================================================================
|
|
||||||
LLVM Release License
|
|
||||||
==============================================================================
|
|
||||||
University of Illinois/NCSA
|
|
||||||
Open Source License
|
|
||||||
|
|
||||||
Copyright (c) 2003-2015 University of Illinois at Urbana-Champaign.
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Developed by:
|
|
||||||
|
|
||||||
LLVM Team
|
|
||||||
|
|
||||||
University of Illinois at Urbana-Champaign
|
|
||||||
|
|
||||||
http://llvm.org
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal with
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do
|
|
||||||
so, subject to the following conditions:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimers.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimers in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the names of the LLVM Team, University of Illinois at
|
|
||||||
Urbana-Champaign, nor the names of its contributors may be used to
|
|
||||||
endorse or promote products derived from this Software without specific
|
|
||||||
prior written permission.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
==============================================================================
|
|
||||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
|
||||||
==============================================================================
|
|
||||||
The LLVM software contains code written by third parties. Such software will
|
|
||||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
|
||||||
This file will describe the copyrights, license, and restrictions which apply
|
|
||||||
to that code.
|
|
||||||
|
|
||||||
The disclaimer of warranty in the University of Illinois Open Source License
|
|
||||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
|
||||||
other licenses gives permission to use the names of the LLVM Team or the
|
|
||||||
University of Illinois to endorse or promote products derived from this
|
|
||||||
Software.
|
|
||||||
|
|
||||||
The following pieces of software have additional or alternate copyrights,
|
|
||||||
licenses, and/or restrictions:
|
|
||||||
|
|
||||||
Program Directory
|
|
||||||
------- ---------
|
|
||||||
Autoconf llvm/autoconf
|
|
||||||
llvm/projects/ModuleMaker/autoconf
|
|
||||||
Google Test llvm/utils/unittest/googletest
|
|
||||||
OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex}
|
|
||||||
pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
|
|
||||||
ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
|
|
||||||
md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
|
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -5,8 +5,7 @@ set exe_name=odin.exe
|
|||||||
|
|
||||||
:: Debug = 0, Release = 1
|
:: Debug = 0, Release = 1
|
||||||
set release_mode=1
|
set release_mode=1
|
||||||
|
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||||
set compiler_flags= -nologo -Oi -TC -W4 -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||||
@@ -16,7 +15,7 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
)
|
)
|
||||||
|
|
||||||
set compiler_warnings= ^
|
set compiler_warnings= ^
|
||||||
-we4013 -we4706 -we4002 -we4133 ^
|
-W4 -WX ^
|
||||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||||
-wd4201 -wd4204 -wd4244 ^
|
-wd4201 -wd4204 -wd4244 ^
|
||||||
-wd4306 ^
|
-wd4306 ^
|
||||||
@@ -39,20 +38,15 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||||
set linker_settings=%libs% %linker_flags%
|
set linker_settings=%libs% %linker_flags%
|
||||||
|
|
||||||
|
del *.pdb > NUL 2> NUL
|
||||||
|
del *.ilk > NUL 2> NUL
|
||||||
|
|
||||||
rem set build_dir= "\"
|
cl %compiler_settings% "src\main.cpp" ^
|
||||||
rem if not exist %build_dir% mkdir %build_dir%
|
/link %linker_settings% -OUT:%exe_name% ^
|
||||||
rem pushd %build_dir%
|
&& odin run code/demo.odin -opt=0
|
||||||
del *.pdb > NUL 2> NUL
|
rem && odin docs core/fmt.odin
|
||||||
del *.ilk > NUL 2> NUL
|
|
||||||
|
|
||||||
cl %compiler_settings% "src\main.c" ^
|
del *.obj > NUL 2> NUL
|
||||||
/link %linker_settings% -OUT:%exe_name% ^
|
|
||||||
&& odin run code/demo.odin
|
|
||||||
rem odin run code/demo.odin
|
|
||||||
|
|
||||||
|
|
||||||
:do_not_compile_exe
|
|
||||||
rem popd
|
|
||||||
:end_of_build
|
:end_of_build
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
release_mode=0
|
||||||
|
|
||||||
|
warnings_to_disable="-std=c++11 -g -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 [ "$release_mode" -eq "0" ]; then
|
||||||
|
other_args="${other_args} -g -fno-inline-functions"
|
||||||
|
fi
|
||||||
|
if [[ "$(uname)" == "Darwin" ]]; then
|
||||||
|
|
||||||
|
# Set compiler to clang on MacOS
|
||||||
|
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||||
|
compiler="clang"
|
||||||
|
|
||||||
|
other_args="${other_args} -liconv"
|
||||||
|
fi
|
||||||
|
|
||||||
|
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||||
|
|
||||||
|
./odin run code/demo.odin
|
||||||
+597
-28
@@ -1,32 +1,601 @@
|
|||||||
#import "fmt.odin"
|
|
||||||
#import "utf8.odin"
|
import (
|
||||||
|
"fmt.odin";
|
||||||
|
"strconv.odin";
|
||||||
|
"mem.odin";
|
||||||
|
"thread.odin" when ODIN_OS == "windows";
|
||||||
|
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
|
|
||||||
|
/*
|
||||||
|
"atomics.odin";
|
||||||
|
"bits.odin";
|
||||||
|
"hash.odin";
|
||||||
|
"math.odin";
|
||||||
|
"opengl.odin";
|
||||||
|
"os.odin";
|
||||||
|
"raw.odin";
|
||||||
|
"sort.odin";
|
||||||
|
"strings.odin";
|
||||||
|
"sync.odin";
|
||||||
|
"types.odin";
|
||||||
|
"utf8.odin";
|
||||||
|
"utf16.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nested_struct_declarations :: proc() {
|
||||||
|
{
|
||||||
|
FooInteger :: int;
|
||||||
|
Foo :: struct {
|
||||||
|
i: FooInteger;
|
||||||
|
};
|
||||||
|
f := Foo{FooInteger(137)};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Foo :: struct {
|
||||||
|
Integer :: int;
|
||||||
|
|
||||||
|
i: Integer;
|
||||||
|
}
|
||||||
|
f := Foo{Foo.Integer(137)};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
match 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;
|
||||||
|
|
||||||
|
match 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);
|
||||||
|
|
||||||
|
match 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);
|
||||||
|
|
||||||
|
match 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 :: struct(Key, Value: type) {
|
||||||
|
Slot :: struct {
|
||||||
|
occupied: bool;
|
||||||
|
hash: u32;
|
||||||
|
key: Key;
|
||||||
|
value: Value;
|
||||||
|
}
|
||||||
|
SIZE_MIN :: 32;
|
||||||
|
|
||||||
|
count: int;
|
||||||
|
allocator: Allocator;
|
||||||
|
slots: []Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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([]T.Slot, max(capacity, T.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), T.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() {
|
main :: proc() {
|
||||||
MAX :: 64
|
when true {
|
||||||
buf: [MAX]rune
|
fmt.println("\n# general_stuff"); general_stuff();
|
||||||
backing: [MAX]byte
|
fmt.println("\n# nested_struct_declarations"); nested_struct_declarations();
|
||||||
offset: int
|
fmt.println("\n# default_struct_values"); default_struct_values();
|
||||||
|
fmt.println("\n# union_type"); union_type();
|
||||||
msg := "Hello"
|
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
|
||||||
count := utf8.rune_count(msg)
|
fmt.println("\n# threading_example"); threading_example();
|
||||||
assert(count <= MAX)
|
|
||||||
runes := buf[:count]
|
|
||||||
|
|
||||||
offset = 0
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
s := msg[offset:]
|
|
||||||
r, len := utf8.decode_rune(s)
|
|
||||||
runes[count-i-1] = r
|
|
||||||
offset += len
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = 0
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
data, len := utf8.encode_rune(runes[i])
|
|
||||||
copy(backing[offset:], data[:len])
|
|
||||||
offset += len
|
|
||||||
}
|
|
||||||
|
|
||||||
reverse := backing[:offset] as string
|
|
||||||
fmt.println(reverse) // olleH
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
compile_assert(type_of(x) == int);
|
||||||
|
compile_assert(type_of(y) == f32);
|
||||||
|
compile_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);
|
||||||
|
*/
|
||||||
|
}
|
||||||
+130
-123
@@ -1,91 +1,91 @@
|
|||||||
#import "win32.odin"
|
import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
#import "fmt.odin"
|
import wgl "sys/wgl.odin" when ODIN_OS == "windows";
|
||||||
#import "math.odin"
|
import "fmt.odin";
|
||||||
#import "os.odin"
|
import "math.odin";
|
||||||
#import "opengl.odin" as gl
|
import "os.odin";
|
||||||
|
import gl "opengl.odin";
|
||||||
|
|
||||||
TWO_HEARTS :: '💕'
|
const TWO_HEARTS = '💕';
|
||||||
|
|
||||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency()
|
var win32_perf_count_freq = win32.get_query_performance_frequency();
|
||||||
time_now :: proc() -> f64 {
|
proc time_now() -> f64 {
|
||||||
assert(win32_perf_count_freq != 0)
|
assert(win32_perf_count_freq != 0);
|
||||||
|
|
||||||
counter: i64
|
var counter: i64;
|
||||||
win32.QueryPerformanceCounter(^counter)
|
win32.query_performance_counter(&counter);
|
||||||
result := counter as f64 / win32_perf_count_freq as f64
|
return f64(counter) / f64(win32_perf_count_freq);
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
win32_print_last_error :: proc() {
|
proc win32_print_last_error() {
|
||||||
err_code := win32.GetLastError() as int
|
var err_code = win32.get_last_error();
|
||||||
if err_code != 0 {
|
if err_code != 0 {
|
||||||
fmt.println("GetLastError: %", err_code)
|
fmt.println("get_last_error: ", err_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yuk!
|
// Yuk!
|
||||||
to_c_string :: proc(s: string) -> []u8 {
|
proc to_c_string(s: string) -> []u8 {
|
||||||
c_str := new_slice(u8, s.count+1)
|
var c_str = make([]u8, len(s)+1);
|
||||||
copy(c_str, s as []byte)
|
copy(c_str, []u8(s));
|
||||||
c_str[s.count] = 0
|
c_str[len(s)] = 0;
|
||||||
return c_str
|
return c_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Window :: struct {
|
type Window struct {
|
||||||
width, height: int
|
width, height: int,
|
||||||
wc: win32.WNDCLASSEXA
|
wc: win32.WndClassExA,
|
||||||
dc: win32.HDC
|
dc: win32.Hdc,
|
||||||
hwnd: win32.HWND
|
hwnd: win32.Hwnd,
|
||||||
opengl_context, rc: win32.HGLRC
|
opengl_context, rc: wgl.Hglrc,
|
||||||
c_title: []u8
|
c_title: []u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
|
proc make_window(title: string, msg, height: int, window_proc: win32.WndProc) -> (Window, bool) {
|
||||||
using win32
|
using win32;
|
||||||
|
|
||||||
w: Window
|
var w: Window;
|
||||||
w.width, w.height = msg, height
|
w.width, w.height = msg, height;
|
||||||
|
|
||||||
class_name := "Win32-Odin-Window\x00"
|
var class_name = "Win32-Odin-Window\x00";
|
||||||
c_class_name := class_name.data
|
var c_class_name = &class_name[0];
|
||||||
if title[title.count-1] != 0 {
|
if title[len(title)-1] != 0 {
|
||||||
w.c_title = to_c_string(title)
|
w.c_title = to_c_string(title);
|
||||||
} else {
|
} else {
|
||||||
w.c_title = title as []u8
|
w.c_title = []u8(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
instance := GetModuleHandleA(nil)
|
var instance = get_module_handle_a(nil);
|
||||||
|
|
||||||
w.wc = WNDCLASSEXA{
|
w.wc = WndClassExA{
|
||||||
size = size_of(WNDCLASSEXA) as u32,
|
size = size_of(WndClassExA),
|
||||||
style = CS_VREDRAW | CS_HREDRAW,
|
style = CS_VREDRAW | CS_HREDRAW,
|
||||||
instance = instance as HINSTANCE,
|
instance = Hinstance(instance),
|
||||||
class_name = c_class_name,
|
class_name = c_class_name,
|
||||||
wnd_proc = window_proc,
|
wnd_proc = window_proc,
|
||||||
};
|
};
|
||||||
|
|
||||||
if RegisterClassExA(^w.wc) == 0 {
|
if register_class_ex_a(&w.wc) == 0 {
|
||||||
win32_print_last_error()
|
win32_print_last_error();
|
||||||
return w, false
|
return w, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
w.hwnd = CreateWindowExA(0,
|
w.hwnd = create_window_ex_a(0,
|
||||||
c_class_name, w.c_title.data,
|
c_class_name, &w.c_title[0],
|
||||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
w.width as i32, w.height as i32,
|
i32(w.width), i32(w.height),
|
||||||
nil, nil, instance, nil)
|
nil, nil, instance, nil);
|
||||||
|
|
||||||
if w.hwnd == nil {
|
if w.hwnd == nil {
|
||||||
win32_print_last_error()
|
win32_print_last_error();
|
||||||
return w, false
|
return w, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
w.dc = GetDC(w.hwnd)
|
w.dc = get_dc(w.hwnd);
|
||||||
|
|
||||||
{
|
{
|
||||||
pfd := PIXELFORMATDESCRIPTOR{
|
var pfd = PixelFormatDescriptor{
|
||||||
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
|
size = size_of(PixelFormatDescriptor),
|
||||||
version = 1,
|
version = 1,
|
||||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||||
pixel_type = PFD_TYPE_RGBA,
|
pixel_type = PFD_TYPE_RGBA,
|
||||||
@@ -94,122 +94,129 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
|||||||
depth_bits = 24,
|
depth_bits = 24,
|
||||||
stencil_bits = 8,
|
stencil_bits = 8,
|
||||||
layer_type = PFD_MAIN_PLANE,
|
layer_type = PFD_MAIN_PLANE,
|
||||||
}
|
};
|
||||||
|
|
||||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil)
|
set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
|
||||||
w.opengl_context = wglCreateContext(w.dc)
|
w.opengl_context = wgl.create_context(w.dc);
|
||||||
wglMakeCurrent(w.dc, w.opengl_context)
|
wgl.make_current(w.dc, w.opengl_context);
|
||||||
|
|
||||||
attribs := [8]i32{
|
var attribs = [8]i32{
|
||||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
|
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||||
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
||||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||||
}
|
};
|
||||||
|
|
||||||
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType
|
var wgl_str = "wglCreateContextAttribsARB\x00";
|
||||||
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0])
|
var wglCreateContextAttribsARB = wgl.CreateContextAttribsARBType(wgl.get_proc_address(&wgl_str[0]));
|
||||||
wglMakeCurrent(w.dc, w.rc)
|
w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
|
||||||
SwapBuffers(w.dc)
|
wgl.make_current(w.dc, w.rc);
|
||||||
|
swap_buffers(w.dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return w, true
|
return w, true;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_window :: proc(w: ^Window) {
|
proc destroy_window(w: ^Window) {
|
||||||
free(w.c_title.data)
|
free(w.c_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
display_window :: proc(w: ^Window) {
|
proc display_window(w: ^Window) {
|
||||||
win32.SwapBuffers(w.dc)
|
win32.swap_buffers(w.dc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
run :: proc() {
|
proc run() {
|
||||||
using win32
|
using math;
|
||||||
using math
|
|
||||||
|
|
||||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
|
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
||||||
|
using win32;
|
||||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||||
os.exit(0)
|
os.exit(0);
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
return DefWindowProcA(hwnd, msg, wparam, lparam)
|
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc)
|
var window, window_success = make_window("Odin Language Demo", 854, 480, win32.WndProc(win32_proc));
|
||||||
if !window_success {
|
if !window_success {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
defer destroy_window(^window)
|
defer destroy_window(&window);
|
||||||
|
|
||||||
gl.init()
|
gl.init();
|
||||||
|
|
||||||
|
using win32;
|
||||||
|
|
||||||
prev_time := time_now()
|
var prev_time = time_now();
|
||||||
running := true
|
var running = true;
|
||||||
|
|
||||||
pos := Vec2{100, 100}
|
var pos = Vec2{100, 100};
|
||||||
|
|
||||||
for running {
|
for running {
|
||||||
curr_time := time_now()
|
var curr_time = time_now();
|
||||||
dt := (curr_time - prev_time) as f32
|
var dt = f32(curr_time - prev_time);
|
||||||
prev_time = curr_time
|
prev_time = curr_time;
|
||||||
|
|
||||||
msg: MSG
|
var msg: Msg;
|
||||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||||
if msg.message == WM_QUIT {
|
if msg.message == WM_QUIT {
|
||||||
running = false
|
running = false;
|
||||||
}
|
}
|
||||||
TranslateMessage(^msg)
|
translate_message(&msg);
|
||||||
DispatchMessageA(^msg)
|
dispatch_message_a(&msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_key_down(Key_Code.ESCAPE) {
|
if is_key_down(KeyCode.Escape) {
|
||||||
running = false
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
SPEED :: 500
|
const SPEED = 500;
|
||||||
v: Vec2
|
var v: Vec2;
|
||||||
|
|
||||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1 }
|
if is_key_down(KeyCode.Right) { v[0] += 1; }
|
||||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1 }
|
if is_key_down(KeyCode.Left) { v[0] -= 1; }
|
||||||
if is_key_down(Key_Code.UP) { v[1] += 1 }
|
if is_key_down(KeyCode.Up) { v[1] += 1; }
|
||||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1 }
|
if is_key_down(KeyCode.Down) { v[1] -= 1; }
|
||||||
|
|
||||||
v = vec2_norm0(v)
|
v = norm(v);
|
||||||
|
|
||||||
pos += v * Vec2{SPEED * dt}
|
pos += v * Vec2{SPEED * dt};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0)
|
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
||||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
gl.LoadIdentity()
|
gl.LoadIdentity();
|
||||||
gl.Ortho(0, window.width as f64,
|
gl.Ortho(0, f64(window.width),
|
||||||
0, window.height as f64, 0, 1)
|
0, f64(window.height), 0, 1);
|
||||||
|
|
||||||
draw_rect :: proc(x, y, w, h: f32) {
|
proc draw_rect(x, y, w, h: f32) {
|
||||||
gl.Begin(gl.TRIANGLES)
|
gl.Begin(gl.TRIANGLES);
|
||||||
defer gl.End()
|
defer gl.End();
|
||||||
|
|
||||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0)
|
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, 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(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, 1, 0); gl.Vertex3f(x, y+h, 0);
|
||||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0)
|
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_rect(pos.x, pos.y, 50, 50)
|
draw_rect(pos.x, pos.y, 50, 50);
|
||||||
|
|
||||||
display_window(^window)
|
display_window(&window);
|
||||||
ms_to_sleep := (16 - 1000*dt) as i32
|
var ms_to_sleep = i32(16 - 1000*dt);
|
||||||
if ms_to_sleep > 0 {
|
if ms_to_sleep > 0 {
|
||||||
win32.Sleep(ms_to_sleep)
|
win32.sleep(ms_to_sleep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
proc main() {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|||||||
+103
-99
@@ -1,12 +1,12 @@
|
|||||||
#import "fmt.odin" as fmt
|
import "fmt.odin";
|
||||||
|
|
||||||
#foreign_system_library "Ws2_32"
|
foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
||||||
|
|
||||||
|
|
||||||
SOCKET :: type uint
|
type SOCKET uint;
|
||||||
INVALID_SOCKET :: ~(0 as SOCKET)
|
const INVALID_SOCKET = ~SOCKET(0);
|
||||||
|
|
||||||
AF :: enum i32 {
|
type AF enum i32 {
|
||||||
UNSPEC = 0, // unspecified
|
UNSPEC = 0, // unspecified
|
||||||
UNIX = 1, // local to host (pipes, portals)
|
UNIX = 1, // local to host (pipes, portals)
|
||||||
INET = 2, // internetwork: UDP, TCP, etc.
|
INET = 2, // internetwork: UDP, TCP, etc.
|
||||||
@@ -35,113 +35,117 @@ AF :: enum i32 {
|
|||||||
SIP = 24, // Simple Internet Protocol
|
SIP = 24, // Simple Internet Protocol
|
||||||
PIP = 25, // Help Identify PIP packets
|
PIP = 25, // Help Identify PIP packets
|
||||||
MAX = 26,
|
MAX = 26,
|
||||||
}
|
};
|
||||||
|
|
||||||
SOCK_STREAM :: 1
|
const (
|
||||||
SOCKET_ERROR :: -1
|
SOCK_STREAM = 1;
|
||||||
IPPROTO_TCP :: 6
|
SOCKET_ERROR = -1;
|
||||||
AI_PASSIVE :: 0x0020
|
IPPROTO_TCP = 6;
|
||||||
SOMAXCONN :: 128
|
AI_PASSIVE = 0x0020;
|
||||||
|
SOMAXCONN = 128;
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
SD_RECEIVE = 0;
|
||||||
|
SD_SEND = 1;
|
||||||
|
SD_BOTH = 2;
|
||||||
|
)
|
||||||
|
|
||||||
SD_RECEIVE :: 0
|
const WSADESCRIPTION_LEN = 256;
|
||||||
SD_SEND :: 1
|
const WSASYS_STATUS_LEN = 128;
|
||||||
SD_BOTH :: 2
|
type WSADATA struct #ordered {
|
||||||
|
version: i16,
|
||||||
WSADESCRIPTION_LEN :: 256
|
high_version: i16,
|
||||||
WSASYS_STATUS_LEN :: 128
|
|
||||||
WSADATA :: struct #ordered {
|
|
||||||
version: i16
|
|
||||||
high_version: i16
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): This is x64 ordering
|
// NOTE(bill): This is x64 ordering
|
||||||
max_sockets: u16
|
max_sockets: u16,
|
||||||
max_udp_dg: u16
|
max_udp_dg: u16,
|
||||||
vendor_info: ^byte
|
vendor_info: ^u8,
|
||||||
description: [WSADESCRIPTION_LEN+1]byte
|
description: [WSADESCRIPTION_LEN+1]u8,
|
||||||
system_status: [WSASYS_STATUS_LEN+1]byte
|
system_status: [WSASYS_STATUS_LEN+1]u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
addrinfo :: struct #ordered {
|
type addrinfo struct #ordered {
|
||||||
flags: i32
|
flags: i32,
|
||||||
family: i32
|
family: i32,
|
||||||
socktype: i32
|
socktype: i32,
|
||||||
protocol: i32
|
protocol: i32,
|
||||||
addrlen: uint
|
addrlen: uint,
|
||||||
canonname: ^u8
|
canonname: ^u8,
|
||||||
addr: ^sockaddr
|
addr: ^sockaddr,
|
||||||
next: ^addrinfo
|
next: ^addrinfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
sockaddr :: struct #ordered {
|
type sockaddr struct #ordered {
|
||||||
family: u16
|
family: u16,
|
||||||
data: [14]byte
|
data: [14]u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreign ws2 {
|
||||||
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign #dll_import
|
proc WSAStartup (version_requested: i16, data: ^WSADATA) -> i32;
|
||||||
WSACleanup :: proc() -> i32 #foreign #dll_import
|
proc WSACleanup () -> i32;
|
||||||
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign #dll_import
|
proc getaddrinfo (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32;
|
||||||
freeaddrinfo :: proc(ai: ^addrinfo) #foreign #dll_import
|
proc freeaddrinfo (ai: ^addrinfo);
|
||||||
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign #dll_import
|
proc socket (af, type_, protocol: i32) -> SOCKET;
|
||||||
closesocket :: proc(s: SOCKET) -> i32 #foreign #dll_import
|
proc closesocket (s: SOCKET) -> i32;
|
||||||
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign #dll_import
|
proc bind (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32;
|
||||||
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign #dll_import
|
proc listen (s: SOCKET, back_log: i32) -> i32;
|
||||||
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign #dll_import
|
proc accept (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET;
|
||||||
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
|
proc recv (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
||||||
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
|
proc send (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
||||||
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign #dll_import
|
proc shutdown (s: SOCKET, how: i32) -> i32;
|
||||||
WSAGetLastError :: proc() -> i32 #foreign #dll_import
|
proc WSAGetLastError() -> i32;
|
||||||
|
}
|
||||||
to_c_string :: proc(s: string) -> ^byte {
|
proc to_c_string(s: string) -> ^u8 {
|
||||||
c_str := new_slice(byte, s.count+1)
|
var c_str = make([]u8, len(s)+1);
|
||||||
assert(c_str.data != null)
|
copy(c_str, []u8(s));
|
||||||
copy(c_str, s as []byte)
|
c_str[len(s)] = 0;
|
||||||
c_str[s.count] = 0
|
return &c_str[0];
|
||||||
return c_str.data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run :: proc() {
|
proc run() {
|
||||||
wsa: WSADATA
|
var (
|
||||||
res: ^addrinfo = null
|
wsa: WSADATA;
|
||||||
hints: addrinfo
|
res: ^addrinfo = nil;
|
||||||
s, client: SOCKET
|
hints: addrinfo;
|
||||||
|
s, client: SOCKET;
|
||||||
|
)
|
||||||
|
|
||||||
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
|
if WSAStartup(2 | (2 << 8), &wsa) != 0 {
|
||||||
fmt.println("WSAStartup failed: ", WSAGetLastError())
|
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
defer WSACleanup()
|
defer WSACleanup();
|
||||||
|
|
||||||
hints.family = AF.INET as i32
|
hints.family = i32(AF.INET);
|
||||||
hints.socktype = SOCK_STREAM
|
hints.socktype = SOCK_STREAM;
|
||||||
hints.protocol = IPPROTO_TCP
|
hints.protocol = IPPROTO_TCP;
|
||||||
hints.flags = AI_PASSIVE
|
hints.flags = AI_PASSIVE;
|
||||||
|
|
||||||
if getaddrinfo(null, to_c_string("8080"), ^hints, ^res) != 0 {
|
if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 {
|
||||||
fmt.println("getaddrinfo failed: ", WSAGetLastError())
|
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
defer freeaddrinfo(res)
|
defer freeaddrinfo(res);
|
||||||
|
|
||||||
s = socket(res.family, res.socktype, res.protocol)
|
s = socket(res.family, res.socktype, res.protocol);
|
||||||
if s == INVALID_SOCKET {
|
if s == INVALID_SOCKET {
|
||||||
fmt.println("socket failed: ", WSAGetLastError())
|
fmt.println("socket failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
defer closesocket(s)
|
defer closesocket(s);
|
||||||
|
|
||||||
bind(s, res.addr, res.addrlen as i32)
|
bind(s, res.addr, i32(res.addrlen));
|
||||||
listen(s, SOMAXCONN)
|
listen(s, SOMAXCONN);
|
||||||
|
|
||||||
client = accept(s, null, null)
|
client = accept(s, nil, 0);
|
||||||
if client == INVALID_SOCKET {
|
if client == INVALID_SOCKET {
|
||||||
fmt.println("socket failed: ", WSAGetLastError())
|
fmt.println("socket failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
defer closesocket(client)
|
defer closesocket(client);
|
||||||
|
|
||||||
html :=
|
var html =
|
||||||
`HTTP/1.1 200 OK
|
`HTTP/1.1 200 OK
|
||||||
Connection: close
|
Connection: close
|
||||||
Content-type: text/html
|
Content-type: text/html
|
||||||
@@ -154,27 +158,27 @@ Content-type: text/html
|
|||||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
<h1 style="color: orange;">Odin Server Demo</h1>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`
|
`;
|
||||||
|
|
||||||
buf: [1024]byte
|
var buf: [1024]u8;
|
||||||
for {
|
for {
|
||||||
bytes := recv(client, ^buf[0], buf.count as i32, 0)
|
var bytes = recv(client, &buf[0], i32(len(buf)), 0);
|
||||||
if bytes > 0 {
|
if bytes > 0 {
|
||||||
// fmt.println(buf[:bytes] as string)
|
// fmt.println(string(buf[0..<bytes]))
|
||||||
bytes_sent := send(client, html.data, (html.count-1) as i32, 0)
|
var bytes_sent = send(client, &html[0], i32(len(html)-1), 0);
|
||||||
if bytes_sent == SOCKET_ERROR {
|
if bytes_sent == SOCKET_ERROR {
|
||||||
fmt.println("send failed: ", WSAGetLastError())
|
fmt.println("send failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
} else if bytes == 0 {
|
} else if bytes == 0 {
|
||||||
fmt.println("Connection closing...")
|
fmt.println("Connection closing...");
|
||||||
break
|
break;
|
||||||
} else {
|
} else {
|
||||||
fmt.println("recv failed: ", WSAGetLastError())
|
fmt.println("recv failed: ", WSAGetLastError());
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shutdown(client, SD_SEND)
|
shutdown(client, SD_SEND);
|
||||||
}
|
}
|
||||||
|
|||||||
+105
-102
@@ -1,83 +1,86 @@
|
|||||||
#import "fmt.odin"
|
#import "fmt.odin";
|
||||||
#import "os.odin"
|
#import "os.odin";
|
||||||
#import "mem.odin"
|
#import "mem.odin";
|
||||||
// #import "http_test.odin" as ht
|
// #import "http_test.odin" as ht;
|
||||||
// #import "game.odin" as game
|
// #import "game.odin" as game;
|
||||||
// #import "punity.odin" as pn
|
// #import "punity.odin" as pn;
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
// struct_padding()
|
struct_padding();
|
||||||
// bounds_checking()
|
bounds_checking();
|
||||||
// type_introspection()
|
type_introspection();
|
||||||
// any_type()
|
any_type();
|
||||||
// crazy_introspection()
|
crazy_introspection();
|
||||||
// namespaces_and_files()
|
namespaces_and_files();
|
||||||
// miscellany()
|
miscellany();
|
||||||
// ht.run()
|
|
||||||
// game.run()
|
|
||||||
// {
|
|
||||||
// init :: proc(c: ^pn.Core) {}
|
|
||||||
// step :: proc(c: ^pn.Core) {}
|
|
||||||
|
|
||||||
// pn.run(init, step)
|
/*
|
||||||
// }
|
ht.run();
|
||||||
|
game.run();
|
||||||
|
{
|
||||||
|
init :: proc(c: ^pn.Core) {}
|
||||||
|
step :: proc(c: ^pn.Core) {}
|
||||||
|
|
||||||
|
pn.run(init, step);
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
struct_padding :: proc() {
|
struct_padding :: proc() {
|
||||||
{
|
{
|
||||||
A :: struct {
|
A :: struct {
|
||||||
a: u8
|
a: u8,
|
||||||
b: u32
|
b: u32,
|
||||||
c: u16
|
c: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
B :: struct {
|
B :: struct {
|
||||||
a: [7]u8
|
a: [7]u8,
|
||||||
b: [3]u16
|
b: [3]u16,
|
||||||
c: u8
|
c: u8,
|
||||||
d: u16
|
d: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A))
|
fmt.println("size_of(A):", size_of(A));
|
||||||
fmt.println("size_of(B):", size_of(B))
|
fmt.println("size_of(B):", size_of(B));
|
||||||
|
|
||||||
// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
|
// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
A :: struct #ordered {
|
A :: struct #ordered {
|
||||||
a: u8
|
a: u8,
|
||||||
b: u32
|
b: u32,
|
||||||
c: u16
|
c: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
B :: struct #ordered {
|
B :: struct #ordered {
|
||||||
a: [7]u8
|
a: [7]u8,
|
||||||
b: [3]u16
|
b: [3]u16,
|
||||||
c: u8
|
c: u8,
|
||||||
d: u16
|
d: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A))
|
fmt.println("size_of(A):", size_of(A));
|
||||||
fmt.println("size_of(B):", size_of(B))
|
fmt.println("size_of(B):", size_of(B));
|
||||||
|
|
||||||
// C-style structure layout
|
// C-style structure layout
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
A :: struct #packed {
|
A :: struct #packed {
|
||||||
a: u8
|
a: u8,
|
||||||
b: u32
|
b: u32,
|
||||||
c: u16
|
c: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
B :: struct #packed {
|
B :: struct #packed {
|
||||||
a: [7]u8
|
a: [7]u8,
|
||||||
b: [3]u16
|
b: [3]u16,
|
||||||
c: u8
|
c: u8,
|
||||||
d: u16
|
d: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.println("size_of(A):", size_of(A))
|
fmt.println("size_of(A):", size_of(A));
|
||||||
fmt.println("size_of(B):", size_of(B))
|
fmt.println("size_of(B):", size_of(B));
|
||||||
|
|
||||||
// Useful for explicit layout
|
// Useful for explicit layout
|
||||||
}
|
}
|
||||||
@@ -119,7 +122,7 @@ struct_padding :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bounds_checking :: proc() {
|
bounds_checking :: proc() {
|
||||||
x: [4]int
|
x: [4]int;
|
||||||
// x[-1] = 0; // Compile Time
|
// x[-1] = 0; // Compile Time
|
||||||
// x[4] = 0; // Compile Time
|
// x[4] = 0; // Compile Time
|
||||||
|
|
||||||
@@ -132,9 +135,9 @@ bounds_checking :: proc() {
|
|||||||
// Works for arrays, strings, slices, and related procedures & operations
|
// Works for arrays, strings, slices, and related procedures & operations
|
||||||
|
|
||||||
{
|
{
|
||||||
base: [10]int
|
base: [10]int;
|
||||||
s := base[2:6]
|
s := base[2..6];
|
||||||
a, b := -1, 6
|
a, b := -1, 6;
|
||||||
|
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
s[a] = 0;
|
s[a] = 0;
|
||||||
@@ -154,69 +157,69 @@ bounds_checking :: proc() {
|
|||||||
|
|
||||||
type_introspection :: proc() {
|
type_introspection :: proc() {
|
||||||
{
|
{
|
||||||
info: ^Type_Info
|
info: ^Type_Info;
|
||||||
x: int
|
x: int;
|
||||||
|
|
||||||
info = type_info(int) // by type
|
info = type_info(int); // by type
|
||||||
info = type_info_of_val(x) // by value
|
info = type_info_of_val(x); // by value
|
||||||
// See: runtime.odin
|
// See: runtime.odin
|
||||||
|
|
||||||
match type i : info {
|
match i in info {
|
||||||
case Type_Info.Integer:
|
case Type_Info.Integer:
|
||||||
fmt.println("integer!")
|
fmt.println("integer!");
|
||||||
case Type_Info.Float:
|
case Type_Info.Float:
|
||||||
fmt.println("float!")
|
fmt.println("float!");
|
||||||
default:
|
default:
|
||||||
fmt.println("potato!")
|
fmt.println("potato!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsafe cast
|
// Unsafe cast
|
||||||
integer_info := info as ^Type_Info.Integer
|
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Vector2 :: struct { x, y: f32 }
|
Vector2 :: struct { x, y: f32 }
|
||||||
Vector3 :: struct { x, y, z: f32 }
|
Vector3 :: struct { x, y, z: f32 }
|
||||||
|
|
||||||
v1: Vector2
|
v1: Vector2;
|
||||||
v2: Vector3
|
v2: Vector3;
|
||||||
v3: Vector3
|
v3: Vector3;
|
||||||
|
|
||||||
t1 := type_info_of_val(v1)
|
t1 := type_info_of_val(v1);
|
||||||
t2 := type_info_of_val(v2)
|
t2 := type_info_of_val(v2);
|
||||||
t3 := type_info_of_val(v3)
|
t3 := type_info_of_val(v3);
|
||||||
|
|
||||||
fmt.println()
|
fmt.println();
|
||||||
fmt.print("Type of v1 is:\n\t", t1)
|
fmt.print("Type of v1 is:\n\t", t1);
|
||||||
|
|
||||||
fmt.println()
|
fmt.println();
|
||||||
fmt.print("Type of v2 is:\n\t", t2)
|
fmt.print("Type of v2 is:\n\t", t2);
|
||||||
|
|
||||||
fmt.println("\n")
|
fmt.println("\n");
|
||||||
fmt.println("t1 == t2:", t1 == t2)
|
fmt.println("t1 == t2:", t1 == t2);
|
||||||
fmt.println("t2 == t3:", t2 == t3)
|
fmt.println("t2 == t3:", t2 == t3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
any_type :: proc() {
|
any_type :: proc() {
|
||||||
a: any
|
a: any;
|
||||||
|
|
||||||
x: int = 123
|
x: int = 123;
|
||||||
y: f64 = 6.28
|
y: f64 = 6.28;
|
||||||
z: string = "Yo-Yo Ma"
|
z: string = "Yo-Yo Ma";
|
||||||
// All types can be implicit cast to `any`
|
// All types can be implicit cast to `any`
|
||||||
a = x
|
a = x;
|
||||||
a = y
|
a = y;
|
||||||
a = z
|
a = z;
|
||||||
a = a // This the "identity" type, it doesn't get converted
|
a = a; // This the "identity" type, it doesn't get converted
|
||||||
|
|
||||||
a = 123 // Literals are copied onto the stack first
|
a = 123; // Literals are copied onto the stack first
|
||||||
|
|
||||||
// any has two members
|
// any has two members
|
||||||
// data - rawptr to the data
|
// data - rawptr to the data
|
||||||
// type_info - pointer to the type info
|
// type_info - pointer to the type info
|
||||||
|
|
||||||
fmt.println(x, y, z)
|
fmt.println(x, y, z);
|
||||||
// See: fmt.odin
|
// See: fmt.odin
|
||||||
// For variadic any procedures in action
|
// For variadic any procedures in action
|
||||||
}
|
}
|
||||||
@@ -232,15 +235,15 @@ crazy_introspection :: proc() {
|
|||||||
TOMATO,
|
TOMATO,
|
||||||
}
|
}
|
||||||
|
|
||||||
s: string
|
s: string;
|
||||||
s = enum_to_string(Fruit.PEACH)
|
// s = enum_to_string(Fruit.PEACH);
|
||||||
fmt.println(s)
|
fmt.println(s);
|
||||||
|
|
||||||
f := Fruit.GRAPE
|
f := Fruit.GRAPE;
|
||||||
s = enum_to_string(f)
|
// s = enum_to_string(f);
|
||||||
fmt.println(s)
|
fmt.println(s);
|
||||||
|
|
||||||
fmt.println(f)
|
fmt.println(f);
|
||||||
// See: runtime.odin
|
// See: runtime.odin
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,15 +262,15 @@ crazy_introspection :: proc() {
|
|||||||
TOMATO,
|
TOMATO,
|
||||||
}
|
}
|
||||||
|
|
||||||
fruit_ti := type_info(Fruit)
|
fruit_ti := type_info(Fruit);
|
||||||
name := (fruit_ti as ^Type_Info.Named).name // Unsafe casts
|
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
|
||||||
info := type_info_base(fruit_ti) as ^Type_Info.Enum // Unsafe casts
|
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
|
||||||
|
|
||||||
fmt.printf("% :: enum % {\n", name, info.base);
|
fmt.printf("%s :: enum %T {\n", name, info.base);
|
||||||
for i := 0; i < info.values.count; i++ {
|
for i := 0; i < len(info.values); i++ {
|
||||||
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i])
|
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
|
||||||
}
|
}
|
||||||
fmt.printf("}\n")
|
fmt.printf("}\n");
|
||||||
|
|
||||||
// NOTE(bill): look at that type-safe printf!
|
// NOTE(bill): look at that type-safe printf!
|
||||||
}
|
}
|
||||||
@@ -275,10 +278,10 @@ crazy_introspection :: proc() {
|
|||||||
{
|
{
|
||||||
Vector3 :: struct {x, y, z: f32}
|
Vector3 :: struct {x, y, z: f32}
|
||||||
|
|
||||||
a := Vector3{x = 1, y = 4, z = 9}
|
a := Vector3{x = 1, y = 4, z = 9};
|
||||||
fmt.println(a)
|
fmt.println(a);
|
||||||
b := Vector3{x = 9, y = 3, z = 1}
|
b := Vector3{x = 9, y = 3, z = 1};
|
||||||
fmt.println(b)
|
fmt.println(b);
|
||||||
|
|
||||||
// NOTE(bill): See fmt.odin
|
// NOTE(bill): See fmt.odin
|
||||||
}
|
}
|
||||||
@@ -301,7 +304,7 @@ namespaces_and_files :: proc() {
|
|||||||
#import "file.odin" as _
|
#import "file.odin" as _
|
||||||
|
|
||||||
// Exporting import
|
// Exporting import
|
||||||
#load "file.odin"
|
#include "file.odin"
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Talk about scope rules and diagram
|
// Talk about scope rules and diagram
|
||||||
|
|||||||
+360
-368
File diff suppressed because it is too large
Load Diff
+17
-17
@@ -1,14 +1,14 @@
|
|||||||
#import "fmt.odin"
|
#import "fmt.odin";
|
||||||
#import "utf8.odin"
|
#import "utf8.odin";
|
||||||
#import "hash.odin"
|
#import "hash.odin";
|
||||||
#import "mem.odin"
|
#import "mem.odin";
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
{ // New Standard Library stuff
|
{ // New Standard Library stuff
|
||||||
s := "Hello"
|
s := "Hello";
|
||||||
fmt.println(s,
|
fmt.println(s,
|
||||||
utf8.valid_string(s),
|
utf8.valid_string(s),
|
||||||
hash.murmur64(s.data, s.count))
|
hash.murmur64(cast([]byte)s));
|
||||||
|
|
||||||
// utf8.odin
|
// utf8.odin
|
||||||
// hash.odin
|
// hash.odin
|
||||||
@@ -19,15 +19,15 @@ main :: proc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
arena: mem.Arena
|
arena: mem.Arena;
|
||||||
mem.init_arena_from_context(^arena, mem.megabytes(16)) // Uses default allocator
|
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
|
||||||
defer mem.free_arena(^arena)
|
defer mem.free_arena(^arena);
|
||||||
|
|
||||||
push_allocator mem.arena_allocator(^arena) {
|
push_allocator mem.arena_allocator(^arena) {
|
||||||
x := new(int)
|
x := new(int);
|
||||||
x^ = 1337
|
x^ = 1337;
|
||||||
|
|
||||||
fmt.println(x^)
|
fmt.println(x^);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -48,14 +48,14 @@ main :: proc() {
|
|||||||
|
|
||||||
// You can also "push" a context
|
// You can also "push" a context
|
||||||
|
|
||||||
c := current_context() // Create copy of the allocator
|
c := context; // Create copy of the allocator
|
||||||
c.allocator = mem.arena_allocator(^arena)
|
c.allocator = mem.arena_allocator(^arena);
|
||||||
|
|
||||||
push_context c {
|
push_context c {
|
||||||
x := new(int)
|
x := new(int);
|
||||||
x^ = 365
|
x^ = 365;
|
||||||
|
|
||||||
fmt.println(x^)
|
fmt.println(x^);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,284 @@
|
|||||||
|
#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";
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
// syntax();
|
||||||
|
procedure_overloading();
|
||||||
|
}
|
||||||
|
|
||||||
|
syntax :: proc() {
|
||||||
|
// Cyclic type checking
|
||||||
|
// Uncomment to see the error
|
||||||
|
// A :: struct {b: B};
|
||||||
|
// B :: struct {a: A};
|
||||||
|
|
||||||
|
x: int;
|
||||||
|
y := cast(f32)x;
|
||||||
|
z := transmute(u32)y;
|
||||||
|
// down_cast, union_cast are similar too
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Basic directives
|
||||||
|
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
|
||||||
|
// NOTE: new and improved `printf`
|
||||||
|
// TODO: It does need accurate float printing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// record fields use the same syntax a procedure signatures
|
||||||
|
Thing1 :: struct {
|
||||||
|
x: f32,
|
||||||
|
y: int,
|
||||||
|
z: ^[]int,
|
||||||
|
};
|
||||||
|
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
||||||
|
|
||||||
|
// Slice interals are now just a `ptr+len+cap`
|
||||||
|
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
|
||||||
|
|
||||||
|
// Helper type - Help the reader understand what it is quicker
|
||||||
|
My_Int :: #type int;
|
||||||
|
My_Proc :: #type proc(int) -> f32;
|
||||||
|
|
||||||
|
|
||||||
|
// All declarations with : are either variable or constant
|
||||||
|
// To make these declarations syntactically consistent
|
||||||
|
v_variable := 123;
|
||||||
|
c_constant :: 123;
|
||||||
|
c_type1 :: int;
|
||||||
|
c_type2 :: []int;
|
||||||
|
c_proc :: proc() { /* code here */ };
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
x += 1;
|
||||||
|
x -= 1;
|
||||||
|
// ++ and -- have been removed
|
||||||
|
// x++;
|
||||||
|
// x--;
|
||||||
|
// Question: Should they be added again?
|
||||||
|
// They were removed as they are redundant and statements, not expressions
|
||||||
|
// like in C/C++
|
||||||
|
*/
|
||||||
|
|
||||||
|
// You can now build files as a `.dll`
|
||||||
|
// `odin build_dll demo.odin`
|
||||||
|
|
||||||
|
|
||||||
|
// New vector syntax
|
||||||
|
u, v: [vector 3]f32;
|
||||||
|
v[0] = 123;
|
||||||
|
v.x = 123; // valid for all vectors with count 1 to 4
|
||||||
|
|
||||||
|
// Next part
|
||||||
|
prefixes();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
||||||
|
|
||||||
|
#thread_local my_tls: Prefix_Type;
|
||||||
|
|
||||||
|
prefixes :: proc() {
|
||||||
|
using var: Prefix_Type;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Same as C99's `restrict`
|
||||||
|
bar :: proc(no_alias a, b: ^int) {
|
||||||
|
// Assumes a never equals b so it can perform optimizations with that fact
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
when_statements();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
when_statements :: proc() {
|
||||||
|
X :: 123 + 12;
|
||||||
|
Y :: X/5;
|
||||||
|
COND :: Y > 0;
|
||||||
|
|
||||||
|
when COND {
|
||||||
|
fmt.println("Y > 0");
|
||||||
|
} else {
|
||||||
|
fmt.println("Y <= 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
when false {
|
||||||
|
this_code_does_not_exist(123, 321);
|
||||||
|
but_its_syntax_is_valid();
|
||||||
|
x :: ^^^^int;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign_procedures();
|
||||||
|
}
|
||||||
|
|
||||||
|
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
|
||||||
|
// 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";
|
||||||
|
// NOTE: If that library doesn't get used, it doesn't get linked with
|
||||||
|
// NOTE: There is not link checking yet to see if that procedure does come from that library
|
||||||
|
|
||||||
|
// See sys/windows.odin for more examples
|
||||||
|
|
||||||
|
special_expressions();
|
||||||
|
}
|
||||||
|
|
||||||
|
special_expressions :: proc() {
|
||||||
|
/*
|
||||||
|
// Block expression
|
||||||
|
x := {
|
||||||
|
a: f32 = 123;
|
||||||
|
b := a-123;
|
||||||
|
c := b/a;
|
||||||
|
give c;
|
||||||
|
}; // semicolon is required as it's an expression
|
||||||
|
|
||||||
|
y := if x < 50 {
|
||||||
|
give x;
|
||||||
|
} else {
|
||||||
|
// TODO: Type cohesion is not yet finished
|
||||||
|
give 123;
|
||||||
|
}; // semicolon is required as it's an expression
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is allows for inline blocks of code and will be a useful feature to have when
|
||||||
|
// macros will be implemented into the language
|
||||||
|
|
||||||
|
loops();
|
||||||
|
}
|
||||||
|
|
||||||
|
loops :: proc() {
|
||||||
|
// The C-style for loop
|
||||||
|
for i := 0; i < 123; i += 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for i := 0; i < 123; {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for false {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..123 { // 123 exclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..123-1 { // 122 inclusive
|
||||||
|
}
|
||||||
|
|
||||||
|
for val, idx in 12..16 {
|
||||||
|
fmt.println(val, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||||
|
|
||||||
|
for p in primes {
|
||||||
|
fmt.println(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pointers to arrays, slices, or strings are allowed
|
||||||
|
for _ in ^primes {
|
||||||
|
// ignore the value and just iterate across it
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
name := "你好,世界";
|
||||||
|
fmt.println(name);
|
||||||
|
for r in name {
|
||||||
|
compile_assert(type_of_val(r) == rune);
|
||||||
|
fmt.printf("%r\n", r);
|
||||||
|
}
|
||||||
|
|
||||||
|
when false {
|
||||||
|
for i, size := 0; i < name.count; i += size {
|
||||||
|
r: rune;
|
||||||
|
r, size = utf8.decode_rune(name[i..]);
|
||||||
|
fmt.printf("%r\n", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
procedure_overloading();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
procedure_overloading :: proc() {
|
||||||
|
THINGF :: 14451.1;
|
||||||
|
THINGI :: 14451;
|
||||||
|
|
||||||
|
foo :: proc() {
|
||||||
|
fmt.printf("Zero args\n");
|
||||||
|
}
|
||||||
|
foo :: proc(i: int) {
|
||||||
|
fmt.printf("int arg, i=%d\n", i);
|
||||||
|
}
|
||||||
|
foo :: proc(f: f64) {
|
||||||
|
i := cast(int)f;
|
||||||
|
fmt.printf("f64 arg, f=%d\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
foo();
|
||||||
|
foo(THINGF);
|
||||||
|
// foo(THINGI); // 14451 is just a number so it could go to either procedures
|
||||||
|
foo(cast(int)THINGI);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foo :: proc(x: ^i32) -> (int, int) {
|
||||||
|
fmt.println("^int");
|
||||||
|
return 123, cast(int)(x^);
|
||||||
|
}
|
||||||
|
foo :: proc(x: rawptr) {
|
||||||
|
fmt.println("rawptr");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a: i32 = 123;
|
||||||
|
b: f32;
|
||||||
|
c: rawptr;
|
||||||
|
fmt.println(foo(^a));
|
||||||
|
foo(^b);
|
||||||
|
foo(c);
|
||||||
|
// foo(nil); // nil could go to numerous types thus the ambiguity
|
||||||
|
|
||||||
|
f: proc();
|
||||||
|
f = foo; // The correct `foo` to chosen
|
||||||
|
f();
|
||||||
|
|
||||||
|
|
||||||
|
// See math.odin and atomic.odin for more examples
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#load "win32.odin"
|
#include "win32.odin"
|
||||||
|
|
||||||
assume :: proc(cond: bool) #foreign "llvm.assume"
|
assume :: proc(cond: bool) #foreign "llvm.assume"
|
||||||
|
|
||||||
|
|||||||
+315
-301
@@ -1,34 +1,42 @@
|
|||||||
#import "win32.odin"
|
import (
|
||||||
#import "fmt.odin"
|
win32 "sys/windows.odin";
|
||||||
#import "os.odin"
|
"fmt.odin";
|
||||||
|
"os.odin";
|
||||||
|
"mem.odin";
|
||||||
|
)
|
||||||
|
|
||||||
CANVAS_WIDTH :: 128
|
const (
|
||||||
CANVAS_HEIGHT :: 128
|
CANVAS_WIDTH = 128;
|
||||||
CANVAS_SCALE :: 3
|
CANVAS_HEIGHT = 128;
|
||||||
FRAME_TIME :: 1.0/30.0
|
CANVAS_SCALE = 3;
|
||||||
WINDOW_TITLE : string : "Punity\x00"
|
FRAME_TIME = 1.0/30.0;
|
||||||
|
WINDOW_TITLE = "Punity\x00";
|
||||||
|
)
|
||||||
|
|
||||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0)
|
const _ = compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||||
|
|
||||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE
|
const (
|
||||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE
|
WINDOW_WIDTH = CANVAS_WIDTH * CANVAS_SCALE;
|
||||||
|
WINDOW_HEIGHT = CANVAS_HEIGHT * CANVAS_SCALE;
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
STACK_CAPACITY = 1<<20;
|
||||||
|
STORAGE_CAPACITY = 1<<20;
|
||||||
|
|
||||||
STACK_CAPACITY :: 1<<20
|
DRAW_LIST_RESERVE = 128;
|
||||||
STORAGE_CAPACITY :: 1<<20
|
|
||||||
|
|
||||||
DRAW_LIST_RESERVE :: 128
|
MAX_KEYS = 256;
|
||||||
|
)
|
||||||
|
|
||||||
MAX_KEYS :: 256
|
type Core struct {
|
||||||
|
stack: ^Bank,
|
||||||
|
storage: ^Bank,
|
||||||
|
|
||||||
Core :: struct {
|
running: bool,
|
||||||
stack: ^Bank
|
key_modifiers: u32,
|
||||||
storage: ^Bank
|
key_states: [MAX_KEYS]u8,
|
||||||
|
key_deltas: [MAX_KEYS]u8,
|
||||||
running: bool
|
|
||||||
key_modifiers: u32
|
|
||||||
key_states: [MAX_KEYS]byte
|
|
||||||
key_deltas: [MAX_KEYS]byte
|
|
||||||
|
|
||||||
perf_frame,
|
perf_frame,
|
||||||
perf_frame_inner,
|
perf_frame_inner,
|
||||||
@@ -36,158 +44,154 @@ Core :: struct {
|
|||||||
perf_audio,
|
perf_audio,
|
||||||
perf_blit,
|
perf_blit,
|
||||||
perf_blit_cvt,
|
perf_blit_cvt,
|
||||||
perf_blit_gdi: Perf_Span
|
perf_blit_gdi: Perf_Span,
|
||||||
|
|
||||||
frame: i64
|
frame: i64,
|
||||||
|
|
||||||
canvas: Canvas
|
canvas: Canvas,
|
||||||
draw_list: ^Draw_List
|
draw_list: ^Draw_List,
|
||||||
}
|
}
|
||||||
|
|
||||||
Perf_Span :: struct {
|
type Perf_Span struct {
|
||||||
stamp: f64
|
stamp: f64,
|
||||||
delta: f32
|
delta: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Bank :: struct {
|
type Bank struct {
|
||||||
memory: []byte
|
memory: []u8,
|
||||||
cursor: int
|
cursor: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
Bank_State :: struct {
|
type Bank_State struct {
|
||||||
state: Bank
|
state: Bank,
|
||||||
bank: ^Bank
|
bank: ^Bank,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Color :: raw_union {
|
type Color raw_union {
|
||||||
using channels: struct{ a, b, g, r: byte }
|
using channels: struct{a, b, g, r: u8},
|
||||||
rgba: u32
|
rgba: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
Palette :: struct {
|
type Palette struct {
|
||||||
colors: [256]Color
|
colors: [256]Color,
|
||||||
colors_count: byte
|
colors_count: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Rect :: raw_union {
|
type Rect raw_union {
|
||||||
using minmax: struct {
|
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
||||||
min_x, min_y, max_x, max_y: int
|
using pos: struct {left, top, right, bottom: int},
|
||||||
}
|
e: [4]int,
|
||||||
using pos: struct {
|
|
||||||
left, top, right, bottom: int
|
|
||||||
}
|
|
||||||
e: [4]int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap :: struct {
|
type Bitmap struct {
|
||||||
pixels: []byte
|
pixels: []u8,
|
||||||
width: int
|
width: int,
|
||||||
height: int
|
height: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
Font :: struct {
|
type Font struct {
|
||||||
using bitmap: Bitmap
|
using bitmap: Bitmap,
|
||||||
char_width: int
|
char_width: int,
|
||||||
char_height: int
|
char_height: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas :: struct {
|
type Canvas struct {
|
||||||
using bitmap: ^Bitmap
|
using bitmap: ^Bitmap,
|
||||||
palette: Palette
|
palette: Palette,
|
||||||
translate_x: int
|
translate_x: int,
|
||||||
translate_y: int
|
translate_y: int,
|
||||||
clip: Rect
|
clip: Rect,
|
||||||
font: ^Font
|
font: ^Font,
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawFlag :: enum {
|
type DrawFlag enum {
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
FLIP_H = 1<<0,
|
FLIP_H = 1<<0,
|
||||||
FLIP_V = 1<<1,
|
FLIP_V = 1<<1,
|
||||||
MASK = 1<<2,
|
MASK = 1<<2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Draw_Item struct {}
|
||||||
Draw_List :: struct {
|
type Draw_List struct {
|
||||||
Item :: struct {
|
items: []Draw_Item,
|
||||||
|
|
||||||
}
|
|
||||||
items: []Item
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Key :: enum {
|
type Key enum {
|
||||||
MOD_SHIFT = 0x0001,
|
ModShift = 0x0001,
|
||||||
MOD_CONTROL = 0x0002,
|
ModControl = 0x0002,
|
||||||
MOD_ALT = 0x0004,
|
ModAlt = 0x0004,
|
||||||
MOD_SUPER = 0x0008,
|
ModSuper = 0x0008,
|
||||||
|
|
||||||
UNKNOWN =-1,
|
|
||||||
INVALID =-2,
|
|
||||||
|
|
||||||
LBUTTON = 1,
|
Unknown =-1,
|
||||||
RBUTTON = 2,
|
Invalid =-2,
|
||||||
CANCEL = 3,
|
|
||||||
MBUTTON = 4,
|
|
||||||
|
|
||||||
BACK = 8,
|
|
||||||
TAB = 9,
|
Lbutton = 1,
|
||||||
CLEAR = 12,
|
Rbutton = 2,
|
||||||
RETURN = 13,
|
Cancel = 3,
|
||||||
SHIFT = 16,
|
Mbutton = 4,
|
||||||
CONTROL = 17,
|
|
||||||
MENU = 18,
|
|
||||||
PAUSE = 19,
|
Back = 8,
|
||||||
CAPITAL = 20,
|
Tab = 9,
|
||||||
KANA = 0x15,
|
Clear = 12,
|
||||||
HANGEUL = 0x15,
|
Return = 13,
|
||||||
HANGUL = 0x15,
|
Shift = 16,
|
||||||
JUNJA = 0x17,
|
Control = 17,
|
||||||
FINAL = 0x18,
|
Menu = 18,
|
||||||
HANJA = 0x19,
|
Pause = 19,
|
||||||
KANJI = 0x19,
|
Capital = 20,
|
||||||
ESCAPE = 0x1B,
|
Kana = 0x15,
|
||||||
CONVERT = 0x1C,
|
Hangeul = 0x15,
|
||||||
NONCONVERT = 0x1D,
|
Hangul = 0x15,
|
||||||
ACCEPT = 0x1E,
|
Junja = 0x17,
|
||||||
MODECHANGE = 0x1F,
|
Final = 0x18,
|
||||||
SPACE = 32,
|
Hanja = 0x19,
|
||||||
PRIOR = 33,
|
Kanji = 0x19,
|
||||||
NEXT = 34,
|
Escape = 0x1B,
|
||||||
END = 35,
|
Convert = 0x1C,
|
||||||
HOME = 36,
|
NonConvert = 0x1D,
|
||||||
LEFT = 37,
|
Accept = 0x1E,
|
||||||
UP = 38,
|
ModeChange = 0x1F,
|
||||||
RIGHT = 39,
|
Space = 32,
|
||||||
DOWN = 40,
|
Prior = 33,
|
||||||
SELECT = 41,
|
Next = 34,
|
||||||
PRINT = 42,
|
End = 35,
|
||||||
EXEC = 43,
|
Home = 36,
|
||||||
SNAPSHOT = 44,
|
Left = 37,
|
||||||
INSERT = 45,
|
Up = 38,
|
||||||
DELETE = 46,
|
Right = 39,
|
||||||
HELP = 47,
|
Down = 40,
|
||||||
LWIN = 0x5B,
|
Select = 41,
|
||||||
RWIN = 0x5C,
|
Print = 42,
|
||||||
APPS = 0x5D,
|
Exec = 43,
|
||||||
SLEEP = 0x5F,
|
Snapshot = 44,
|
||||||
NUMPAD0 = 0x60,
|
Insert = 45,
|
||||||
NUMPAD1 = 0x61,
|
Delete = 46,
|
||||||
NUMPAD2 = 0x62,
|
Help = 47,
|
||||||
NUMPAD3 = 0x63,
|
Lwin = 0x5B,
|
||||||
NUMPAD4 = 0x64,
|
Rwin = 0x5C,
|
||||||
NUMPAD5 = 0x65,
|
Apps = 0x5D,
|
||||||
NUMPAD6 = 0x66,
|
Sleep = 0x5F,
|
||||||
NUMPAD7 = 0x67,
|
Numpad0 = 0x60,
|
||||||
NUMPAD8 = 0x68,
|
Numpad1 = 0x61,
|
||||||
NUMPAD9 = 0x69,
|
Numpad2 = 0x62,
|
||||||
MULTIPLY = 0x6A,
|
Numpad3 = 0x63,
|
||||||
ADD = 0x6B,
|
Numpad4 = 0x64,
|
||||||
SEPARATOR = 0x6C,
|
Numpad5 = 0x65,
|
||||||
SUBTRACT = 0x6D,
|
Numpad6 = 0x66,
|
||||||
DECIMAL = 0x6E,
|
Numpad7 = 0x67,
|
||||||
DIVIDE = 0x6F,
|
Numpad8 = 0x68,
|
||||||
|
Numpad9 = 0x69,
|
||||||
|
Multiply = 0x6A,
|
||||||
|
Add = 0x6B,
|
||||||
|
Separator = 0x6C,
|
||||||
|
Subtract = 0x6D,
|
||||||
|
Decimal = 0x6E,
|
||||||
|
Divide = 0x6F,
|
||||||
F1 = 0x70,
|
F1 = 0x70,
|
||||||
F2 = 0x71,
|
F2 = 0x71,
|
||||||
F3 = 0x72,
|
F3 = 0x72,
|
||||||
@@ -212,32 +216,33 @@ Key :: enum {
|
|||||||
F22 = 0x85,
|
F22 = 0x85,
|
||||||
F23 = 0x86,
|
F23 = 0x86,
|
||||||
F24 = 0x87,
|
F24 = 0x87,
|
||||||
NUMLOCK = 0x90,
|
Numlock = 0x90,
|
||||||
SCROLL = 0x91,
|
Scroll = 0x91,
|
||||||
LSHIFT = 0xA0,
|
Lshift = 0xA0,
|
||||||
RSHIFT = 0xA1,
|
Rshift = 0xA1,
|
||||||
LCONTROL = 0xA2,
|
Lcontrol = 0xA2,
|
||||||
RCONTROL = 0xA3,
|
Rcontrol = 0xA3,
|
||||||
LMENU = 0xA4,
|
Lmenu = 0xA4,
|
||||||
RMENU = 0xA5,
|
Rmenu = 0xA5,
|
||||||
|
|
||||||
APOSTROPHE = 39, /* ' */
|
|
||||||
COMMA = 44, /* , */
|
Apostrophe = 39, /* ' */
|
||||||
MINUS = 45, /* - */
|
Comma = 44, /* , */
|
||||||
PERIOD = 46, /* . */
|
Minus = 45, /* - */
|
||||||
SLASH = 47, /* / */
|
Period = 46, /* . */
|
||||||
NUM0 = 48,
|
Slash = 47, /* / */
|
||||||
NUM1 = 49,
|
Num0 = 48,
|
||||||
NUM2 = 50,
|
Num1 = 49,
|
||||||
NUM3 = 51,
|
Num2 = 50,
|
||||||
NUM4 = 52,
|
Num3 = 51,
|
||||||
NUM5 = 53,
|
Num4 = 52,
|
||||||
NUM6 = 54,
|
Num5 = 53,
|
||||||
NUM7 = 55,
|
Num6 = 54,
|
||||||
NUM8 = 56,
|
Num7 = 55,
|
||||||
NUM9 = 57,
|
Num8 = 56,
|
||||||
SEMICOLON = 59, /* ; */
|
Num9 = 57,
|
||||||
EQUAL = 61, /* = */
|
Semicolon = 59, /* ; */
|
||||||
|
Equal = 61, /* = */
|
||||||
A = 65,
|
A = 65,
|
||||||
B = 66,
|
B = 66,
|
||||||
C = 67,
|
C = 67,
|
||||||
@@ -264,221 +269,230 @@ Key :: enum {
|
|||||||
X = 88,
|
X = 88,
|
||||||
Y = 89,
|
Y = 89,
|
||||||
Z = 90,
|
Z = 90,
|
||||||
LEFT_BRACKET = 91, /* [ */
|
LeftBracket = 91, /* [ */
|
||||||
BACKSLASH = 92, /* \ */
|
Backslash = 92, /* \ */
|
||||||
RIGHT_BRACKET = 93, /* ] */
|
RightBracket = 93, /* ] */
|
||||||
GRAVE_ACCENT = 96, /* ` */
|
GraveAccent = 96, /* ` */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
proc key_down(k: Key) -> bool {
|
||||||
|
return _core.key_states[k] != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc key_pressed(k: Key) -> bool {
|
||||||
key_down :: proc(k: Key) -> bool {
|
return (_core.key_deltas[k] != 0) && key_down(k);
|
||||||
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()
|
let win32_perf_count_freq = win32.get_query_performance_frequency();
|
||||||
time_now :: proc() -> f64 {
|
proc time_now() -> f64 {
|
||||||
assert(win32_perf_count_freq != 0)
|
assert(win32_perf_count_freq != 0);
|
||||||
|
|
||||||
counter: i64
|
var counter: i64;
|
||||||
win32.QueryPerformanceCounter(^counter)
|
win32.query_performance_counter(&counter);
|
||||||
result := counter as f64 / win32_perf_count_freq as f64
|
return f64(counter) / f64(win32_perf_count_freq);
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_core: Core
|
var _core: Core;
|
||||||
|
|
||||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
proc run(user_init, user_step: proc(c: ^Core)) {
|
||||||
using win32
|
using win32;
|
||||||
|
|
||||||
|
_core.running = true;
|
||||||
|
|
||||||
_core.running = true
|
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
|
||||||
|
proc win32_app_key_mods() -> u32 {
|
||||||
|
var mods: u32 = 0;
|
||||||
|
|
||||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall {
|
if is_key_down(KeyCode.Shift) {
|
||||||
win32_app_key_mods :: proc() -> u32 {
|
mods |= u32(Key.ModShift);
|
||||||
mods: u32 = 0
|
|
||||||
|
|
||||||
if is_key_down(Key_Code.SHIFT) {
|
|
||||||
mods |= Key.MOD_SHIFT as u32;
|
|
||||||
}
|
}
|
||||||
if is_key_down(Key_Code.CONTROL) {
|
if is_key_down(KeyCode.Control) {
|
||||||
mods |= Key.MOD_CONTROL as u32;
|
mods |= u32(Key.ModControl);
|
||||||
}
|
}
|
||||||
if is_key_down(Key_Code.MENU) {
|
if is_key_down(KeyCode.Menu) {
|
||||||
mods |= Key.MOD_ALT as u32;
|
mods |= u32(Key.ModAlt);
|
||||||
}
|
}
|
||||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
if is_key_down(KeyCode.Lwin) || is_key_down(KeyCode.Rwin) {
|
||||||
mods |= Key.MOD_SUPER as u32;
|
mods |= u32(Key.ModSuper);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mods
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
match msg {
|
match msg {
|
||||||
case WM_KEYDOWN:
|
case WM_KEYDOWN:
|
||||||
_core.key_modifiers = win32_app_key_mods()
|
_core.key_modifiers = win32_app_key_mods();
|
||||||
if wparam < MAX_KEYS {
|
if wparam < MAX_KEYS {
|
||||||
_core.key_states[wparam] = 1
|
_core.key_states[wparam] = 1;
|
||||||
_core.key_deltas[wparam] = 1
|
_core.key_deltas[wparam] = 1;
|
||||||
}
|
}
|
||||||
return 0
|
return 0;
|
||||||
|
|
||||||
case WM_KEYUP:
|
case WM_KEYUP:
|
||||||
_core.key_modifiers = win32_app_key_mods()
|
_core.key_modifiers = win32_app_key_mods();
|
||||||
if wparam < MAX_KEYS {
|
if wparam < MAX_KEYS {
|
||||||
_core.key_states[wparam] = 0
|
_core.key_states[wparam] = 0;
|
||||||
_core.key_deltas[wparam] = 1
|
_core.key_deltas[wparam] = 1;
|
||||||
}
|
}
|
||||||
return 0
|
return 0;
|
||||||
|
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
PostQuitMessage(0)
|
post_quit_message(0);
|
||||||
_core.running = false
|
_core.running = false;
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProcA(hwnd, msg, wparam, lparam)
|
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
window_class := WNDCLASSEXA{
|
var class_name = "Punity\x00";
|
||||||
class_name = ("Punity\x00" as string).data, // C-style string
|
var window_class = WndClassExA{
|
||||||
size = size_of(WNDCLASSEXA) as u32,
|
class_name = &class_name[0],
|
||||||
|
size = size_of(WndClassExA),
|
||||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||||
instance = GetModuleHandleA(null) as HINSTANCE,
|
instance = Hinstance(get_module_handle_a(nil)),
|
||||||
wnd_proc = win32_proc,
|
wnd_proc = win32_proc,
|
||||||
// wnd_proc = DefWindowProcA,
|
// wnd_proc = DefWindowProcA,
|
||||||
background = GetStockObject(BLACK_BRUSH) as HBRUSH,
|
background = Hbrush(get_stock_object(BLACK_BRUSH)),
|
||||||
|
};
|
||||||
|
|
||||||
|
if register_class_ex_a(&window_class) == 0 {
|
||||||
|
fmt.fprintln(os.stderr, "register_class_ex_a failed");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if RegisterClassExA(^window_class) == 0 {
|
var screen_width = get_system_metrics(SM_CXSCREEN);
|
||||||
fmt.fprintln(os.stderr, "RegisterClassExA failed")
|
var screen_height = get_system_metrics(SM_CYSCREEN);
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
screen_width := GetSystemMetrics(SM_CXSCREEN)
|
var rc: Rect;
|
||||||
screen_height := GetSystemMetrics(SM_CYSCREEN)
|
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;
|
||||||
|
|
||||||
rc: RECT
|
var style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||||
rc.left = (screen_width - WINDOW_WIDTH) / 2
|
assert(adjust_window_rect(&rc, style, 0) != 0);
|
||||||
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
|
var wt = WINDOW_TITLE;
|
||||||
assert(AdjustWindowRect(^rc, style, 0) != 0)
|
|
||||||
|
|
||||||
wt := WINDOW_TITLE
|
var win32_window = create_window_ex_a(0,
|
||||||
|
window_class.class_name,
|
||||||
|
&wt[0],
|
||||||
|
style,
|
||||||
|
rc.left, rc.top,
|
||||||
|
rc.right-rc.left, rc.bottom-rc.top,
|
||||||
|
nil, nil, window_class.instance,
|
||||||
|
nil);
|
||||||
|
|
||||||
win32_window := CreateWindowExA(0,
|
if win32_window == nil {
|
||||||
window_class.class_name,
|
fmt.fprintln(os.stderr, "create_window_ex_a failed");
|
||||||
wt.data,
|
return;
|
||||||
style,
|
|
||||||
rc.left, rc.top,
|
|
||||||
rc.right-rc.left, rc.bottom-rc.top,
|
|
||||||
null, null, window_class.instance,
|
|
||||||
null);
|
|
||||||
|
|
||||||
if win32_window == null {
|
|
||||||
fmt.fprintln(os.stderr, "CreateWindowExA failed")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
window_bmi: BITMAPINFO;
|
var window_bmi: BitmapInfo;
|
||||||
window_bmi.size = size_of(BITMAPINFO.HEADER) as u32
|
window_bmi.size = size_of(BitmapInfoHeader);
|
||||||
window_bmi.width = CANVAS_WIDTH
|
window_bmi.width = CANVAS_WIDTH;
|
||||||
window_bmi.height = CANVAS_HEIGHT
|
window_bmi.height = CANVAS_HEIGHT;
|
||||||
window_bmi.planes = 1
|
window_bmi.planes = 1;
|
||||||
window_bmi.bit_count = 32
|
window_bmi.bit_count = 32;
|
||||||
window_bmi.compression = BI_RGB
|
window_bmi.compression = BI_RGB;
|
||||||
|
|
||||||
|
|
||||||
user_init(^_core)
|
user_init(&_core);
|
||||||
|
|
||||||
|
show_window(win32_window, SW_SHOW);
|
||||||
|
|
||||||
ShowWindow(win32_window, SW_SHOW)
|
var window_buffer = make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||||
|
defer free(window_buffer);
|
||||||
|
|
||||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
for _, i in window_buffer {
|
||||||
assert(window_buffer.data != null)
|
window_buffer[i] = 0xff00ff;
|
||||||
defer free(window_buffer.data)
|
|
||||||
|
|
||||||
for i := 0; i < window_buffer.count; i++ {
|
|
||||||
window_buffer[i] = 0xff00ff
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
dt: f64;
|
||||||
|
prev_time = time_now();
|
||||||
|
curr_time = time_now();
|
||||||
|
total_time : f64 = 0;
|
||||||
|
offset_x = 0;
|
||||||
|
offset_y = 0;
|
||||||
|
)
|
||||||
|
|
||||||
prev_time, curr_time,dt: f64
|
var message: Msg;
|
||||||
prev_time = time_now()
|
|
||||||
curr_time = time_now()
|
|
||||||
total_time : f64 = 0
|
|
||||||
offset_x := 0;
|
|
||||||
offset_y := 0;
|
|
||||||
|
|
||||||
message: MSG
|
|
||||||
for _core.running {
|
for _core.running {
|
||||||
curr_time = time_now()
|
curr_time = time_now();
|
||||||
dt = curr_time - prev_time
|
dt = curr_time - prev_time;
|
||||||
prev_time = curr_time
|
prev_time = curr_time;
|
||||||
total_time += dt
|
total_time += dt;
|
||||||
|
|
||||||
offset_x += 1
|
offset_x += 1;
|
||||||
offset_y += 2
|
offset_y += 2;
|
||||||
|
|
||||||
{
|
{
|
||||||
data: [128]byte
|
var buf: [128]u8;
|
||||||
buf := data[:0]
|
var s = fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
|
||||||
fmt.bprintf(^buf, "Punity: % ms\x00", dt*1000)
|
win32.set_window_text_a(win32_window, &s[0]);
|
||||||
win32.SetWindowTextA(win32_window, buf.data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for y := 0; y < CANVAS_HEIGHT; y++ {
|
for var y = 0; y < CANVAS_HEIGHT; y++ {
|
||||||
for x := 0; x < CANVAS_WIDTH; x++ {
|
for var x = 0; x < CANVAS_WIDTH; x++ {
|
||||||
g := (x % 32) * 8
|
var g = (x % 32) * 8;
|
||||||
b := (y % 32) * 8
|
var b = (y % 32) * 8;
|
||||||
window_buffer[x + y*CANVAS_WIDTH] = (g << 8 | b) as u32
|
window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_zero(^_core.key_deltas[0], size_of_val(_core.key_deltas[0]))
|
mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
|
||||||
|
|
||||||
|
for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||||
for PeekMessageA(^message, null, 0, 0, PM_REMOVE) != 0 {
|
|
||||||
if message.message == WM_QUIT {
|
if message.message == WM_QUIT {
|
||||||
_core.running = false
|
_core.running = false;
|
||||||
}
|
}
|
||||||
TranslateMessage(^message)
|
translate_message(&message);
|
||||||
DispatchMessageA(^message)
|
dispatch_message_a(&message);
|
||||||
}
|
}
|
||||||
|
|
||||||
user_step(^_core)
|
user_step(&_core);
|
||||||
|
|
||||||
dc := GetDC(win32_window);
|
var dc = get_dc(win32_window);
|
||||||
StretchDIBits(dc,
|
stretch_dibits(dc,
|
||||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||||
window_buffer.data,
|
&window_buffer[0],
|
||||||
^window_bmi,
|
&window_bmi,
|
||||||
DIB_RGB_COLORS,
|
DIB_RGB_COLORS,
|
||||||
SRCCOPY)
|
SRCCOPY);
|
||||||
ReleaseDC(win32_window, dc)
|
release_dc(win32_window, dc);
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
delta := time_now() - prev_time
|
var delta = time_now() - prev_time;
|
||||||
ms := ((FRAME_TIME - delta) * 1000) as i32
|
var ms = i32((FRAME_TIME - delta) * 1000);
|
||||||
if ms > 0 {
|
if ms > 0 {
|
||||||
win32.Sleep(ms)
|
win32.sleep(ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_core.frame++
|
_core.frame++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
proc main() {
|
||||||
|
proc user_init(c: ^Core) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proc user_step(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" as format
|
|
||||||
|
|
||||||
thing :: proc() {
|
|
||||||
format.println("Hello2!")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*#import "fmt.odin" as .
|
|
||||||
|
|
||||||
thing :: proc() {
|
|
||||||
println("Hello3!")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
/*#import "fmt.odin" as _
|
|
||||||
|
|
||||||
thing :: proc() {
|
|
||||||
// println("Hello4!")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#load "fmt.odin"
|
|
||||||
|
|
||||||
thing :: proc() {
|
|
||||||
println("Hello5!")
|
|
||||||
}*/
|
|
||||||
+953
-277
File diff suppressed because it is too large
Load Diff
+199
-130
@@ -1,157 +1,226 @@
|
|||||||
#shared_global_scope
|
#shared_global_scope;
|
||||||
|
|
||||||
#import "fmt.odin"
|
__multi3 :: proc(a, b: u128) -> u128 #cc_c #link_name "__multi3" {
|
||||||
|
bits_in_dword_2 :: size_of(i64) * 4;
|
||||||
|
lower_mask :: u128(~u64(0) >> bits_in_dword_2);
|
||||||
|
|
||||||
|
|
||||||
__u128_mod :: proc(a, b: u128) -> u128 #link_name "__umodti3" {
|
TWords :: struct #raw_union {
|
||||||
_, r := __u128_quo_mod(a, b)
|
all: u128;
|
||||||
return r
|
using _: struct {
|
||||||
|
when ODIN_ENDIAN == "big" {
|
||||||
|
lo, hi: u64;
|
||||||
|
} else {
|
||||||
|
hi, lo: u64;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
r: TWords;
|
||||||
|
t: u64;
|
||||||
|
|
||||||
|
r.lo = u64(a & lower_mask) * u64(b & lower_mask);
|
||||||
|
t = r.lo >> bits_in_dword_2;
|
||||||
|
r.lo &= u64(lower_mask);
|
||||||
|
t += u64(a >> bits_in_dword_2) * u64(b & lower_mask);
|
||||||
|
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
|
||||||
|
r.hi = t >> bits_in_dword_2;
|
||||||
|
t = r.lo >> bits_in_dword_2;
|
||||||
|
r.lo &= u64(lower_mask);
|
||||||
|
t += u64(b >> bits_in_dword_2) * u64(a & lower_mask);
|
||||||
|
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
|
||||||
|
r.hi += t >> bits_in_dword_2;
|
||||||
|
r.hi += u64(a >> bits_in_dword_2) * u64(b >> bits_in_dword_2);
|
||||||
|
return r.all;
|
||||||
}
|
}
|
||||||
|
|
||||||
__u128_quo :: proc(a, b: u128) -> u128 #link_name "__udivti3" {
|
__u128_mod :: proc(a, b: u128) -> u128 #cc_c #link_name "__umodti3" {
|
||||||
n, _ := __u128_quo_mod(a, b)
|
r: u128;
|
||||||
return n
|
__u128_quo_mod(a, b, &r);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
__i128_mod :: proc(a, b: i128) -> i128 #link_name "__modti3" {
|
__u128_quo :: proc(a, b: u128) -> u128 #cc_c #link_name "__udivti3" {
|
||||||
_, r := __i128_quo_mod(a, b)
|
return __u128_quo_mod(a, b, nil);
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__i128_quo :: proc(a, b: i128) -> i128 #link_name "__divti3" {
|
__i128_mod :: proc(a, b: i128) -> i128 #cc_c #link_name "__modti3" {
|
||||||
n, _ := __i128_quo_mod(a, b)
|
r: i128;
|
||||||
return n
|
__i128_quo_mod(a, b, &r);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
__i128_quo_mod :: proc(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
__i128_quo :: proc(a, b: i128) -> i128 #cc_c #link_name "__divti3" {
|
||||||
s := b >> 127
|
return __i128_quo_mod(a, b, nil);
|
||||||
b = (b ~ s) - s
|
}
|
||||||
s = a >> 127
|
|
||||||
a = (a ~ s) - s
|
|
||||||
|
|
||||||
n, r := __u128_quo_mod(a as u128, b as u128)
|
__i128_quo_mod :: proc(a, b: i128, rem: ^i128) -> (quo: i128) #cc_c #link_name "__divmodti4" {
|
||||||
return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
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 do rem^ = irem;
|
||||||
|
return iquo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
__u128_quo_mod :: proc(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
__u128_quo_mod :: proc(a, b: u128, rem: ^u128) -> (quo: u128) #cc_c #link_name "__udivmodti4" {
|
||||||
clz :: proc(x: u64) -> u64 {
|
alo, ahi := u64(a), u64(a>>64);
|
||||||
clz_u64 :: proc(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
blo, bhi := u64(b), u64(b>>64);
|
||||||
return clz_u64(x, false)
|
if b == 0 {
|
||||||
}
|
if rem != nil do rem^ = 0;
|
||||||
ctz :: proc(x: u64) -> u64 {
|
return u128(alo/blo);
|
||||||
ctz_u64 :: proc(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
|
||||||
return ctz_u64(x, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r, d, x, q: u128 = a, b, 1, 0;
|
||||||
|
|
||||||
u128_lo_hi :: raw_union {
|
for r >= d && (d>>127)&1 == 0 {
|
||||||
all: u128
|
x <<= 1;
|
||||||
using _lohi: struct {lo, hi: u64}
|
d <<= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
n, d, q, r: u128_lo_hi
|
for x != 0 {
|
||||||
sr: u64
|
if r >= d {
|
||||||
|
r -= d;
|
||||||
n.all = a
|
q |= x;
|
||||||
d.all = b
|
|
||||||
|
|
||||||
if n.hi == 0 {
|
|
||||||
if d.hi == 0 {
|
|
||||||
return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
|
|
||||||
}
|
}
|
||||||
return 0, n.lo as u128
|
x >>= 1;
|
||||||
|
d >>= 1;
|
||||||
}
|
}
|
||||||
if d.lo == 0 {
|
|
||||||
if d.hi == 0 {
|
|
||||||
return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
|
|
||||||
}
|
|
||||||
if n.lo == 0 {
|
|
||||||
r.hi = n.hi % d.hi
|
|
||||||
r.lo = 0
|
|
||||||
return (n.hi / d.hi) as u128, r.all
|
|
||||||
}
|
|
||||||
if (d.hi & (d.hi-1)) == 0 {
|
|
||||||
r.lo = n.lo
|
|
||||||
r.hi = n.hi & (d.hi-1)
|
|
||||||
return (n.hi >> ctz(d.hi)) as u128, r.all
|
|
||||||
}
|
|
||||||
|
|
||||||
sr = clz(d.hi) - clz(n.hi)
|
if rem != nil do rem^ = r;
|
||||||
if sr > 64 - 2 {
|
return q;
|
||||||
return 0, n.all
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
__f16_to_f32 :: proc(f: f16) -> f32 #cc_c #no_inline #link_name "__gnu_h2f_ieee" {
|
||||||
|
when true {
|
||||||
|
// Source: https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
||||||
|
FP32 :: struct #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;
|
||||||
}
|
}
|
||||||
sr++
|
o.u |= u32(hu & 0x8000) << 16;
|
||||||
q.lo = 0
|
return o.f;
|
||||||
q.hi = n.lo << (64-sr)
|
|
||||||
r.hi = n.hi >> sr
|
|
||||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
|
||||||
} else {
|
} else {
|
||||||
if d.hi == 0 {
|
return 0;
|
||||||
if (d.lo & (d.lo - 1)) == 0 {
|
|
||||||
rem := (n.lo % (d.lo - 1)) as u128
|
|
||||||
if d.lo == 1 {
|
|
||||||
return n.all, rem
|
|
||||||
}
|
|
||||||
sr = ctz(d.lo)
|
|
||||||
q.hi = n.hi >> sr
|
|
||||||
q.lo = (n.hi << (64-sr)) | (n.lo >> sr);
|
|
||||||
return q.all, rem
|
|
||||||
}
|
|
||||||
|
|
||||||
sr = 1 + 64 + clz(d.lo) - clz(n.hi)
|
|
||||||
|
|
||||||
q.all = n.all << (128-sr)
|
|
||||||
r.all = n.all >> sr
|
|
||||||
if sr == 64 {
|
|
||||||
q.lo = 0
|
|
||||||
q.hi = n.lo
|
|
||||||
r.hi = 0
|
|
||||||
r.lo = n.hi
|
|
||||||
} else if sr < 64 {
|
|
||||||
q.lo = 0
|
|
||||||
q.hi = n.lo << (64-sr)
|
|
||||||
r.hi = n.hi >> sr
|
|
||||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
|
||||||
} else {
|
|
||||||
q.lo = n.lo << (128-sr)
|
|
||||||
q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
|
|
||||||
r.hi = 0
|
|
||||||
r.lo = n.hi >> (sr-64)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sr = clz(d.hi) - clz(n.hi)
|
|
||||||
if sr > 64-1 {
|
|
||||||
return 0, n.all
|
|
||||||
}
|
|
||||||
sr++
|
|
||||||
q.lo = 0
|
|
||||||
q.hi = n.lo << (64-sr)
|
|
||||||
r.all = n.all >> sr
|
|
||||||
if sr < 64 {
|
|
||||||
r.hi = n.hi >> sr
|
|
||||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
|
||||||
} else {
|
|
||||||
r.hi = 0
|
|
||||||
r.lo = n.hi
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
carry: u64
|
|
||||||
for ; sr > 0; sr-- {
|
|
||||||
r.hi = (r.hi << 1) | (r.lo >> (64-1))
|
|
||||||
r.lo = (r.lo << 1) | (r.hi >> (64-1))
|
|
||||||
q.hi = (q.hi << 1) | (q.lo >> (64-1))
|
|
||||||
q.lo = (q.lo << 1) | carry
|
|
||||||
|
|
||||||
carry = 0
|
|
||||||
if r.all >= d.all {
|
|
||||||
r.all -= d.all
|
|
||||||
carry = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
q.all = (q.all << 1) | (carry as u128)
|
|
||||||
return q.all, r.all
|
|
||||||
}
|
}
|
||||||
|
__f32_to_f16 :: proc(f_: f32) -> f16 #cc_c #no_inline #link_name "__gnu_f2h_ieee" {
|
||||||
|
when false {
|
||||||
|
// Source: https://gist.github.com/rygorous/2156668
|
||||||
|
FP16 :: struct #raw_union {u: u16, f: f16};
|
||||||
|
FP32 :: struct #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_c #no_inline #link_name "__truncdfhf2" {
|
||||||
|
return __f32_to_f16(f32(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
__f16_to_f64 :: proc(f: f16) -> f64 #cc_c #no_inline {
|
||||||
|
return f64(__f16_to_f32(f));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
// 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 += 1;
|
||||||
|
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 += 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
+285
@@ -0,0 +1,285 @@
|
|||||||
|
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 { foreign __llvm_core __llvm_ctpop :: proc(u8) -> u8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctpop :: proc(i8) -> i8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctpop :: proc(u16) -> u16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctpop :: proc(i16) -> i16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctpop :: proc(u32) -> u32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctpop :: proc(i32) -> i32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctpop :: proc(u64) -> u64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctpop :: proc(i64) -> i64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctpop :: proc(u128) -> u128 #link_name "llvm.ctpop.i128" ---;return __llvm_ctpop(i); }
|
||||||
|
count_ones :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctpop :: proc(i128) -> i128 #link_name "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 { foreign __llvm_core __llvm_ctlz :: proc(u8, bool) -> u8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctlz :: proc(i8, bool) -> i8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctlz :: proc(u16, bool) -> u16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctlz :: proc(i16, bool) -> i16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctlz :: proc(u32, bool) -> u32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctlz :: proc(i32, bool) -> i32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctlz :: proc(u64, bool) -> u64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctlz :: proc(i64, bool) -> i64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctlz :: proc(u128, bool) -> u128 #link_name "llvm.ctlz.i128" ---;return __llvm_ctlz(i, false); }
|
||||||
|
leading_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctlz :: proc(i128, bool) -> i128 #link_name "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 { foreign __llvm_core __llvm_cttz :: proc(u8, bool) -> u8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_cttz :: proc(i8, bool) -> i8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_cttz :: proc(u16, bool) -> u16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_cttz :: proc(i16, bool) -> i16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_cttz :: proc(u32, bool) -> u32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_cttz :: proc(i32, bool) -> i32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_cttz :: proc(u64, bool) -> u64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_cttz :: proc(i64, bool) -> i64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_cttz :: proc(u128, bool) -> u128 #link_name "llvm.cttz.i128" ---;return __llvm_cttz(i, false); }
|
||||||
|
trailing_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_cttz :: proc(i128, bool) -> i128 #link_name "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 { foreign __llvm_core __llvm_bitreverse :: proc(u8) -> u8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_bitreverse :: proc(i8) -> i8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_bitreverse :: proc(u16) -> u16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_bitreverse :: proc(i16) -> i16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_bitreverse :: proc(u32) -> u32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_bitreverse :: proc(i32) -> i32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_bitreverse :: proc(u64) -> u64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_bitreverse :: proc(i64) -> i64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_bitreverse :: proc(u128) -> u128 #link_name "llvm.bitreverse.i128" ---;return __llvm_bitreverse(i); }
|
||||||
|
reverse_bits :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_bitreverse :: proc(i128) -> i128 #link_name "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))); } }
|
||||||
|
|
||||||
|
foreign __llvm_core {
|
||||||
|
byte_swap :: proc(u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
||||||
|
byte_swap :: proc(i16) -> i16 #link_name "llvm.bswap.i16" ---;
|
||||||
|
byte_swap :: proc(u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
||||||
|
byte_swap :: proc(i32) -> i32 #link_name "llvm.bswap.i32" ---;
|
||||||
|
byte_swap :: proc(u64) -> u64 #link_name "llvm.bswap.i64" ---;
|
||||||
|
byte_swap :: proc(i64) -> i64 #link_name "llvm.bswap.i64" ---;
|
||||||
|
byte_swap :: proc(u128) -> u128 #link_name "llvm.bswap.i128" ---;
|
||||||
|
byte_swap :: proc(i128) -> i128 #link_name "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) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.uadd.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.sadd.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.uadd.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.sadd.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.uadd.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.sadd.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.uadd.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.sadd.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.uadd.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_add :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "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) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.usub.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.ssub.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.usub.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.ssub.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.usub.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.ssub.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.usub.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.ssub.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.usub.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_sub :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "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) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.umul.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.smul.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.umul.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.smul.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.umul.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.smul.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.umul.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.smul.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.umul.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||||
|
overflowing_mul :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "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; }
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
CHAR_BIT :: 8;
|
||||||
|
|
||||||
|
c_bool :: bool;
|
||||||
|
|
||||||
|
c_char :: u8;
|
||||||
|
|
||||||
|
c_schar :: i8;
|
||||||
|
c_uchar :: i8;
|
||||||
|
|
||||||
|
c_short :: i16;
|
||||||
|
c_ushort :: i16;
|
||||||
|
|
||||||
|
c_int :: i32;
|
||||||
|
c_uint :: u32;
|
||||||
|
|
||||||
|
c_long :: ODIN_OS == "windows" ?
|
||||||
|
i32 :
|
||||||
|
(size_of(int) == 4) ?
|
||||||
|
i32 :
|
||||||
|
i64;
|
||||||
|
|
||||||
|
c_ulong :: ODIN_OS == "windows" ?
|
||||||
|
u32 :
|
||||||
|
(size_of(int) == 4) ?
|
||||||
|
u32 :
|
||||||
|
u64;
|
||||||
|
|
||||||
|
c_longlong :: i64;
|
||||||
|
c_ulonglong :: u64;
|
||||||
|
|
||||||
|
c_float :: f32;
|
||||||
|
c_double :: f64;
|
||||||
|
|
||||||
|
c_complex_float :: complex64;
|
||||||
|
c_complex_double :: complex128;
|
||||||
|
|
||||||
|
c_size_t :: uint;
|
||||||
|
c_ssize_t :: int;
|
||||||
|
c_ptrdiff_t :: int;
|
||||||
|
c_uintptr_t :: uint;
|
||||||
|
c_intptr_t :: int;
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
// #import "fmt.odin";
|
||||||
|
// Multiple precision decimal numbers
|
||||||
|
// NOTE: This is only for floating point printing and nothing else
|
||||||
|
|
||||||
|
Decimal :: struct {
|
||||||
|
digits: [384]u8; // 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 do buf[i] = '0';
|
||||||
|
return len(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
n := 10 + a.count + abs(a.decimal_point);
|
||||||
|
|
||||||
|
// TODO(bill): make this work with a buffer that's not big enough
|
||||||
|
assert(len(buf) >= n);
|
||||||
|
buf = buf[0..n];
|
||||||
|
|
||||||
|
if a.count == 0 {
|
||||||
|
buf[0] = '0';
|
||||||
|
return string(buf[0..1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
w := 0;
|
||||||
|
if a.decimal_point <= 0 {
|
||||||
|
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+=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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf[0..w]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim trailing zeros
|
||||||
|
trim :: proc(a: ^Decimal) {
|
||||||
|
for a.count > 0 && a.digits[a.count-1] == '0' {
|
||||||
|
a.count -= 1;
|
||||||
|
}
|
||||||
|
if a.count == 0 {
|
||||||
|
a.decimal_point = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
assign :: proc(a: ^Decimal, i: u64) {
|
||||||
|
buf: [32]u8;
|
||||||
|
n := 0;
|
||||||
|
for i > 0 {
|
||||||
|
j := i/10;
|
||||||
|
i -= 10*j;
|
||||||
|
buf[n] = u8('0'+i);
|
||||||
|
n+=1;
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.count = 0;
|
||||||
|
for n -= 1; n >= 0; n -= 1 {
|
||||||
|
a.digits[a.count] = buf[n];
|
||||||
|
a.count+=1;
|
||||||
|
}
|
||||||
|
a.decimal_point = a.count;
|
||||||
|
trim(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
shift_right :: proc(a: ^Decimal, k: uint) {
|
||||||
|
r := 0; // read index
|
||||||
|
w := 0; // write index
|
||||||
|
|
||||||
|
n: uint;
|
||||||
|
for ; n>>k == 0; r+=1 {
|
||||||
|
if r >= a.count {
|
||||||
|
if n == 0 {
|
||||||
|
// Just in case
|
||||||
|
a.count = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for n>>k == 0 {
|
||||||
|
n = n * 10;
|
||||||
|
r+=1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c := uint(a.digits[r]);
|
||||||
|
n = n*10 + c - '0';
|
||||||
|
}
|
||||||
|
a.decimal_point -= r-1;
|
||||||
|
|
||||||
|
mask: uint = (1<<k) - 1;
|
||||||
|
|
||||||
|
for ; r < a.count; r+=1 {
|
||||||
|
c := uint(a.digits[r]);
|
||||||
|
dig := n>>k;
|
||||||
|
n &= mask;
|
||||||
|
a.digits[w] = u8('0' + dig);
|
||||||
|
w+=1;
|
||||||
|
n = n*10 + c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
for n > 0 {
|
||||||
|
dig := n>>k;
|
||||||
|
n &= mask;
|
||||||
|
if w < len(a.digits) {
|
||||||
|
a.digits[w] = u8('0' + dig);
|
||||||
|
w+=1;
|
||||||
|
} else if dig > 0 {
|
||||||
|
a.trunc = true;
|
||||||
|
}
|
||||||
|
n *= 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a.count = w;
|
||||||
|
trim(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
shift_left :: proc(a: ^Decimal, k: uint) {
|
||||||
|
delta := int(k/4);
|
||||||
|
|
||||||
|
r := a.count; // read index
|
||||||
|
w := a.count+delta; // write index
|
||||||
|
|
||||||
|
n: uint;
|
||||||
|
for r -= 1; r >= 0; r -= 1 {
|
||||||
|
n += (uint(a.digits[r]) - '0') << k;
|
||||||
|
quo := n/10;
|
||||||
|
rem := n - 10*quo;
|
||||||
|
w -= 1;
|
||||||
|
if w < len(a.digits) {
|
||||||
|
a.digits[w] = u8('0' + rem);
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true;
|
||||||
|
}
|
||||||
|
n = quo;
|
||||||
|
}
|
||||||
|
|
||||||
|
for n > 0 {
|
||||||
|
quo := n/10;
|
||||||
|
rem := n - 10*quo;
|
||||||
|
w -= 1;
|
||||||
|
if 0 <= w && w < len(a.digits) {
|
||||||
|
a.digits[w] = u8('0' + rem);
|
||||||
|
} else if rem != 0 {
|
||||||
|
a.trunc = true;
|
||||||
|
}
|
||||||
|
n = quo;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.count += delta;
|
||||||
|
a.count = min(a.count, len(a.digits));
|
||||||
|
a.decimal_point += delta;
|
||||||
|
trim(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
shift :: proc(a: ^Decimal, k: int) {
|
||||||
|
uint_size :: 8*size_of(uint);
|
||||||
|
max_shift :: uint_size-4;
|
||||||
|
|
||||||
|
match {
|
||||||
|
case a.count == 0:
|
||||||
|
// no need to update
|
||||||
|
case k > 0:
|
||||||
|
for k > max_shift {
|
||||||
|
shift_left(a, max_shift);
|
||||||
|
k -= max_shift;
|
||||||
|
}
|
||||||
|
shift_left(a, uint(k));
|
||||||
|
|
||||||
|
|
||||||
|
case k < 0:
|
||||||
|
for k < -max_shift {
|
||||||
|
shift_right(a, max_shift);
|
||||||
|
k += max_shift;
|
||||||
|
}
|
||||||
|
shift_right(a, uint(-k));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 do return true;
|
||||||
|
return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.digits[nd] >= '5';
|
||||||
|
}
|
||||||
|
|
||||||
|
round :: proc(a: ^Decimal, nd: int) {
|
||||||
|
if nd < 0 || nd >= a.count { return; }
|
||||||
|
if can_round_up(a, nd) {
|
||||||
|
round_up(a, nd);
|
||||||
|
} else {
|
||||||
|
round_down(a, nd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
round_up :: proc(a: ^Decimal, nd: int) {
|
||||||
|
if nd < 0 || nd >= a.count { return; }
|
||||||
|
|
||||||
|
for i := nd-1; i >= 0; i -= 1 {
|
||||||
|
if c := a.digits[i]; c < '9' {
|
||||||
|
a.digits[i]+=1;
|
||||||
|
a.count = i+1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number is just 9s
|
||||||
|
a.digits[0] = '1';
|
||||||
|
a.count = 1;
|
||||||
|
a.decimal_point+=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
round_down :: proc(a: ^Decimal, nd: int) {
|
||||||
|
if nd < 0 || nd >= a.count { return; }
|
||||||
|
a.count = nd;
|
||||||
|
trim(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Extract integer part, rounded appropriately. There are no guarantees about overflow.
|
||||||
|
rounded_integer :: proc(a: ^Decimal) -> u64 {
|
||||||
|
if a.decimal_point > 20 {
|
||||||
|
return 0xffff_ffff_ffff_ffff;
|
||||||
|
}
|
||||||
|
i: int = 0;
|
||||||
|
n: u64 = 0;
|
||||||
|
m := min(a.decimal_point, a.count);
|
||||||
|
for ; i < m; i += 1 {
|
||||||
|
n = n*10 + u64(a.digits[i]-'0');
|
||||||
|
}
|
||||||
|
for ; i < a.decimal_point; i += 1 {
|
||||||
|
n *= 10;
|
||||||
|
}
|
||||||
|
if can_round_up(a, a.decimal_point) {
|
||||||
|
n+=1;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
+1057
-465
File diff suppressed because it is too large
Load Diff
+160
-122
@@ -1,170 +1,208 @@
|
|||||||
crc32 :: proc(data: rawptr, len: int) -> u32 {
|
import "mem.odin";
|
||||||
result := ~(0 as u32)
|
|
||||||
s := slice_ptr(data as ^u8, len)
|
crc32 :: proc(data: []u8) -> u32 {
|
||||||
for i := 0; i < len; i++ {
|
result := ~u32(0);
|
||||||
b := s[i] as u32
|
for b in data {
|
||||||
result = result>>8 ~ __CRC32_TABLE[(result ~ b) & 0xff]
|
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||||
}
|
}
|
||||||
return ~result
|
return ~result;
|
||||||
}
|
}
|
||||||
crc64 :: proc(data: rawptr, len: int) -> u64 {
|
crc64 :: proc(data: []u8) -> u64 {
|
||||||
result := ~(0 as u64)
|
result := ~u64(0);
|
||||||
s := slice_ptr(data as ^u8, len)
|
for b in data {
|
||||||
for i := 0; i < len; i++ {
|
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||||
b := s[i] as u64
|
|
||||||
result = result>>8 ~ __CRC64_TABLE[(result ~ b) & 0xff]
|
|
||||||
}
|
}
|
||||||
return ~result
|
return ~result;
|
||||||
}
|
}
|
||||||
|
|
||||||
fnv32 :: proc(data: rawptr, len: int) -> u32 {
|
fnv32 :: proc(data: []u8) -> u32 {
|
||||||
s := slice_ptr(data as ^u8, len)
|
h: u32 = 0x811c9dc5;
|
||||||
|
for b in data {
|
||||||
h: u32 = 0x811c9dc5
|
h = (h * 0x01000193) ~ u32(b);
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
h = (h * 0x01000193) ~ s[i] as u32
|
|
||||||
}
|
}
|
||||||
return h
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
fnv64 :: proc(data: rawptr, len: int) -> u64 {
|
fnv64 :: proc(data: []u8) -> u64 {
|
||||||
s := slice_ptr(data as ^u8, len)
|
h: u64 = 0xcbf29ce484222325;
|
||||||
|
for b in data {
|
||||||
h: u64 = 0xcbf29ce484222325
|
h = (h * 0x100000001b3) ~ u64(b);
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
h = (h * 0x100000001b3) ~ s[i] as u64
|
|
||||||
}
|
}
|
||||||
return h
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
fnv32a :: proc(data: rawptr, len: int) -> u32 {
|
fnv32a :: proc(data: []u8) -> u32 {
|
||||||
s := slice_ptr(data as ^u8, len)
|
h: u32 = 0x811c9dc5;
|
||||||
|
for b in data {
|
||||||
h: u32 = 0x811c9dc5
|
h = (h ~ u32(b)) * 0x01000193;
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
h = (h ~ s[i] as u32) * 0x01000193
|
|
||||||
}
|
}
|
||||||
return h
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
fnv64a :: proc(data: rawptr, len: int) -> u64 {
|
fnv64a :: proc(data: []u8) -> u64 {
|
||||||
s := slice_ptr(data as ^u8, len)
|
h: u64 = 0xcbf29ce484222325;
|
||||||
|
for b in data {
|
||||||
h: u64 = 0xcbf29ce484222325
|
h = (h ~ u64(b)) * 0x100000001b3;
|
||||||
for i := 0; i < len; i++ {
|
|
||||||
h = (h ~ s[i] as u64) * 0x100000001b3
|
|
||||||
}
|
}
|
||||||
return h
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
murmur32 :: proc(data: []u8) -> u32 {
|
||||||
|
c1_32: u32 : 0xcc9e2d51;
|
||||||
|
c2_32: u32 : 0x1b873593;
|
||||||
|
|
||||||
murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
h1: u32 = 0;
|
||||||
SEED :: 0x9747b28c
|
nblocks := len(data)/4;
|
||||||
|
p := &data[0];
|
||||||
|
p1 := p + 4*nblocks;
|
||||||
|
|
||||||
if size_of(int) == 8 {
|
for ; p < p1; p += 4 {
|
||||||
m :: 0xc6a4a7935bd1e995
|
k1 := (cast(^u32)p)^;
|
||||||
r :: 47
|
|
||||||
|
|
||||||
h: u64 = SEED ~ (len as u64 * m)
|
k1 *= c1_32;
|
||||||
|
k1 = (k1 << 15) | (k1 >> 17);
|
||||||
|
k1 *= c2_32;
|
||||||
|
|
||||||
data := slice_ptr(data_ as ^u64, len/size_of(u64))
|
h1 ~= k1;
|
||||||
data2 := slice_ptr(data_ as ^u8, len)
|
h1 = (h1 << 13) | (h1 >> 19);
|
||||||
|
h1 = h1*5 + 0xe6546b64;
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < data.count; i++ {
|
tail := data[nblocks*4 ..];
|
||||||
k := data[i]
|
k1: u32;
|
||||||
|
match len(tail)&3 {
|
||||||
|
case 3:
|
||||||
|
k1 ~= u32(tail[2]) << 16;
|
||||||
|
fallthrough;
|
||||||
|
case 2:
|
||||||
|
k1 ~= u32(tail[2]) << 8;
|
||||||
|
fallthrough;
|
||||||
|
case 1:
|
||||||
|
k1 ~= u32(tail[0]);
|
||||||
|
k1 *= c1_32;
|
||||||
|
k1 = (k1 << 15) | (k1 >> 17) ;
|
||||||
|
k1 *= c2_32;
|
||||||
|
h1 ~= k1;
|
||||||
|
}
|
||||||
|
|
||||||
k *= m
|
h1 ~= u32(len(data));
|
||||||
k ~= k>>r
|
|
||||||
k *= m
|
|
||||||
|
|
||||||
h ~= k
|
h1 ~= h1 >> 16;
|
||||||
h *= m
|
h1 *= 0x85ebca6b;
|
||||||
|
h1 ~= h1 >> 13;
|
||||||
|
h1 *= 0xc2b2ae35;
|
||||||
|
h1 ~= h1 >> 16;
|
||||||
|
|
||||||
|
return h1;
|
||||||
|
}
|
||||||
|
|
||||||
|
murmur64 :: proc(data: []u8) -> u64 {
|
||||||
|
SEED :: 0x9747b28c;
|
||||||
|
|
||||||
|
when size_of(int) == 8 {
|
||||||
|
m :: 0xc6a4a7935bd1e995;
|
||||||
|
r :: 47;
|
||||||
|
|
||||||
|
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||||
|
data64 := mem.slice_ptr(cast(^u64)&data[0], len(data)/size_of(u64));
|
||||||
|
|
||||||
|
for _, i in data64 {
|
||||||
|
k := data64[i];
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ~= k>>r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h ~= k;
|
||||||
|
h *= m;
|
||||||
}
|
}
|
||||||
|
|
||||||
match len & 7 {
|
match len(data)&7 {
|
||||||
case 7: h ~= data2[6] as u64 << 48; fallthrough
|
case 7: h ~= u64(data[6]) << 48; fallthrough;
|
||||||
case 6: h ~= data2[5] as u64 << 40; fallthrough
|
case 6: h ~= u64(data[5]) << 40; fallthrough;
|
||||||
case 5: h ~= data2[4] as u64 << 32; fallthrough
|
case 5: h ~= u64(data[4]) << 32; fallthrough;
|
||||||
case 4: h ~= data2[3] as u64 << 24; fallthrough
|
case 4: h ~= u64(data[3]) << 24; fallthrough;
|
||||||
case 3: h ~= data2[2] as u64 << 16; fallthrough
|
case 3: h ~= u64(data[2]) << 16; fallthrough;
|
||||||
case 2: h ~= data2[1] as u64 << 8; fallthrough
|
case 2: h ~= u64(data[1]) << 8; fallthrough;
|
||||||
case 1:
|
case 1:
|
||||||
h ~= data2[0] as u64
|
h ~= u64(data[0]);
|
||||||
h *= m
|
h *= m;
|
||||||
}
|
}
|
||||||
|
|
||||||
h ~= h>>r
|
h ~= h>>r;
|
||||||
h *= m
|
h *= m;
|
||||||
h ~= h>>r
|
h ~= h>>r;
|
||||||
|
|
||||||
return h
|
return h;
|
||||||
} else {
|
} else {
|
||||||
m :: 0x5bd1e995
|
m :: 0x5bd1e995;
|
||||||
r :: 24
|
r :: 24;
|
||||||
|
|
||||||
h1: u32 = SEED as u32 ~ len as u32
|
h1 := u32(SEED) ~ u32(len(data));
|
||||||
h2: u32 = SEED >> 32
|
h2 := u32(SEED) >> 32;
|
||||||
|
data32 := mem.slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||||
|
len := len(data);
|
||||||
|
i := 0;
|
||||||
|
|
||||||
data := slice_ptr(data_ as ^u32, len/size_of(u32))
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for len >= 8 {
|
for len >= 8 {
|
||||||
k1, k2: u32
|
k1, k2: u32;
|
||||||
k1 = data[i]; i++
|
k1 = data32[i]; i += 1;
|
||||||
k1 *= m
|
k1 *= m;
|
||||||
k1 ~= k1>>r
|
k1 ~= k1>>r;
|
||||||
k1 *= m
|
k1 *= m;
|
||||||
h1 *= m
|
h1 *= m;
|
||||||
h1 ~= k1
|
h1 ~= k1;
|
||||||
len -= 4
|
len -= 4;
|
||||||
|
|
||||||
k2 = data[i]; i++
|
k2 = data32[i]; i += 1;
|
||||||
k2 *= m
|
k2 *= m;
|
||||||
k2 ~= k2>>r
|
k2 ~= k2>>r;
|
||||||
k2 *= m
|
k2 *= m;
|
||||||
h2 *= m
|
h2 *= m;
|
||||||
h2 ~= k2
|
h2 ~= k2;
|
||||||
len -= 4
|
len -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len >= 4) {
|
if len >= 4 {
|
||||||
k1: u32
|
k1: u32;
|
||||||
k1 = data[i]; i++
|
k1 = data32[i]; i += 1;
|
||||||
k1 *= m
|
k1 *= m;
|
||||||
k1 ~= k1>>r
|
k1 ~= k1>>r;
|
||||||
k1 *= m
|
k1 *= m;
|
||||||
h1 *= m
|
h1 *= m;
|
||||||
h1 ~= k1
|
h1 ~= k1;
|
||||||
len -= 4
|
len -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
data8 := slice_ptr((data.data+i) as ^u8, 3) // NOTE(bill): This is unsafe
|
// TODO(bill): Fix this
|
||||||
|
#no_bounds_check data8 := slice_to_bytes(data32[i..])[..3];
|
||||||
match len {
|
match len {
|
||||||
case 3: h2 ~= data8[2] as u32 << 16; fallthrough
|
case 3:
|
||||||
case 2: h2 ~= data8[1] as u32 << 8; fallthrough
|
h2 ~= u32(data8[2]) << 16;
|
||||||
|
fallthrough;
|
||||||
|
case 2:
|
||||||
|
h2 ~= u32(data8[1]) << 8;
|
||||||
|
fallthrough;
|
||||||
case 1:
|
case 1:
|
||||||
h2 ~= data8[0] as u32
|
h2 ~= u32(data8[0]);
|
||||||
h2 *= m
|
h2 *= m;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 ~= h2>>18
|
h1 ~= h2>>18;
|
||||||
h1 *= m
|
h1 *= m;
|
||||||
h2 ~= h1>>22
|
h2 ~= h1>>22;
|
||||||
h2 *= m
|
h2 *= m;
|
||||||
h1 ~= h2>>17
|
h1 ~= h2>>17;
|
||||||
h1 *= m
|
h1 *= m;
|
||||||
h2 ~= h1>>19
|
h2 ~= h1>>19;
|
||||||
h2 *= m
|
h2 *= m;
|
||||||
|
|
||||||
h := (h1 as u64)<<32 | h2 as u64
|
return u64(h1)<<32 | u64(h2);
|
||||||
return h
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_crc32_table := [256]u32{
|
||||||
__CRC32_TABLE := [256]u32{
|
|
||||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||||
@@ -229,8 +267,8 @@ __CRC32_TABLE := [256]u32{
|
|||||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||||
}
|
};
|
||||||
__CRC64_TABLE := [256]u64{
|
_crc64_table := [256]u64{
|
||||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||||
@@ -295,4 +333,4 @@ __CRC64_TABLE := [256]u64{
|
|||||||
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||||
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||||
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||||
}
|
};
|
||||||
|
|||||||
+277
-271
@@ -1,141 +1,152 @@
|
|||||||
TAU :: 6.28318530717958647692528676655900576
|
TAU :: 6.28318530717958647692528676655900576;
|
||||||
PI :: 3.14159265358979323846264338327950288
|
PI :: 3.14159265358979323846264338327950288;
|
||||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448
|
ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
|
||||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362
|
ONE_OVER_PI :: 0.159154943091895335768883763372514362;
|
||||||
|
|
||||||
E :: 2.71828182845904523536
|
E :: 2.71828182845904523536;
|
||||||
SQRT_TWO :: 1.41421356237309504880168872420969808
|
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||||
SQRT_THREE :: 1.73205080756887729352744634150587236
|
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||||
SQRT_FIVE :: 2.23606797749978969640917366873127623
|
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||||
|
|
||||||
LOG_TWO :: 0.693147180559945309417232121458176568
|
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||||
LOG_TEN :: 2.30258509299404568401799145468436421
|
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||||
|
|
||||||
EPSILON :: 1.19209290e-7
|
EPSILON :: 1.19209290e-7;
|
||||||
|
|
||||||
τ :: TAU
|
τ :: TAU;
|
||||||
π :: PI
|
π :: PI;
|
||||||
|
|
||||||
|
Vec2 :: [vector 2]f32;
|
||||||
|
Vec3 :: [vector 3]f32;
|
||||||
|
Vec4 :: [vector 4]f32;
|
||||||
|
|
||||||
Vec2 :: type {2}f32
|
// Column major
|
||||||
Vec3 :: type {3}f32
|
Mat2 :: [2][2]f32;
|
||||||
Vec4 :: type {4}f32
|
Mat3 :: [3][3]f32;
|
||||||
|
Mat4 :: [4][4]f32;
|
||||||
|
|
||||||
Mat2 :: type [2]Vec2
|
Complex :: complex64;
|
||||||
Mat3 :: type [3]Vec3
|
|
||||||
Mat4 :: type [4]Vec4
|
|
||||||
|
|
||||||
|
foreign __llvm_core {
|
||||||
|
sqrt :: proc(x: f32) -> f32 #link_name "llvm.sqrt.f32" ---;
|
||||||
|
sqrt :: proc(x: f64) -> f64 #link_name "llvm.sqrt.f64" ---;
|
||||||
|
|
||||||
sqrt32 :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
|
sin :: proc(θ: f32) -> f32 #link_name "llvm.sin.f32" ---;
|
||||||
sqrt64 :: proc(x: f64) -> f64 #foreign "llvm.sqrt.f64"
|
sin :: proc(θ: f64) -> f64 #link_name "llvm.sin.f64" ---;
|
||||||
|
|
||||||
sin32 :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
|
cos :: proc(θ: f32) -> f32 #link_name "llvm.cos.f32" ---;
|
||||||
sin64 :: proc(x: f64) -> f64 #foreign "llvm.sin.f64"
|
cos :: proc(θ: f64) -> f64 #link_name "llvm.cos.f64" ---;
|
||||||
|
|
||||||
cos32 :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
|
pow :: proc(x, power: f32) -> f32 #link_name "llvm.pow.f32" ---;
|
||||||
cos64 :: proc(x: f64) -> f64 #foreign "llvm.cos.f64"
|
pow :: proc(x, power: f64) -> f64 #link_name "llvm.pow.f64" ---;
|
||||||
|
|
||||||
tan32 :: proc(x: f32) -> f32 #inline { return sin32(x)/cos32(x) }
|
fmuladd :: proc(a, b, c: f32) -> f32 #link_name "llvm.fmuladd.f32" ---;
|
||||||
tan64 :: proc(x: f64) -> f64 #inline { return sin64(x)/cos64(x) }
|
fmuladd :: proc(a, b, c: f64) -> f64 #link_name "llvm.fmuladd.f64" ---;
|
||||||
|
|
||||||
lerp32 :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t }
|
|
||||||
lerp64 :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t }
|
|
||||||
|
|
||||||
clamp32 :: proc(x, lower, upper: f32) -> f32 { return min(max(x, lower), upper) }
|
|
||||||
clamp64 :: proc(x, lower, upper: f64) -> f64 { return min(max(x, lower), upper) }
|
|
||||||
|
|
||||||
sign32 :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 }
|
|
||||||
sign64 :: proc(x: f64) -> f64 { if x >= 0 { return +1 } return -1 }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
copy_sign32 :: proc(x, y: f32) -> f32 {
|
|
||||||
ix := x transmute u32
|
|
||||||
iy := y transmute u32
|
|
||||||
ix &= 0x7fffffff
|
|
||||||
ix |= iy & 0x80000000
|
|
||||||
return ix transmute f32
|
|
||||||
}
|
}
|
||||||
round32 :: proc(x: f32) -> f32 {
|
|
||||||
if x >= 0 {
|
tan :: proc(θ: f32) -> f32 #inline do return sin(θ)/cos(θ);
|
||||||
return floor32(x + 0.5)
|
tan :: proc(θ: f64) -> f64 #inline do return sin(θ)/cos(θ);
|
||||||
|
|
||||||
|
|
||||||
|
lerp :: proc(a, b, t: f32) -> (x: f32) do return a*(1-t) + b*t;
|
||||||
|
lerp :: proc(a, b, t: f64) -> (x: f64) do return a*(1-t) + b*t;
|
||||||
|
unlerp :: proc(a, b, x: f32) -> (t: f32) do return (x-a)/(b-a);
|
||||||
|
unlerp :: proc(a, b, x: f64) -> (t: f64) do return (x-a)/(b-a);
|
||||||
|
|
||||||
|
|
||||||
|
sign :: proc(x: f32) -> f32 { if x >= 0 do return +1; return -1; }
|
||||||
|
sign :: proc(x: f64) -> f64 { if x >= 0 do return +1; return -1; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
|
||||||
|
round :: proc(x: f64) -> f64 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
|
||||||
|
|
||||||
|
floor :: proc(x: f32) -> f32 { if x >= 0 do return f32(i64(x)); return f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||||
|
floor :: proc(x: f64) -> f64 { if x >= 0 do return f64(i64(x)); return f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||||
|
|
||||||
|
ceil :: proc(x: f32) -> f32 { if x < 0 do return f32(i64(x)); return f32(i64(x+1)); }// TODO: Get accurate versions
|
||||||
|
ceil :: proc(x: f64) -> f64 { if x < 0 do return f64(i64(x)); return f64(i64(x+1)); }// TODO: Get accurate versions
|
||||||
|
|
||||||
|
remainder :: proc(x, y: f32) -> f32 do return x - round(x/y) * y;
|
||||||
|
remainder :: proc(x, y: f64) -> f64 do return x - round(x/y) * y;
|
||||||
|
|
||||||
|
mod :: proc(x, y: f32) -> f32 {
|
||||||
|
result: f32;
|
||||||
|
y = abs(y);
|
||||||
|
result = remainder(abs(x), y);
|
||||||
|
if sign(result) < 0 {
|
||||||
|
result += y;
|
||||||
}
|
}
|
||||||
return ceil32(x - 0.5)
|
return copy_sign(result, x);
|
||||||
}
|
}
|
||||||
floor32 :: proc(x: f32) -> f32 {
|
mod :: proc(x, y: f64) -> f64 {
|
||||||
if x >= 0 {
|
result: f64;
|
||||||
return x as int as f32
|
y = abs(y);
|
||||||
|
result = remainder(abs(x), y);
|
||||||
|
if sign(result) < 0 {
|
||||||
|
result += y;
|
||||||
}
|
}
|
||||||
return (x-0.5) as int as f32
|
return copy_sign(result, x);
|
||||||
}
|
|
||||||
ceil32 :: proc(x: f32) -> f32 {
|
|
||||||
if x < 0 {
|
|
||||||
return x as int as f32
|
|
||||||
}
|
|
||||||
return ((x as int)+1) as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder32 :: proc(x, y: f32) -> f32 {
|
|
||||||
return x - round32(x/y) * y
|
|
||||||
}
|
|
||||||
|
|
||||||
fmod32 :: proc(x, y: f32) -> f32 {
|
|
||||||
y = abs(y)
|
|
||||||
result := remainder32(abs(x), y)
|
|
||||||
if sign32(result) < 0 {
|
|
||||||
result += y
|
|
||||||
}
|
|
||||||
return copy_sign32(result, x)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360 }
|
to_radians :: proc(degrees: f32) -> f32 do return degrees * TAU / 360;
|
||||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU }
|
to_degrees :: proc(radians: f32) -> f32 do return radians * 360 / TAU;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
dot :: proc(a, b: $T/[vector 2]$E) -> E { c := a*b; return c.x + c.y; }
|
||||||
|
dot :: proc(a, b: $T/[vector 3]$E) -> E { c := a*b; return c.x + c.y + c.z; }
|
||||||
|
dot :: proc(a, b: $T/[vector 4]$E) -> E { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||||
|
|
||||||
dot2 :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y }
|
cross :: proc(x, y: $T/[vector 3]$E) -> T {
|
||||||
dot3 :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z }
|
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||||
dot4 :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w }
|
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||||
|
return T(a - b);
|
||||||
cross3 :: 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
vec2_mag :: proc(v: Vec2) -> f32 { return sqrt32(dot2(v, v)) }
|
mag :: proc(v: $T/[vector 2]$E) -> E do return sqrt(dot(v, v));
|
||||||
vec3_mag :: proc(v: Vec3) -> f32 { return sqrt32(dot3(v, v)) }
|
mag :: proc(v: $T/[vector 3]$E) -> E do return sqrt(dot(v, v));
|
||||||
vec4_mag :: proc(v: Vec4) -> f32 { return sqrt32(dot4(v, v)) }
|
mag :: proc(v: $T/[vector 4]$E) -> E do return sqrt(dot(v, v));
|
||||||
|
|
||||||
vec2_norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{vec2_mag(v)} }
|
norm :: proc(v: $T/[vector 2]$E) -> T do return v / mag(v);
|
||||||
vec3_norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{vec3_mag(v)} }
|
norm :: proc(v: $T/[vector 3]$E) -> T do return v / mag(v);
|
||||||
vec4_norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{vec4_mag(v)} }
|
norm :: proc(v: $T/[vector 4]$E) -> T do return v / mag(v);
|
||||||
|
|
||||||
vec2_norm0 :: proc(v: Vec2) -> Vec2 {
|
norm0 :: proc(v: $T/[vector 2]$E) -> T {
|
||||||
m := vec2_mag(v)
|
m := mag(v);
|
||||||
if m == 0 {
|
if m == 0 do return 0;
|
||||||
return Vec2{0}
|
return v/m;
|
||||||
}
|
|
||||||
return v / Vec2{m}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3_norm0 :: proc(v: Vec3) -> Vec3 {
|
norm0 :: proc(v: $T/[vector 3]$E) -> T {
|
||||||
m := vec3_mag(v)
|
m := mag(v);
|
||||||
if m == 0 {
|
if m == 0 do return 0;
|
||||||
return Vec3{0}
|
return v/m;
|
||||||
}
|
|
||||||
return v / Vec3{m}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4_norm0 :: proc(v: Vec4) -> Vec4 {
|
norm0 :: proc(v: $T/[vector 4]$E) -> T {
|
||||||
m := vec4_mag(v)
|
m := mag(v);
|
||||||
if m == 0 {
|
if m == 0 do return 0;
|
||||||
return Vec4{0}
|
return v/m;
|
||||||
}
|
|
||||||
return v / Vec4{m}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -146,228 +157,223 @@ mat4_identity :: proc() -> Mat4 {
|
|||||||
{0, 1, 0, 0},
|
{0, 1, 0, 0},
|
||||||
{0, 0, 1, 0},
|
{0, 0, 1, 0},
|
||||||
{0, 0, 0, 1},
|
{0, 0, 0, 1},
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||||
for j := 0; j < 4; j++ {
|
for j in 0..4 {
|
||||||
for i := 0; i < 4; i++ {
|
for i in 0..4 {
|
||||||
m[i][j], m[j][i] = m[j][i], m[i][j]
|
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
|
mul :: proc(a, b: Mat4) -> Mat4 {
|
||||||
c: Mat4
|
c: Mat4;
|
||||||
for j := 0; j < 4; j++ {
|
for j in 0..4 {
|
||||||
for i := 0; i < 4; i++ {
|
for i in 0..4 {
|
||||||
c[j][i] = a[0][i]*b[j][0]
|
c[j][i] = a[0][i]*b[j][0] +
|
||||||
+ a[1][i]*b[j][1]
|
a[1][i]*b[j][1] +
|
||||||
+ a[2][i]*b[j][2]
|
a[2][i]*b[j][2] +
|
||||||
+ a[3][i]*b[j][3]
|
a[3][i]*b[j][3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||||
return 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][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][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][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,
|
m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_inverse :: proc(m: Mat4) -> Mat4 {
|
inverse :: proc(m: Mat4) -> Mat4 {
|
||||||
o: Mat4
|
o: Mat4;
|
||||||
|
|
||||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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]
|
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[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04)
|
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05)
|
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05)
|
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||||
|
|
||||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08)
|
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10)
|
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12)
|
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12)
|
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||||
|
|
||||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15)
|
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17)
|
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18)
|
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18)
|
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] +
|
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||||
m[0][1] * o[0][1] +
|
m[0][1] * o[0][1] +
|
||||||
m[0][2] * o[0][2] +
|
m[0][2] * o[0][2] +
|
||||||
m[0][3] * o[0][3])
|
m[0][3] * o[0][3]);
|
||||||
|
|
||||||
o[0][0] *= ood
|
o[0][0] *= ood;
|
||||||
o[0][1] *= ood
|
o[0][1] *= ood;
|
||||||
o[0][2] *= ood
|
o[0][2] *= ood;
|
||||||
o[0][3] *= ood
|
o[0][3] *= ood;
|
||||||
o[1][0] *= ood
|
o[1][0] *= ood;
|
||||||
o[1][1] *= ood
|
o[1][1] *= ood;
|
||||||
o[1][2] *= ood
|
o[1][2] *= ood;
|
||||||
o[1][3] *= ood
|
o[1][3] *= ood;
|
||||||
o[2][0] *= ood
|
o[2][0] *= ood;
|
||||||
o[2][1] *= ood
|
o[2][1] *= ood;
|
||||||
o[2][2] *= ood
|
o[2][2] *= ood;
|
||||||
o[2][3] *= ood
|
o[2][3] *= ood;
|
||||||
o[3][0] *= ood
|
o[3][0] *= ood;
|
||||||
o[3][1] *= ood
|
o[3][1] *= ood;
|
||||||
o[3][2] *= ood
|
o[3][2] *= ood;
|
||||||
o[3][3] *= ood
|
o[3][3] *= ood;
|
||||||
|
|
||||||
return o
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||||
m := mat4_identity()
|
m := mat4_identity();
|
||||||
m[3][0] = v.x
|
m[3][0] = v.x;
|
||||||
m[3][1] = v.y
|
m[3][1] = v.y;
|
||||||
m[3][2] = v.z
|
m[3][2] = v.z;
|
||||||
m[3][3] = 1
|
m[3][3] = 1;
|
||||||
return m
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||||
c := cos32(angle_radians)
|
c := cos(angle_radians);
|
||||||
s := sin32(angle_radians)
|
s := sin(angle_radians);
|
||||||
|
|
||||||
a := vec3_norm(v)
|
a := norm(v);
|
||||||
t := a * Vec3{1-c}
|
t := a * (1-c);
|
||||||
|
|
||||||
rot := mat4_identity()
|
rot := mat4_identity();
|
||||||
|
|
||||||
rot[0][0] = c + t.x*a.x
|
rot[0][0] = c + t.x*a.x;
|
||||||
rot[0][1] = 0 + t.x*a.y + s*a.z
|
rot[0][1] = 0 + t.x*a.y + s*a.z;
|
||||||
rot[0][2] = 0 + t.x*a.z - s*a.y
|
rot[0][2] = 0 + t.x*a.z - s*a.y;
|
||||||
rot[0][3] = 0
|
rot[0][3] = 0;
|
||||||
|
|
||||||
rot[1][0] = 0 + t.y*a.x - s*a.z
|
rot[1][0] = 0 + t.y*a.x - s*a.z;
|
||||||
rot[1][1] = c + t.y*a.y
|
rot[1][1] = c + t.y*a.y;
|
||||||
rot[1][2] = 0 + t.y*a.z + s*a.x
|
rot[1][2] = 0 + t.y*a.z + s*a.x;
|
||||||
rot[1][3] = 0
|
rot[1][3] = 0;
|
||||||
|
|
||||||
rot[2][0] = 0 + t.z*a.x + s*a.y
|
rot[2][0] = 0 + t.z*a.x + s*a.y;
|
||||||
rot[2][1] = 0 + t.z*a.y - s*a.x
|
rot[2][1] = 0 + t.z*a.y - s*a.x;
|
||||||
rot[2][2] = c + t.z*a.z
|
rot[2][2] = c + t.z*a.z;
|
||||||
rot[2][3] = 0
|
rot[2][3] = 0;
|
||||||
|
|
||||||
return rot
|
return rot;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||||
m[0][0] = v.x
|
m[0][0] *= v.x;
|
||||||
m[1][1] = v.y
|
m[1][1] *= v.y;
|
||||||
m[2][2] = v.z
|
m[2][2] *= v.z;
|
||||||
return m
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
mat4_scalef :: proc(m: Mat4, s: f32) -> Mat4 {
|
scale :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||||
m[0][0] = s
|
m[0][0] *= s;
|
||||||
m[1][1] = s
|
m[1][1] *= s;
|
||||||
m[2][2] = s
|
m[2][2] *= s;
|
||||||
return m
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mat4_look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||||
f := vec3_norm(centre - eye)
|
f := norm(centre - eye);
|
||||||
s := vec3_norm(cross3(f, up))
|
s := norm(cross(f, up));
|
||||||
u := cross3(s, f)
|
u := cross(s, f);
|
||||||
|
|
||||||
m: Mat4
|
return Mat4{
|
||||||
|
{+s.x, +u.x, -f.x, 0},
|
||||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0}
|
{+s.y, +u.y, -f.y, 0},
|
||||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0}
|
{+s.z, +u.z, -f.z, 0},
|
||||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0}
|
{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
|
||||||
m[3] = Vec4{dot3(s, eye), dot3(u, eye), dot3(f, eye), 1}
|
};
|
||||||
|
|
||||||
return m
|
|
||||||
}
|
}
|
||||||
mat4_perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
|
||||||
m: Mat4
|
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||||
tan_half_fovy := tan32(0.5 * fovy)
|
m: Mat4;
|
||||||
m[0][0] = 1.0 / (aspect*tan_half_fovy)
|
tan_half_fovy := tan(0.5 * fovy);
|
||||||
m[1][1] = 1.0 / (tan_half_fovy)
|
|
||||||
m[2][2] = -(far + near) / (far - near)
|
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||||
m[2][3] = -1.0
|
m[1][1] = 1.0 / (tan_half_fovy);
|
||||||
m[3][2] = -2.0*far*near / (far - near)
|
m[2][2] = -(far + near) / (far - near);
|
||||||
return m
|
m[2][3] = -1.0;
|
||||||
|
m[3][2] = -2.0*far*near / (far - near);
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mat4_ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||||
m := mat4_identity()
|
m := mat4_identity();
|
||||||
|
m[0][0] = +2.0 / (right - left);
|
||||||
m[0][0] = +2.0 / (right - left)
|
m[1][1] = +2.0 / (top - bottom);
|
||||||
m[1][1] = +2.0 / (top - bottom)
|
m[2][2] = -2.0 / (far - near);
|
||||||
m[2][2] = -2.0 / (far - near)
|
m[3][0] = -(right + left) / (right - left);
|
||||||
m[3][0] = -(right + left) / (right - left)
|
m[3][1] = -(top + bottom) / (top - bottom);
|
||||||
m[3][1] = -(top + bottom) / (top - bottom)
|
m[3][2] = -(far + near) / (far - near);
|
||||||
m[3][2] = -(far + near) / (far - near)
|
return m;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
F32_DIG :: 6
|
F64_DIG :: 15; // # of decimal digits of precision
|
||||||
F32_EPSILON :: 1.192092896e-07
|
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||||
F32_GUARD :: 0
|
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||||
F32_MANT_DIG :: 24
|
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||||
F32_MAX :: 3.402823466e+38
|
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||||
F32_MAX_10_EXP :: 38
|
F64_MAX_EXP :: 1024; // max binary exponent
|
||||||
F32_MAX_EXP :: 128
|
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||||
F32_MIN :: 1.175494351e-38
|
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||||
F32_MIN_10_EXP :: -37
|
F64_MIN_EXP :: -1021; // min binary exponent
|
||||||
F32_MIN_EXP :: -125
|
F64_RADIX :: 2; // exponent radix
|
||||||
F32_NORMALIZE :: 0
|
F64_ROUNDS :: 1; // addition rounding: near
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+201
-219
@@ -1,111 +1,90 @@
|
|||||||
#import "fmt.odin"
|
import (
|
||||||
#import "os.odin"
|
"fmt.odin";
|
||||||
|
"os.odin";
|
||||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
|
"raw.odin";
|
||||||
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
|
)
|
||||||
llvm_memset_64bit(data, value as byte, len, 1, false)
|
foreign __llvm_core {
|
||||||
return data
|
swap :: proc(b: u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
||||||
|
swap :: proc(b: u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
||||||
|
swap :: proc(b: u64) -> u64 #link_name "llvm.bswap.i64" ---;
|
||||||
}
|
}
|
||||||
|
|
||||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless {
|
||||||
return set(data, 0, len)
|
return __mem_set(data, value, len);
|
||||||
}
|
}
|
||||||
|
zero :: proc(data: rawptr, len: int) -> rawptr #cc_contextless {
|
||||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
|
return __mem_zero(data, len);
|
||||||
// NOTE(bill): This _must_ implemented like C's memmove
|
|
||||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64"
|
|
||||||
llvm_memmove_64bit(dst, src, len, 1, false)
|
|
||||||
return dst
|
|
||||||
}
|
}
|
||||||
|
copy :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
|
||||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
|
return __mem_copy(dst, src, len);
|
||||||
// NOTE(bill): This _must_ implemented like C's memcpy
|
}
|
||||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64"
|
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
|
||||||
llvm_memcpy_64bit(dst, src, len, 1, false)
|
return __mem_copy_non_overlapping(dst, src, len);
|
||||||
return dst
|
}
|
||||||
|
compare :: proc(a, b: []u8) -> int #cc_contextless {
|
||||||
|
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
compare :: proc(dst, src: rawptr, n: int) -> int #link_name "__mem_compare" {
|
slice_ptr :: proc(ptr: ^$T, len: int) -> []T #cc_contextless {
|
||||||
// Translation of http://mgronhol.github.io/fast-strcmp/
|
assert(len >= 0);
|
||||||
a := slice_ptr(dst as ^byte, n)
|
slice := raw.Slice{data = ptr, len = len, cap = len};
|
||||||
b := slice_ptr(src as ^byte, n)
|
return (cast(^[]T)&slice)^;
|
||||||
|
}
|
||||||
|
slice_ptr :: proc(ptr: ^$T, len, cap: int) -> []T #cc_contextless {
|
||||||
|
assert(0 <= len && len <= cap);
|
||||||
|
slice := raw.Slice{data = ptr, len = len, cap = cap};
|
||||||
|
return (cast(^[]T)&slice)^;
|
||||||
|
}
|
||||||
|
|
||||||
fast := n/size_of(int) + 1
|
slice_to_bytes :: proc(slice: []$T) -> []u8 #cc_contextless {
|
||||||
offset := (fast-1)*size_of(int)
|
s := cast(^raw.Slice)&slice;
|
||||||
curr_block := 0
|
s.len *= size_of(T);
|
||||||
if n <= size_of(int) {
|
s.cap *= size_of(T);
|
||||||
fast = 0
|
return (cast(^[]u8)s)^;
|
||||||
}
|
|
||||||
|
|
||||||
la := slice_ptr(^a[0] as ^int, fast)
|
|
||||||
lb := slice_ptr(^b[0] as ^int, fast)
|
|
||||||
|
|
||||||
for ; curr_block < fast; curr_block++ {
|
|
||||||
if (la[curr_block] ~ lb[curr_block]) != 0 {
|
|
||||||
for pos := curr_block*size_of(int); pos < n; pos++ {
|
|
||||||
if (a[pos] ~ b[pos]) != 0 {
|
|
||||||
return a[pos] as int - b[pos] as int
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; offset < n; offset++ {
|
|
||||||
if (a[offset] ~ b[offset]) != 0 {
|
|
||||||
return a[offset] as int - b[offset] as int
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kilobytes :: proc(x: int) -> int #inline #cc_contextless { return (x) * 1024; }
|
||||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024 }
|
megabytes :: proc(x: int) -> int #inline #cc_contextless { return kilobytes(x) * 1024; }
|
||||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024 }
|
gigabytes :: proc(x: int) -> int #inline #cc_contextless { return megabytes(x) * 1024; }
|
||||||
gigabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024 }
|
terabytes :: proc(x: int) -> int #inline #cc_contextless { return gigabytes(x) * 1024; }
|
||||||
terabytes :: proc(x: int) -> int #inline { return terabytes(x) * 1024 }
|
|
||||||
|
|
||||||
is_power_of_two :: proc(x: int) -> bool {
|
is_power_of_two :: proc(x: int) -> bool {
|
||||||
if x <= 0 {
|
if x <= 0 do return false;
|
||||||
return false
|
return (x & (x-1)) == 0;
|
||||||
}
|
|
||||||
return (x & (x-1)) == 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
||||||
assert(is_power_of_two(align))
|
assert(is_power_of_two(align));
|
||||||
|
|
||||||
a := align as uint
|
a := uint(align);
|
||||||
p := ptr as uint
|
p := uint(ptr);
|
||||||
modulo := p & (a-1)
|
modulo := p & (a-1);
|
||||||
if modulo != 0 {
|
if modulo != 0 do p += a - modulo;
|
||||||
p += a - modulo
|
return rawptr(p);
|
||||||
}
|
|
||||||
return p as rawptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AllocationHeader :: struct {
|
AllocationHeader :: struct {
|
||||||
size: int
|
size: int;
|
||||||
}
|
}
|
||||||
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
|
||||||
header.size = size
|
|
||||||
ptr := (header+1) as ^int
|
|
||||||
|
|
||||||
for i := 0; ptr as rawptr < data; i++ {
|
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
||||||
(ptr+i)^ = -1
|
header.size = size;
|
||||||
|
ptr := cast(^uint)(header+1);
|
||||||
|
n := cast(^uint)data - ptr;
|
||||||
|
|
||||||
|
for i in 0..n {
|
||||||
|
(ptr+i)^ = ~uint(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||||
p := data as ^int
|
if data == nil do return nil;
|
||||||
for (p-1)^ == -1 {
|
p := cast(^uint)data;
|
||||||
p = (p-1)
|
for (p-1)^ == ~uint(0) do p = (p-1);
|
||||||
}
|
return cast(^AllocationHeader)(p-1);
|
||||||
return (p as ^AllocationHeader)-1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -115,37 +94,39 @@ allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
|||||||
// Custom allocators
|
// Custom allocators
|
||||||
|
|
||||||
Arena :: struct {
|
Arena :: struct {
|
||||||
backing: Allocator
|
backing: Allocator;
|
||||||
memory: []byte
|
offset: int;
|
||||||
temp_count: int
|
memory: []u8;
|
||||||
|
temp_count: int;
|
||||||
|
}
|
||||||
|
|
||||||
Temp_Memory :: struct {
|
ArenaTempMemory :: struct {
|
||||||
arena: ^Arena
|
arena: ^Arena;
|
||||||
original_count: int
|
original_count: int;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
init_arena_from_memory :: proc(using a: ^Arena, data: []u8) {
|
||||||
backing = Allocator{}
|
backing = Allocator{};
|
||||||
memory = data[:0]
|
memory = data[..0];
|
||||||
temp_count = 0
|
temp_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||||
backing = context.allocator
|
backing = context.allocator;
|
||||||
memory = new_slice(byte, 0, size)
|
memory = make([]u8, size);
|
||||||
temp_count = 0
|
temp_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_arena :: proc(using a: ^Arena) {
|
destroy_arena :: proc(using a: ^Arena) {
|
||||||
if backing.procedure != nil {
|
if backing.procedure != nil {
|
||||||
push_allocator backing {
|
push_allocator backing {
|
||||||
free(memory.data)
|
free(memory);
|
||||||
memory = memory[0:0:0]
|
memory = nil;
|
||||||
|
offset = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,57 +135,57 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
|||||||
return Allocator{
|
return Allocator{
|
||||||
procedure = arena_allocator_proc,
|
procedure = arena_allocator_proc,
|
||||||
data = arena,
|
data = arena,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
|
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||||
arena := allocator_data as ^Arena
|
using Allocator.Mode;
|
||||||
|
arena := cast(^Arena)allocator_data;
|
||||||
|
|
||||||
using Allocator.Mode
|
|
||||||
match mode {
|
match mode {
|
||||||
case ALLOC:
|
case Alloc:
|
||||||
total_size := size + alignment
|
total_size := size + alignment;
|
||||||
|
|
||||||
if arena.memory.count + total_size > arena.memory.capacity {
|
if arena.offset + total_size > len(arena.memory) {
|
||||||
fmt.fprintln(os.stderr, "Arena out of memory")
|
fmt.fprintln(os.stderr, "Arena out of memory");
|
||||||
return nil
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
#no_bounds_check end := ^arena.memory[arena.memory.count]
|
#no_bounds_check end := &arena.memory[arena.offset];
|
||||||
|
|
||||||
ptr := align_forward(end, alignment)
|
ptr := align_forward(end, alignment);
|
||||||
arena.memory.count += total_size
|
arena.offset += total_size;
|
||||||
return zero(ptr, size)
|
return zero(ptr, size);
|
||||||
|
|
||||||
case FREE:
|
case Free:
|
||||||
// NOTE(bill): Free all at once
|
// NOTE(bill): Free all at once
|
||||||
// Use Arena.Temp_Memory if you want to free a block
|
// Use ArenaTempMemory if you want to free a block
|
||||||
|
|
||||||
case FREE_ALL:
|
case FreeAll:
|
||||||
arena.memory.count = 0
|
arena.offset = 0;
|
||||||
|
|
||||||
case RESIZE:
|
case Resize:
|
||||||
return default_resize_align(old_memory, old_size, size, alignment)
|
return default_resize_align(old_memory, old_size, size, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena.Temp_Memory {
|
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
|
||||||
tmp: Arena.Temp_Memory
|
tmp: ArenaTempMemory;
|
||||||
tmp.arena = a
|
tmp.arena = a;
|
||||||
tmp.original_count = a.memory.count
|
tmp.original_count = len(a.memory);
|
||||||
a.temp_count++
|
a.temp_count += 1;
|
||||||
return tmp
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) {
|
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
|
||||||
assert(arena.memory.count >= original_count)
|
assert(len(arena.memory) >= original_count);
|
||||||
assert(arena.temp_count > 0)
|
assert(arena.temp_count > 0);
|
||||||
arena.memory.count = original_count
|
arena.memory = arena.memory[..original_count];
|
||||||
arena.temp_count--
|
arena.temp_count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -214,119 +195,120 @@ end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) {
|
|||||||
|
|
||||||
|
|
||||||
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||||
WORD_SIZE :: size_of(int)
|
prev_pow2 :: proc(n: i64) -> i64 {
|
||||||
using Type_Info
|
if n <= 0 do return 0;
|
||||||
|
n |= n >> 1;
|
||||||
match type info : type_info {
|
n |= n >> 2;
|
||||||
case Named:
|
n |= n >> 4;
|
||||||
return align_of_type_info(info.base)
|
n |= n >> 8;
|
||||||
case Integer:
|
n |= n >> 16;
|
||||||
return info.size
|
n |= n >> 32;
|
||||||
case Float:
|
return n - (n >> 1);
|
||||||
return info.size
|
|
||||||
case String:
|
|
||||||
return WORD_SIZE
|
|
||||||
case Boolean:
|
|
||||||
return 1
|
|
||||||
case Pointer:
|
|
||||||
return WORD_SIZE
|
|
||||||
case Maybe:
|
|
||||||
return max(align_of_type_info(info.elem), 1)
|
|
||||||
case Procedure:
|
|
||||||
return WORD_SIZE
|
|
||||||
case Array:
|
|
||||||
return align_of_type_info(info.elem)
|
|
||||||
case Slice:
|
|
||||||
return WORD_SIZE
|
|
||||||
case Vector:
|
|
||||||
return align_of_type_info(info.elem)
|
|
||||||
case Struct:
|
|
||||||
return info.align
|
|
||||||
case Union:
|
|
||||||
return info.align
|
|
||||||
case Raw_Union:
|
|
||||||
return info.align
|
|
||||||
case Enum:
|
|
||||||
return align_of_type_info(info.base)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
WORD_SIZE :: size_of(int);
|
||||||
|
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
|
||||||
|
using Type_Info;
|
||||||
|
match info in type_info.variant {
|
||||||
|
case Named:
|
||||||
|
return align_of_type_info(info.base);
|
||||||
|
case Integer:
|
||||||
|
return type_info.align;
|
||||||
|
case Rune:
|
||||||
|
return type_info.align;
|
||||||
|
case Float:
|
||||||
|
return type_info.align;
|
||||||
|
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 Dynamic_Array:
|
||||||
|
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 type_info.align;
|
||||||
|
case Struct:
|
||||||
|
return type_info.align;
|
||||||
|
case Union:
|
||||||
|
return type_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 {
|
align_formula :: proc(size, align: int) -> int {
|
||||||
result := size + align-1
|
result := size + align-1;
|
||||||
return result - result%align
|
return result - result%align;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||||
WORD_SIZE :: size_of(int)
|
WORD_SIZE :: size_of(int);
|
||||||
using Type_Info
|
using Type_Info;
|
||||||
|
match info in type_info.variant {
|
||||||
match type info : type_info {
|
|
||||||
case Named:
|
case Named:
|
||||||
return size_of_type_info(info.base)
|
return size_of_type_info(info.base);
|
||||||
case Integer:
|
case Integer:
|
||||||
return info.size
|
return type_info.size;
|
||||||
|
case Rune:
|
||||||
|
return type_info.size;
|
||||||
case Float:
|
case Float:
|
||||||
return info.size
|
return type_info.size;
|
||||||
case Any:
|
|
||||||
return 2*WORD_SIZE
|
|
||||||
case String:
|
case String:
|
||||||
return 2*WORD_SIZE
|
return 2*WORD_SIZE;
|
||||||
case Boolean:
|
case Boolean:
|
||||||
return 1
|
return 1;
|
||||||
|
case Any:
|
||||||
|
return 2*WORD_SIZE;
|
||||||
case Pointer:
|
case Pointer:
|
||||||
return WORD_SIZE
|
return WORD_SIZE;
|
||||||
case Maybe:
|
|
||||||
return size_of_type_info(info.elem) + 1
|
|
||||||
case Procedure:
|
case Procedure:
|
||||||
return WORD_SIZE
|
return WORD_SIZE;
|
||||||
case Array:
|
case Array:
|
||||||
count := info.count
|
count := info.count;
|
||||||
if count == 0 {
|
if count == 0 do return 0;
|
||||||
return 0
|
size := size_of_type_info(info.elem);
|
||||||
}
|
align := align_of_type_info(info.elem);
|
||||||
size := size_of_type_info(info.elem)
|
alignment := align_formula(size, align);
|
||||||
align := align_of_type_info(info.elem)
|
return alignment*(count-1) + size;
|
||||||
alignment := align_formula(size, align)
|
case Dynamic_Array:
|
||||||
return alignment*(count-1) + size
|
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
|
||||||
case Slice:
|
case Slice:
|
||||||
return 3*WORD_SIZE
|
return 2*WORD_SIZE;
|
||||||
case Vector:
|
case Vector:
|
||||||
is_bool :: proc(type_info: ^Type_Info) -> bool {
|
count := info.count;
|
||||||
match type info : type_info {
|
if count == 0 do return 0;
|
||||||
case Named:
|
size := size_of_type_info(info.elem);
|
||||||
return is_bool(info.base)
|
align := align_of_type_info(info.elem);
|
||||||
case Boolean:
|
alignment := align_formula(size, align);
|
||||||
return true
|
return alignment*(count-1) + size;
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
count := info.count
|
|
||||||
if count == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
bit_size := 8*size_of_type_info(info.elem)
|
|
||||||
if is_bool(info.elem) {
|
|
||||||
// NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1`
|
|
||||||
// Silly LLVM spec
|
|
||||||
bit_size = 1
|
|
||||||
}
|
|
||||||
total_size_in_bits := bit_size * count
|
|
||||||
total_size := (total_size_in_bits+7)/8
|
|
||||||
return total_size
|
|
||||||
|
|
||||||
case Struct:
|
case Struct:
|
||||||
return info.size
|
return type_info.size;
|
||||||
case Union:
|
case Union:
|
||||||
return info.size
|
return type_info.size;
|
||||||
case Raw_Union:
|
|
||||||
return info.size
|
|
||||||
case Enum:
|
case Enum:
|
||||||
return size_of_type_info(info.base)
|
return size_of_type_info(info.base);
|
||||||
|
case Map:
|
||||||
|
return size_of_type_info(info.generated_struct);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+148
-125
@@ -1,155 +1,178 @@
|
|||||||
#foreign_system_library "opengl32"
|
foreign_system_library (
|
||||||
#import "win32.odin"
|
lib "opengl32.lib" when ODIN_OS == "windows";
|
||||||
#load "opengl_constants.odin"
|
lib "gl" when ODIN_OS == "linux";
|
||||||
|
)
|
||||||
|
import (
|
||||||
|
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
|
"sys/wgl.odin" when ODIN_OS == "windows";
|
||||||
|
)
|
||||||
|
import_load "opengl_constants.odin";
|
||||||
|
|
||||||
Clear :: proc(mask: u32) #foreign "glClear"
|
_ := compile_assert(ODIN_OS != "osx");
|
||||||
ClearColor :: proc(r, g, b, a: f32) #foreign "glClearColor"
|
|
||||||
Begin :: proc(mode: i32) #foreign "glBegin"
|
|
||||||
End :: proc() #foreign "glEnd"
|
|
||||||
Finish :: proc() #foreign "glFinish"
|
|
||||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign "glBlendFunc"
|
|
||||||
Enable :: proc(cap: i32) #foreign "glEnable"
|
|
||||||
Disable :: proc(cap: i32) #foreign "glDisable"
|
|
||||||
GenTextures :: proc(count: i32, result: ^u32) #foreign "glGenTextures"
|
|
||||||
DeleteTextures :: proc(count: i32, result: ^u32) #foreign "glDeleteTextures"
|
|
||||||
TexParameteri :: proc(target, pname, param: i32) #foreign "glTexParameteri"
|
|
||||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign "glTexParameterf"
|
|
||||||
BindTexture :: proc(target: i32, texture: u32) #foreign "glBindTexture"
|
|
||||||
LoadIdentity :: proc() #foreign "glLoadIdentity"
|
|
||||||
Viewport :: proc(x, y, width, height: i32) #foreign "glViewport"
|
|
||||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign "glOrtho"
|
|
||||||
Color3f :: proc(r, g, b: f32) #foreign "glColor3f"
|
|
||||||
Vertex3f :: proc(x, y, z: f32) #foreign "glVertex3f"
|
|
||||||
TexImage2D :: proc(target, level, internal_format,
|
|
||||||
width, height, border,
|
|
||||||
format, _type: i32, pixels: rawptr) #foreign "glTexImage2D"
|
|
||||||
|
|
||||||
GetError :: proc() -> i32 #foreign "glGetError"
|
foreign lib {
|
||||||
GetString :: proc(name: i32) -> ^byte #foreign "glGetString"
|
Clear :: proc(mask: u32) #link_name "glClear" ---;
|
||||||
GetIntegerv :: proc(name: i32, v: ^i32) #foreign "glGetIntegerv"
|
ClearColor :: proc(r, g, b, a: f32) #link_name "glClearColor" ---;
|
||||||
|
Begin :: proc(mode: i32) #link_name "glBegin" ---;
|
||||||
|
End :: proc() #link_name "glEnd" ---;
|
||||||
|
Finish :: proc() #link_name "glFinish" ---;
|
||||||
_libgl := win32.LoadLibraryA(("opengl32.dll\x00" as string).data)
|
BlendFunc :: proc(sfactor, dfactor: i32) #link_name "glBlendFunc" ---;
|
||||||
|
Enable :: proc(cap: i32) #link_name "glEnable" ---;
|
||||||
GetProcAddress :: proc(name: string) -> proc() {
|
Disable :: proc(cap: i32) #link_name "glDisable" ---;
|
||||||
assert(name[name.count-1] == 0)
|
GenTextures :: proc(count: i32, result: ^u32) #link_name "glGenTextures" ---;
|
||||||
res := win32.wglGetProcAddress(name.data)
|
DeleteTextures :: proc(count: i32, result: ^u32) #link_name "glDeleteTextures"---;
|
||||||
if res == nil {
|
TexParameteri :: proc(target, pname, param: i32) #link_name "glTexParameteri" ---;
|
||||||
res = win32.GetProcAddress(_libgl, name.data);
|
TexParameterf :: proc(target: i32, pname: i32, param: f32) #link_name "glTexParameterf" ---;
|
||||||
}
|
BindTexture :: proc(target: i32, texture: u32) #link_name "glBindTexture" ---;
|
||||||
return res
|
LoadIdentity :: proc() #link_name "glLoadIdentity" ---;
|
||||||
|
Viewport :: proc(x, y, width, height: i32) #link_name "glViewport" ---;
|
||||||
|
Ortho :: proc(left, right, bottom, top, near, far: f64) #link_name "glOrtho" ---;
|
||||||
|
Color3f :: proc(r, g, b: f32) #link_name "glColor3f" ---;
|
||||||
|
Vertex3f :: proc(x, y, z: f32) #link_name "glVertex3f" ---;
|
||||||
|
GetError :: proc() -> i32 #link_name "glGetError" ---;
|
||||||
|
GetString :: proc(name: i32) -> ^u8 #link_name "glGetString" ---;
|
||||||
|
GetIntegerv :: proc(name: i32, v: ^i32) #link_name "glGetIntegerv" ---;
|
||||||
|
TexCoord2f :: proc(x, y: f32) #link_name "glTexCoord2f" ---;
|
||||||
|
TexImage2D :: proc(target, level, internal_format,
|
||||||
|
width, height, border,
|
||||||
|
format, type_: i32, pixels: rawptr) #link_name "glTexImage2D" ---;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
GenBuffers: proc(count: i32, buffers: ^u32)
|
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
|
||||||
GenVertexArrays: proc(count: i32, buffers: ^u32)
|
|
||||||
GenSamplers: proc(count: i32, buffers: ^u32)
|
|
||||||
BindBuffer: proc(target: i32, buffer: u32)
|
|
||||||
BindVertexArray: proc(buffer: u32)
|
|
||||||
BindSampler: proc(position: i32, sampler: u32)
|
|
||||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32)
|
|
||||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr)
|
|
||||||
|
|
||||||
DrawArrays: proc(mode, first: i32, count: u32)
|
_libgl := win32.load_library_a(_string_data("opengl32.dll\x00"));
|
||||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr)
|
|
||||||
|
|
||||||
MapBuffer: proc(target, access: i32) -> rawptr
|
get_proc_address :: proc(name: string) -> rawptr {
|
||||||
UnmapBuffer: proc(target: i32)
|
if name[len(name)-1] == 0 {
|
||||||
|
name = name[..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 rawptr(res);
|
||||||
|
}
|
||||||
|
|
||||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr)
|
// Procedures
|
||||||
EnableVertexAttribArray: proc(index: u32)
|
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;
|
||||||
|
|
||||||
CreateShader: proc(shader_type: i32) -> u32
|
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||||
ShaderSource: proc(shader: u32, count: u32, string: ^^byte, length: ^i32)
|
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||||
CompileShader: proc(shader: u32)
|
|
||||||
CreateProgram: proc() -> u32
|
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||||
AttachShader: proc(program, shader: u32)
|
UnmapBuffer: proc(target: i32) #cc_c;
|
||||||
DetachShader: proc(program, shader: u32)
|
|
||||||
DeleteShader: proc(shader: u32)
|
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||||
LinkProgram: proc(program: u32)
|
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||||
UseProgram: proc(program: u32)
|
|
||||||
DeleteProgram: proc(program: u32)
|
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)
|
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32)
|
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte)
|
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: ^byte)
|
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
|
||||||
|
|
||||||
ActiveTexture: proc(texture: i32)
|
ActiveTexture: proc(texture: i32) #cc_c;
|
||||||
GenerateMipmap: proc(target: i32)
|
GenerateMipmap: proc(target: i32) #cc_c;
|
||||||
|
|
||||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32)
|
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32)
|
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32)
|
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32)
|
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32)
|
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32)
|
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||||
|
|
||||||
|
|
||||||
Uniform1i: proc(loc: i32, v0: i32)
|
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||||
Uniform2i: proc(loc: i32, v0, v1: i32)
|
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32)
|
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32)
|
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||||
Uniform1f: proc(loc: i32, v0: f32)
|
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||||
Uniform2f: proc(loc: i32, v0, v1: f32)
|
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32)
|
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32)
|
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32)
|
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||||
|
|
||||||
|
GetUniformLocation: proc(program: u32, name: ^u8) -> i32 #cc_c;
|
||||||
|
|
||||||
GetUniformLocation: proc(program: u32, name: ^byte) -> i32
|
|
||||||
|
|
||||||
init :: proc() {
|
init :: proc() {
|
||||||
set_proc_address :: proc(p: rawptr, name: string) #inline { (p as ^proc())^ = GetProcAddress(name) }
|
set_proc_address :: proc(p: rawptr, name: string) #inline {
|
||||||
|
x := cast(^rawptr)p;
|
||||||
|
x^ = get_proc_address(name);
|
||||||
|
}
|
||||||
|
|
||||||
set_proc_address(^GenBuffers, "glGenBuffers\x00")
|
set_proc_address(&GenBuffers, "glGenBuffers\x00");
|
||||||
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00")
|
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
|
||||||
set_proc_address(^GenSamplers, "glGenSamplers\x00")
|
set_proc_address(&GenSamplers, "glGenSamplers\x00");
|
||||||
set_proc_address(^BindBuffer, "glBindBuffer\x00")
|
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
|
||||||
set_proc_address(^BindSampler, "glBindSampler\x00")
|
set_proc_address(&BindBuffer, "glBindBuffer\x00");
|
||||||
set_proc_address(^BindVertexArray, "glBindVertexArray\x00")
|
set_proc_address(&BindSampler, "glBindSampler\x00");
|
||||||
set_proc_address(^BufferData, "glBufferData\x00")
|
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
|
||||||
set_proc_address(^BufferSubData, "glBufferSubData\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(&DrawArrays, "glDrawArrays\x00");
|
||||||
set_proc_address(^DrawElements, "glDrawElements\x00")
|
set_proc_address(&DrawElements, "glDrawElements\x00");
|
||||||
|
|
||||||
set_proc_address(^MapBuffer, "glMapBuffer\x00")
|
set_proc_address(&MapBuffer, "glMapBuffer\x00");
|
||||||
set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00")
|
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
|
||||||
|
|
||||||
set_proc_address(^VertexAttribPointer, "glVertexAttribPointer\x00")
|
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
|
||||||
set_proc_address(^EnableVertexAttribArray, "glEnableVertexAttribArray\x00")
|
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
|
||||||
|
|
||||||
set_proc_address(^CreateShader, "glCreateShader\x00")
|
set_proc_address(&CreateShader, "glCreateShader\x00");
|
||||||
set_proc_address(^ShaderSource, "glShaderSource\x00")
|
set_proc_address(&ShaderSource, "glShaderSource\x00");
|
||||||
set_proc_address(^CompileShader, "glCompileShader\x00")
|
set_proc_address(&CompileShader, "glCompileShader\x00");
|
||||||
set_proc_address(^CreateProgram, "glCreateProgram\x00")
|
set_proc_address(&CreateProgram, "glCreateProgram\x00");
|
||||||
set_proc_address(^AttachShader, "glAttachShader\x00")
|
set_proc_address(&AttachShader, "glAttachShader\x00");
|
||||||
set_proc_address(^DetachShader, "glDetachShader\x00")
|
set_proc_address(&DetachShader, "glDetachShader\x00");
|
||||||
set_proc_address(^DeleteShader, "glDeleteShader\x00")
|
set_proc_address(&DeleteShader, "glDeleteShader\x00");
|
||||||
set_proc_address(^LinkProgram, "glLinkProgram\x00")
|
set_proc_address(&LinkProgram, "glLinkProgram\x00");
|
||||||
set_proc_address(^UseProgram, "glUseProgram\x00")
|
set_proc_address(&UseProgram, "glUseProgram\x00");
|
||||||
set_proc_address(^DeleteProgram, "glDeleteProgram\x00")
|
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
|
||||||
|
|
||||||
set_proc_address(^GetShaderiv, "glGetShaderiv\x00")
|
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
|
||||||
set_proc_address(^GetProgramiv, "glGetProgramiv\x00")
|
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
|
||||||
set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00")
|
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
|
||||||
set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00")
|
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
|
||||||
|
|
||||||
set_proc_address(^ActiveTexture, "glActiveTexture\x00")
|
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
|
||||||
set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00")
|
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
|
||||||
|
|
||||||
set_proc_address(^Uniform1i, "glUniform1i\x00")
|
set_proc_address(&Uniform1i, "glUniform1i\x00");
|
||||||
set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00")
|
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
|
||||||
|
|
||||||
set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00")
|
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
|
||||||
|
|
||||||
set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00")
|
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
|
||||||
set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00")
|
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
|
||||||
set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00")
|
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
|
||||||
set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00")
|
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
|
||||||
set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00")
|
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
|
||||||
set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00")
|
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1365
-1366
File diff suppressed because it is too large
Load Diff
+33
-156
@@ -1,172 +1,49 @@
|
|||||||
#import "win32.odin"
|
import_load (
|
||||||
#import "fmt.odin"
|
"os_windows.odin" when ODIN_OS == "windows";
|
||||||
|
"os_x.odin" when ODIN_OS == "osx";
|
||||||
|
"os_linux.odin" when ODIN_OS == "linux";
|
||||||
|
)
|
||||||
|
|
||||||
File_Time :: type u64
|
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||||
|
return write(fd, cast([]u8)str);
|
||||||
File :: struct {
|
|
||||||
Handle :: type win32.HANDLE
|
|
||||||
handle: Handle
|
|
||||||
last_write_time: File_Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open :: proc(name: string) -> (File, bool) {
|
read_entire_file :: proc(name: string) -> (data: []u8, success: bool) {
|
||||||
using win32
|
fd, err := open(name, O_RDONLY, 0);
|
||||||
buf: [300]byte
|
if err != 0 {
|
||||||
copy(buf[:], name as []byte)
|
return nil, false;
|
||||||
f := File{
|
|
||||||
handle = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil),
|
|
||||||
}
|
}
|
||||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
defer close(fd);
|
||||||
f.last_write_time = last_write_time(^f)
|
|
||||||
return f, success
|
|
||||||
}
|
|
||||||
|
|
||||||
create :: proc(name: string) -> (File, bool) {
|
length: i64;
|
||||||
using win32
|
if length, err = file_size(fd); err != 0 {
|
||||||
buf: [300]byte
|
return nil, false;
|
||||||
copy(buf[:], name as []byte)
|
|
||||||
f := File{
|
|
||||||
handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil),
|
|
||||||
}
|
|
||||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
|
||||||
f.last_write_time = last_write_time(^f)
|
|
||||||
return f, success
|
|
||||||
}
|
|
||||||
|
|
||||||
close :: proc(using f: ^File) {
|
|
||||||
win32.CloseHandle(handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
write :: proc(using f: ^File, buf: []byte) -> bool {
|
|
||||||
bytes_written: i32
|
|
||||||
return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, nil) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
file_has_changed :: proc(f: ^File) -> bool {
|
|
||||||
last_write_time := last_write_time(f)
|
|
||||||
if f.last_write_time != last_write_time {
|
|
||||||
f.last_write_time = last_write_time
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
last_write_time :: proc(f: ^File) -> File_Time {
|
|
||||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
|
||||||
win32.GetFileInformationByHandle(f.handle, ^file_info)
|
|
||||||
l := file_info.last_write_time.low_date_time as File_Time
|
|
||||||
h := file_info.last_write_time.high_date_time as File_Time
|
|
||||||
return l | h << 32
|
|
||||||
}
|
|
||||||
|
|
||||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
|
||||||
last_write_time: win32.FILETIME
|
|
||||||
data: win32.WIN32_FILE_ATTRIBUTE_DATA
|
|
||||||
|
|
||||||
buf: [1024]byte
|
|
||||||
path := buf[:0]
|
|
||||||
fmt.bprint(^path, name, "\x00")
|
|
||||||
|
|
||||||
if win32.GetFileAttributesExA(path.data, win32.GetFileExInfoStandard, ^data) != 0 {
|
|
||||||
last_write_time = data.last_write_time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l := last_write_time.low_date_time as File_Time
|
if length <= 0 {
|
||||||
h := last_write_time.high_date_time as File_Time
|
return nil, true;
|
||||||
return l | h << 32
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
File_Standard :: type enum {
|
|
||||||
INPUT,
|
|
||||||
OUTPUT,
|
|
||||||
ERROR,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(bill): Uses startup to initialize it
|
|
||||||
__std_files := [File_Standard.count]File{
|
|
||||||
{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
|
|
||||||
{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},
|
|
||||||
{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)},
|
|
||||||
}
|
|
||||||
|
|
||||||
stdin := ^__std_files[File_Standard.INPUT]
|
|
||||||
stdout := ^__std_files[File_Standard.OUTPUT]
|
|
||||||
stderr := ^__std_files[File_Standard.ERROR]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
|
||||||
buf: [300]byte
|
|
||||||
copy(buf[:], name as []byte)
|
|
||||||
|
|
||||||
f, file_ok := open(name)
|
|
||||||
if !file_ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
defer close(^f)
|
|
||||||
|
|
||||||
length: i64
|
|
||||||
file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0
|
|
||||||
if !file_size_ok {
|
|
||||||
return nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data := new_slice(u8, length)
|
data := make([]u8, int(length));
|
||||||
if data.data == nil {
|
if data == nil {
|
||||||
return nil, false
|
return nil, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
single_read_length: i32
|
bytes_read, read_err := read(fd, data);
|
||||||
total_read: i64
|
if read_err != 0 {
|
||||||
|
free(data);
|
||||||
for total_read < length {
|
return nil, false;
|
||||||
remaining := length - total_read
|
|
||||||
to_read: u32
|
|
||||||
MAX :: 1<<32-1
|
|
||||||
if remaining <= MAX {
|
|
||||||
to_read = remaining as u32
|
|
||||||
} else {
|
|
||||||
to_read = MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil)
|
|
||||||
if single_read_length <= 0 {
|
|
||||||
free(data.data)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
total_read += single_read_length as i64
|
|
||||||
}
|
}
|
||||||
|
return data[0..bytes_read], true;
|
||||||
return data, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_entire_file :: proc(name: string, data: []u8) -> (sucess: bool) {
|
||||||
|
fd, err := open(name, O_WRONLY, 0);
|
||||||
|
if err != 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
defer close(fd);
|
||||||
|
|
||||||
|
bytes_written, write_err := write(fd, data);
|
||||||
heap_alloc :: proc(size: int) -> rawptr {
|
return write_err != 0;
|
||||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size)
|
|
||||||
}
|
}
|
||||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
|
||||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size)
|
|
||||||
}
|
|
||||||
heap_free :: proc(ptr: rawptr) {
|
|
||||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
exit :: proc(code: int) {
|
|
||||||
win32.ExitProcess(code as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
current_thread_id :: proc() -> int {
|
|
||||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
|
||||||
return GetCurrentThreadId() as int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
foreign_system_library (
|
||||||
|
dl "dl";
|
||||||
|
libc "c";
|
||||||
|
)
|
||||||
|
import "strings.odin";
|
||||||
|
|
||||||
|
Handle :: i32;
|
||||||
|
File_Time :: u64;
|
||||||
|
Errno :: i32;
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
args := _alloc_command_line_arguments();
|
||||||
|
|
||||||
|
_File_Time :: 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: _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 :: 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;}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
_unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
|
||||||
|
_unix_close :: proc(fd: Handle) -> i32 #link_name "close" ---;
|
||||||
|
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "read" ---;
|
||||||
|
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "write" ---;
|
||||||
|
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #link_name "lseek64" ---;
|
||||||
|
_unix_gettid :: proc() -> u64 #link_name "gettid" ---;
|
||||||
|
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #link_name "stat" ---;
|
||||||
|
_unix_access :: proc(path: ^u8, mask: int) -> i32 #link_name "access" ---;
|
||||||
|
|
||||||
|
_unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
|
||||||
|
_unix_free :: proc(ptr: rawptr) #link_name "free" ---;
|
||||||
|
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
|
||||||
|
_unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
|
||||||
|
|
||||||
|
_unix_exit :: proc(status: int) #link_name "exit" ---;
|
||||||
|
}
|
||||||
|
foreign dl {
|
||||||
|
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
|
||||||
|
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
|
||||||
|
_unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
|
||||||
|
_unix_dlerror :: proc() -> ^u8 #link_name "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 = O_RDONLY, perm: u32 = 0) -> (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) -> File_Time {}
|
||||||
|
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
import win32 "sys/windows.odin";
|
||||||
|
import "mem.odin";
|
||||||
|
|
||||||
|
Handle :: int;
|
||||||
|
File_Time :: u64;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
Errno :: int;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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[..], cast([]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 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: []u8) -> (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 == win32.FALSE {
|
||||||
|
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: []u8) -> (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 == win32.FALSE {
|
||||||
|
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;
|
||||||
|
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 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) == 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) -> 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;
|
||||||
|
buf: [1024]u8;
|
||||||
|
|
||||||
|
assert(len(buf) > len(name));
|
||||||
|
|
||||||
|
copy(buf[..], cast([]u8)name);
|
||||||
|
|
||||||
|
if win32.get_file_attributes_ex_a(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
|
||||||
|
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() -> 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 do wstr_len += 1;
|
||||||
|
|
||||||
|
len := 2*wstr_len-1;
|
||||||
|
buf := make([]u8, len+1);
|
||||||
|
str := mem.slice_ptr(wstr, wstr_len+1);
|
||||||
|
|
||||||
|
i, j := 0, 0;
|
||||||
|
for str[j] != 0 {
|
||||||
|
match {
|
||||||
|
case str[j] < 0x80:
|
||||||
|
if i+1 > len do return "";
|
||||||
|
buf[i] = u8(str[j]); i += 1;
|
||||||
|
j += 1;
|
||||||
|
case str[j] < 0x800:
|
||||||
|
if i+2 > len do return "";
|
||||||
|
buf[i] = u8(0xc0 + (str[j]>>6)); i += 1;
|
||||||
|
buf[i] = u8(0x80 + (str[j]&0x3f)); i += 1;
|
||||||
|
j += 1;
|
||||||
|
case 0xd800 <= str[j] && str[j] < 0xdc00:
|
||||||
|
if i+4 > len do return "";
|
||||||
|
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
|
||||||
|
buf[i] = u8(0xf0 + (c >> 18)); i += 1;
|
||||||
|
buf[i] = u8(0x80 + ((c >> 12) & 0x3f)); i += 1;
|
||||||
|
buf[i] = u8(0x80 + ((c >> 6) & 0x3f)); i += 1;
|
||||||
|
buf[i] = u8(0x80 + ((c ) & 0x3f)); i += 1;
|
||||||
|
j += 2;
|
||||||
|
case 0xdc00 <= str[j] && str[j] < 0xe000:
|
||||||
|
return "";
|
||||||
|
case:
|
||||||
|
if i+3 > len do return "";
|
||||||
|
buf[i] = 0xe0 + u8 (str[j] >> 12); i += 1;
|
||||||
|
buf[i] = 0x80 + u8((str[j] >> 6) & 0x3f); i += 1;
|
||||||
|
buf[i] = 0x80 + u8((str[j] ) & 0x3f); i += 1;
|
||||||
|
j += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buf[..i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 do arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
|
||||||
|
return arg_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
+283
@@ -0,0 +1,283 @@
|
|||||||
|
foreign_system_library (
|
||||||
|
dl "dl";
|
||||||
|
libc "c";
|
||||||
|
)
|
||||||
|
|
||||||
|
import "strings.odin";
|
||||||
|
|
||||||
|
Handle :: i32;
|
||||||
|
File_Time :: u64;
|
||||||
|
Errno :: int;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
_File_Time :: 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: 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 :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFLNK;
|
||||||
|
S_ISREG :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFREG;
|
||||||
|
S_ISDIR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFDIR;
|
||||||
|
S_ISCHR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFCHR;
|
||||||
|
S_ISBLK :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFBLK;
|
||||||
|
S_ISFIFO :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFIFO;
|
||||||
|
S_ISSOCK :: proc(m: u32) -> bool #inline 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 {
|
||||||
|
unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
|
||||||
|
unix_close :: proc(handle: Handle) #link_name "close" ---;
|
||||||
|
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "read" ---;
|
||||||
|
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "write" ---;
|
||||||
|
unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int #link_name "lseek" ---;
|
||||||
|
unix_gettid :: proc() -> u64 #link_name "gettid" ---;
|
||||||
|
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #link_name "stat" ---;
|
||||||
|
unix_access :: proc(path: ^u8, mask: int) -> int #link_name "access" ---;
|
||||||
|
|
||||||
|
unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
|
||||||
|
unix_free :: proc(ptr: rawptr) #link_name "free" ---;
|
||||||
|
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
|
||||||
|
unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
|
||||||
|
|
||||||
|
unix_exit :: proc(status: int) #link_name "exit" ---;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign dl {
|
||||||
|
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
|
||||||
|
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
|
||||||
|
unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
|
||||||
|
unix_dlerror :: proc() -> ^u8 #link_name "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 = 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, 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 :: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
Any :: struct #ordered {
|
||||||
|
data: rawptr;
|
||||||
|
type_info: ^Type_Info;
|
||||||
|
};
|
||||||
|
|
||||||
|
String :: struct #ordered {
|
||||||
|
data: ^u8;
|
||||||
|
len: int;
|
||||||
|
};
|
||||||
|
|
||||||
|
Slice :: struct #ordered {
|
||||||
|
data: rawptr;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
};
|
||||||
|
|
||||||
|
Dynamic_Array :: struct #ordered {
|
||||||
|
data: rawptr;
|
||||||
|
len: int;
|
||||||
|
cap: int;
|
||||||
|
allocator: Allocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
Map :: struct #ordered {
|
||||||
|
hashes: [dynamic]int;
|
||||||
|
entries: Dynamic_Array;
|
||||||
|
};
|
||||||
|
|
||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
bubble_sort :: 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 {
|
||||||
|
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 {
|
||||||
|
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(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(a[0..i], f);
|
||||||
|
quick_sort(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(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 {
|
||||||
|
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+1 {
|
||||||
|
for j in 0..a {
|
||||||
|
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 {
|
||||||
|
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+1 {
|
||||||
|
for j in 0..a {
|
||||||
|
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 {
|
||||||
|
match delta := a - b; {
|
||||||
|
case delta < 0: return -1;
|
||||||
|
case delta > 0: return +1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
compare_f32s :: proc(a, b: f32) -> int {
|
||||||
|
match delta := a - b; {
|
||||||
|
case delta < 0: return -1;
|
||||||
|
case delta > 0: return +1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
compare_f64s :: proc(a, b: f64) -> int {
|
||||||
|
match delta := a - b; {
|
||||||
|
case delta < 0: return -1;
|
||||||
|
case delta > 0: return +1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
compare_strings :: proc(a, b: string) -> int {
|
||||||
|
return __string_cmp(a, b);
|
||||||
|
}
|
||||||
@@ -0,0 +1,490 @@
|
|||||||
|
import . "decimal.odin";
|
||||||
|
|
||||||
|
Int_Flag :: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if neg do return -value;
|
||||||
|
return 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 == '_' do continue;
|
||||||
|
v := u128(_digit_value(r));
|
||||||
|
if v >= base do break;
|
||||||
|
value *= base;
|
||||||
|
value += u128(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if neg do return -value;
|
||||||
|
return 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 += 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 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 s[i] == 'e' || s[i] == 'E' {
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
match 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: []u8, b: bool) -> string {
|
||||||
|
if b {
|
||||||
|
append(&buf, "true");
|
||||||
|
} else {
|
||||||
|
append(&buf, "false");
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 {
|
||||||
|
bits: u64;
|
||||||
|
flt: ^FloatInfo;
|
||||||
|
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, ...cast([]u8)s);
|
||||||
|
return buf;
|
||||||
|
|
||||||
|
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};
|
||||||
|
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[..m]);
|
||||||
|
for ; m < digs.decimal_point; m += 1 {
|
||||||
|
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{'%', fmt};
|
||||||
|
append(&buf, ...c[..]);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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;
|
||||||
|
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: Int_Flag) -> string {
|
||||||
|
if base < 2 || base > MAX_BASE {
|
||||||
|
panic("strconv: illegal base passed to append_bits");
|
||||||
|
}
|
||||||
|
|
||||||
|
neg: bool;
|
||||||
|
a: [129]u8;
|
||||||
|
i := len(a);
|
||||||
|
u, neg = is_integer_negative(u, is_signed, bit_size);
|
||||||
|
b := u128(base);
|
||||||
|
for u >= b {
|
||||||
|
i-=1; a[i] = digits[uint(u % b)];
|
||||||
|
u /= b;
|
||||||
|
}
|
||||||
|
i-=1; a[i] = digits[uint(u % b)];
|
||||||
|
|
||||||
|
if flags&Int_Flag.Prefix != 0 {
|
||||||
|
ok := true;
|
||||||
|
match 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if neg {
|
||||||
|
i-=1; a[i] = '-';
|
||||||
|
} else if flags&Int_Flag.Plus != 0 {
|
||||||
|
i-=1; a[i] = '+';
|
||||||
|
} else if flags&Int_Flag.Space != 0 {
|
||||||
|
i-=1; a[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
append(&buf, ...a[i..]);
|
||||||
|
return string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import "mem.odin";
|
||||||
|
|
||||||
|
new_string :: proc(s: string) -> string {
|
||||||
|
c := make([]u8, len(s)+1);
|
||||||
|
copy(c, cast([]u8)s);
|
||||||
|
c[len(s)] = 0;
|
||||||
|
return string(c[..len(s)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_c_string :: proc(s: string) -> ^u8 {
|
||||||
|
c := make([]u8, len(s)+1);
|
||||||
|
copy(c, cast([]u8)s);
|
||||||
|
c[len(s)] = 0;
|
||||||
|
return &c[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
to_odin_string :: proc(c: ^u8) -> string {
|
||||||
|
if c == nil do return "";
|
||||||
|
len := 0;
|
||||||
|
for (c+len)^ != 0 do len+=1;
|
||||||
|
return string(mem.slice_ptr(c, len));
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
import_load (
|
||||||
|
"sync_windows.odin" when ODIN_OS == "windows";
|
||||||
|
"sync_linux.odin" when ODIN_OS == "linux";
|
||||||
|
)
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import (
|
||||||
|
"atomics.odin";
|
||||||
|
"os.odin";
|
||||||
|
)
|
||||||
|
|
||||||
|
Semaphore :: struct {
|
||||||
|
// _handle: win32.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex :: struct {
|
||||||
|
_semaphore: Semaphore;
|
||||||
|
_counter: i32;
|
||||||
|
_owner: i32;
|
||||||
|
_recursion: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
current_thread_id :: proc() -> i32 {
|
||||||
|
return i32(os.current_thread_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_init :: proc(s: ^Semaphore) {
|
||||||
|
// s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||||
|
// win32.CloseHandle(s._handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||||
|
// win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_release :: proc(s: ^Semaphore) #inline {
|
||||||
|
semaphore_post(s, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
semaphore_wait :: proc(s: ^Semaphore) {
|
||||||
|
// win32.WaitForSingleObject(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 += 1;
|
||||||
|
}
|
||||||
|
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 += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
mutex_unlock :: proc(m: ^Mutex) {
|
||||||
|
recursion: i32;
|
||||||
|
thread_id := current_thread_id();
|
||||||
|
assert(thread_id == atomics.load(&m._owner));
|
||||||
|
|
||||||
|
m._recursion -= 1;
|
||||||
|
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,122 @@
|
|||||||
|
import (
|
||||||
|
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||||
|
"atomics.odin";
|
||||||
|
)
|
||||||
|
|
||||||
|
Semaphore :: struct {
|
||||||
|
_handle: win32.Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mutex :: struct {
|
||||||
|
_semaphore: Semaphore;
|
||||||
|
_counter: i32;
|
||||||
|
_owner: i32;
|
||||||
|
_recursion: i32;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mutex :: struct {
|
||||||
|
_critical_section: win32.Critical_Section;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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 win32.try_enter_critical_section(&m._critical_section) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock :: proc(m: ^Mutex) {
|
||||||
|
win32.leave_critical_section(&m._critical_section);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
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,83 @@
|
|||||||
|
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;
|
||||||
|
Color_Ref :: 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(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
|
||||||
|
Choose_Pixel_Format_ARB_Type :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
|
||||||
|
Swap_Interval_EXT_Type :: #type proc(interval: i32) -> bool #cc_c;
|
||||||
|
Get_Extensions_String_ARB_Type :: #type proc(Hdc) -> ^u8 #cc_c;
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
create_context :: proc(hdc: Hdc) -> Hglrc #link_name "wglCreateContext" ---;
|
||||||
|
make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #link_name "wglMakeCurrent" ---;
|
||||||
|
get_proc_address :: proc(c_str: ^u8) -> rawptr #link_name "wglGetProcAddress" ---;
|
||||||
|
delete_context :: proc(hglrc: Hglrc) -> Bool #link_name "wglDeleteContext" ---;
|
||||||
|
copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool #link_name "wglCopyContext" ---;
|
||||||
|
create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #link_name "wglCreateLayerContext" ---;
|
||||||
|
describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool #link_name "wglDescribeLayerPlane" ---;
|
||||||
|
get_current_context :: proc() -> Hglrc #link_name "wglGetCurrentContext" ---;
|
||||||
|
get_current_dc :: proc() -> Hdc #link_name "wglGetCurrentDC" ---;
|
||||||
|
get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #link_name "wglGetLayerPaletteEntries" ---;
|
||||||
|
realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #link_name "wglRealizeLayerPalette" ---;
|
||||||
|
set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #link_name "wglSetLayerPaletteEntries" ---;
|
||||||
|
share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #link_name "wglShareLists" ---;
|
||||||
|
swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool #link_name "wglSwapLayerBuffers" ---;
|
||||||
|
use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #link_name "wglUseFontBitmaps" ---;
|
||||||
|
use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool #link_name "wglUseFontOutlines" ---;
|
||||||
|
}
|
||||||
@@ -0,0 +1,729 @@
|
|||||||
|
foreign_system_library (
|
||||||
|
"kernel32.lib" when ODIN_OS == "windows";
|
||||||
|
"user32.lib" when ODIN_OS == "windows";
|
||||||
|
"gdi32.lib" when ODIN_OS == "windows";
|
||||||
|
"winmm.lib" when ODIN_OS == "windows";
|
||||||
|
"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;
|
||||||
|
Hmonitor :: Handle;
|
||||||
|
Wparam :: uint;
|
||||||
|
Lparam :: int;
|
||||||
|
Lresult :: int;
|
||||||
|
Wnd_Proc :: proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
|
||||||
|
|
||||||
|
Bool :: i32;
|
||||||
|
FALSE: Bool : 0;
|
||||||
|
TRUE: Bool : 1;
|
||||||
|
|
||||||
|
Point :: struct #ordered {
|
||||||
|
x, y: i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Wnd_Class_Ex_A :: struct #ordered {
|
||||||
|
size, style: u32;
|
||||||
|
wnd_proc: Wnd_Proc;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
By_Handle_File_Information :: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
File_Attribute_Data :: struct #ordered {
|
||||||
|
file_attributes: u32;
|
||||||
|
creation_time,
|
||||||
|
last_access_time,
|
||||||
|
last_write_time: Filetime;
|
||||||
|
file_size_high,
|
||||||
|
file_size_low: u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Find_Data :: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Security_Attributes :: struct #ordered {
|
||||||
|
length: u32;
|
||||||
|
security_descriptor: rawptr;
|
||||||
|
inherit_handle: Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Pixel_Format_Descriptor :: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
Critical_Section :: struct #ordered {
|
||||||
|
debug_info: ^Critical_Section_Debug;
|
||||||
|
|
||||||
|
lock_count: i32;
|
||||||
|
recursion_count: i32;
|
||||||
|
owning_thread: Handle;
|
||||||
|
lock_semaphore: Handle;
|
||||||
|
spin_count: ^u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Critical_Section_Debug :: struct #ordered {
|
||||||
|
typ: u16;
|
||||||
|
creator_back_trace_index: u16;
|
||||||
|
critical_section: ^Critical_Section;
|
||||||
|
process_locks_list: ^List_Entry;
|
||||||
|
entry_count: u32;
|
||||||
|
contention_count: u32;
|
||||||
|
flags: u32;
|
||||||
|
creator_back_trace_index_high: u16;
|
||||||
|
spare_word: u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
List_Entry :: struct #ordered {flink, blink: ^List_Entry};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MAPVK_VK_TO_VSC :: 0;
|
||||||
|
MAPVK_VSC_TO_VK :: 1;
|
||||||
|
MAPVK_VK_TO_CHAR :: 2;
|
||||||
|
MAPVK_VSC_TO_VK_EX :: 3;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
INVALID_HANDLE :: Handle(~int(0));
|
||||||
|
|
||||||
|
CREATE_SUSPENDED :: 0x00000004;
|
||||||
|
STACK_SIZE_PARAM_IS_A_RESERVATION :: 0x00010000;
|
||||||
|
WAIT_ABANDONED :: 0x00000080;
|
||||||
|
WAIT_OBJECT_0 :: 0;
|
||||||
|
WAIT_TIMEOUT :: 0x00000102;
|
||||||
|
WAIT_FAILED :: 0xffffffff;
|
||||||
|
|
||||||
|
CS_VREDRAW :: 0x0001;
|
||||||
|
CS_HREDRAW :: 0x0002;
|
||||||
|
CS_OWNDC :: 0x0020;
|
||||||
|
CW_USEDEFAULT :: -0x80000000;
|
||||||
|
|
||||||
|
WS_OVERLAPPED :: 0;
|
||||||
|
WS_MAXIMIZEBOX :: 0x00010000;
|
||||||
|
WS_MINIMIZEBOX :: 0x00020000;
|
||||||
|
WS_THICKFRAME :: 0x00040000;
|
||||||
|
WS_SYSMENU :: 0x00080000;
|
||||||
|
WS_BORDER :: 0x00800000;
|
||||||
|
WS_CAPTION :: 0x00C00000;
|
||||||
|
WS_VISIBLE :: 0x10000000;
|
||||||
|
WS_POPUP :: 0x80000000;
|
||||||
|
WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
|
||||||
|
WS_POPUPWINDOW :: WS_POPUP | WS_BORDER | WS_SYSMENU;
|
||||||
|
|
||||||
|
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_SYSKEYDOWN :: 0x0104;
|
||||||
|
WM_SYSKEYUP :: 0x0105;
|
||||||
|
WM_WINDOWPOSCHANGED :: 0x0047;
|
||||||
|
WM_SETCURSOR :: 0x0020;
|
||||||
|
WM_CHAR :: 0x0102;
|
||||||
|
WM_ACTIVATE :: 0x0006;
|
||||||
|
WM_SETFOCUS :: 0x0007;
|
||||||
|
WM_KILLFOCUS :: 0x0008;
|
||||||
|
WM_USER :: 0x0400;
|
||||||
|
|
||||||
|
WM_MOUSEWHEEL :: 0x020A;
|
||||||
|
WM_MOUSEMOVE :: 0x0200;
|
||||||
|
WM_LBUTTONDOWN :: 0x0201;
|
||||||
|
WM_LBUTTONUP :: 0x0202;
|
||||||
|
WM_LBUTTONDBLCLK :: 0x0203;
|
||||||
|
WM_RBUTTONDOWN :: 0x0204;
|
||||||
|
WM_RBUTTONUP :: 0x0205;
|
||||||
|
WM_RBUTTONDBLCLK :: 0x0206;
|
||||||
|
WM_MBUTTONDOWN :: 0x0207;
|
||||||
|
WM_MBUTTONUP :: 0x0208;
|
||||||
|
WM_MBUTTONDBLCLK :: 0x0209;
|
||||||
|
|
||||||
|
PM_NOREMOVE :: 0x0000;
|
||||||
|
PM_REMOVE :: 0x0001;
|
||||||
|
PM_NOYIELD :: 0x0002;
|
||||||
|
|
||||||
|
BLACK_BRUSH :: 4;
|
||||||
|
|
||||||
|
SM_CXSCREEN :: 0;
|
||||||
|
SM_CYSCREEN :: 1;
|
||||||
|
|
||||||
|
SW_SHOW :: 5;
|
||||||
|
|
||||||
|
COLOR_BACKGROUND :: Hbrush(int(1));
|
||||||
|
|
||||||
|
INVALID_SET_FILE_POINTER :: ~u32(0);
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008;
|
||||||
|
INFINITE :: 0xffffffff;
|
||||||
|
GWL_STYLE :: -16;
|
||||||
|
Hwnd_TOP :: Hwnd(uint(0));
|
||||||
|
|
||||||
|
BI_RGB :: 0;
|
||||||
|
DIB_RGB_COLORS :: 0x00;
|
||||||
|
SRCCOPY: u32 : 0x00cc0020;
|
||||||
|
|
||||||
|
|
||||||
|
MONITOR_DEFAULTTONULL :: 0x00000000;
|
||||||
|
MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
|
||||||
|
MONITOR_DEFAULTTONEAREST :: 0x00000002;
|
||||||
|
|
||||||
|
SWP_FRAMECHANGED :: 0x0020;
|
||||||
|
SWP_NOOWNERZORDER :: 0x0200;
|
||||||
|
SWP_NOZORDER :: 0x0004;
|
||||||
|
SWP_NOSIZE :: 0x0001;
|
||||||
|
SWP_NOMOVE :: 0x0002;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Windows OpenGL
|
||||||
|
|
||||||
|
PFD_TYPE_RGBA :: 0;
|
||||||
|
PFD_TYPE_COLORINDEX :: 1;
|
||||||
|
PFD_MAIN_PLANE :: 0;
|
||||||
|
PFD_OVERLAY_PLANE :: 1;
|
||||||
|
PFD_UNDERLAY_PLANE :: -1;
|
||||||
|
PFD_DOUBLEBUFFER :: 1;
|
||||||
|
PFD_STEREO :: 2;
|
||||||
|
PFD_DRAW_TO_WINDOW :: 4;
|
||||||
|
PFD_DRAW_TO_BITMAP :: 8;
|
||||||
|
PFD_SUPPORT_GDI :: 16;
|
||||||
|
PFD_SUPPORT_OPENGL :: 32;
|
||||||
|
PFD_GENERIC_FORMAT :: 64;
|
||||||
|
PFD_NEED_PALETTE :: 128;
|
||||||
|
PFD_NEED_SYSTEM_PALETTE :: 0x00000100;
|
||||||
|
PFD_SWAP_EXCHANGE :: 0x00000200;
|
||||||
|
PFD_SWAP_COPY :: 0x00000400;
|
||||||
|
PFD_SWAP_LAYER_BUFFERS :: 0x00000800;
|
||||||
|
PFD_GENERIC_ACCELERATED :: 0x00001000;
|
||||||
|
PFD_DEPTH_DONTCARE :: 0x20000000;
|
||||||
|
PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000;
|
||||||
|
PFD_STEREO_DONTCARE :: 0x80000000;
|
||||||
|
|
||||||
|
GET_FILEEX_INFO_LEVELS :: i32;
|
||||||
|
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0;
|
||||||
|
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
|
||||||
|
|
||||||
|
|
||||||
|
foreign kernel32 {
|
||||||
|
get_last_error :: proc() -> i32 #cc_std #link_name "GetLastError" ---;
|
||||||
|
exit_process :: proc(exit_code: u32) #cc_std #link_name "ExitProcess" ---;
|
||||||
|
get_module_handle_a :: proc(module_name: ^u8) -> Hinstance #cc_std #link_name "GetModuleHandleA" ---;
|
||||||
|
sleep :: proc(ms: i32) -> i32 #cc_std #link_name "Sleep" ---;
|
||||||
|
query_performance_frequency :: proc(result: ^i64) -> i32 #cc_std #link_name "QueryPerformanceFrequency" ---;
|
||||||
|
query_performance_counter :: proc(result: ^i64) -> i32 #cc_std #link_name "QueryPerformanceCounter" ---;
|
||||||
|
output_debug_string_a :: proc(c_str: ^u8) #cc_std #link_name "OutputDebugStringA" ---;
|
||||||
|
|
||||||
|
get_command_line_a :: proc() -> ^u8 #cc_std #link_name "GetCommandLineA" ---;
|
||||||
|
get_command_line_w :: proc() -> ^u16 #cc_std #link_name "GetCommandLineW" ---;
|
||||||
|
get_system_metrics :: proc(index: i32) -> i32 #cc_std #link_name "GetSystemMetrics" ---;
|
||||||
|
get_current_thread_id :: proc() -> u32 #cc_std #link_name "GetCurrentThreadId" ---;
|
||||||
|
|
||||||
|
get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) #cc_std #link_name "GetSystemTimeAsFileTime" ---;
|
||||||
|
file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool #cc_std #link_name "FileTimeToLocalFileTime" ---;
|
||||||
|
file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool #cc_std #link_name "FileTimeToSystemTime" ---;
|
||||||
|
system_time_to_file_time :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool #cc_std #link_name "SystemTimeToFileTime" ---;
|
||||||
|
|
||||||
|
close_handle :: proc(h: Handle) -> i32 #cc_std #link_name "CloseHandle" ---;
|
||||||
|
get_std_handle :: proc(h: i32) -> Handle #cc_std #link_name "GetStdHandle" ---;
|
||||||
|
create_file_a :: proc(filename: ^u8, desired_access, share_mode: u32,
|
||||||
|
security: rawptr,
|
||||||
|
creation, flags_and_attribs: u32, template_file: Handle) -> Handle #cc_std #link_name "CreateFileA" ---;
|
||||||
|
read_file :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool #cc_std #link_name "ReadFile" ---;
|
||||||
|
write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool #cc_std #link_name "WriteFile" ---;
|
||||||
|
|
||||||
|
get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool #cc_std #link_name "GetFileSizeEx" ---;
|
||||||
|
get_file_attributes_a :: proc(filename: ^u8) -> u32 #cc_std #link_name "GetFileAttributesA" ---;
|
||||||
|
get_file_attributes_ex_a :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool #cc_std #link_name "GetFileAttributesExA" ---;
|
||||||
|
get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool #cc_std #link_name "GetFileInformationByHandle" ---;
|
||||||
|
|
||||||
|
get_file_type :: proc(file_handle: Handle) -> u32 #cc_std #link_name "GetFileType" ---;
|
||||||
|
set_file_pointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #cc_std #link_name "SetFilePointer" ---;
|
||||||
|
|
||||||
|
set_handle_information :: proc(obj: Handle, mask, flags: u32) -> Bool #cc_std #link_name "SetHandleInformation" ---;
|
||||||
|
|
||||||
|
find_first_file_a :: proc(file_name : ^u8, data : ^Find_Data) -> Handle #cc_std #link_name "FindFirstFileA" ---;
|
||||||
|
find_next_file_a :: proc(file : Handle, data : ^Find_Data) -> Bool #cc_std #link_name "FindNextFileA" ---;
|
||||||
|
find_close :: proc(file : Handle) -> Bool #cc_std #link_name "FindClose" ---;
|
||||||
|
|
||||||
|
|
||||||
|
heap_alloc :: proc(h: Handle, flags: u32, bytes: int) -> rawptr #cc_std #link_name "HeapAlloc" ---;
|
||||||
|
heap_realloc :: proc(h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr #cc_std #link_name "HeapReAlloc" ---;
|
||||||
|
heap_free :: proc(h: Handle, flags: u32, memory: rawptr) -> Bool #cc_std #link_name "HeapFree" ---;
|
||||||
|
get_process_heap :: proc() -> Handle #cc_std #link_name "GetProcessHeap" ---;
|
||||||
|
|
||||||
|
|
||||||
|
create_semaphore_a :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^u8) -> Handle #cc_std #link_name "CreateSemaphoreA" ---;
|
||||||
|
release_semaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool #cc_std #link_name "ReleaseSemaphore" ---;
|
||||||
|
wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 #cc_std #link_name "WaitForSingleObject" ---;
|
||||||
|
|
||||||
|
|
||||||
|
interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #cc_c #link_name "InterlockedCompareExchange" ---;
|
||||||
|
interlocked_exchange :: proc(dst: ^i32, desired: i32) -> i32 #cc_c #link_name "InterlockedExchange" ---;
|
||||||
|
interlocked_exchange_add :: proc(dst: ^i32, desired: i32) -> i32 #cc_c #link_name "InterlockedExchangeAdd" ---;
|
||||||
|
interlocked_and :: proc(dst: ^i32, desired: i32) -> i32 #cc_c #link_name "InterlockedAnd" ---;
|
||||||
|
interlocked_or :: proc(dst: ^i32, desired: i32) -> i32 #cc_c #link_name "InterlockedOr" ---;
|
||||||
|
|
||||||
|
interlocked_compare_exchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 #cc_c #link_name "InterlockedCompareExchange64" ---;
|
||||||
|
interlocked_exchange64 :: proc(dst: ^i64, desired: i64) -> i64 #cc_c #link_name "InterlockedExchange64" ---;
|
||||||
|
interlocked_exchange_add64 :: proc(dst: ^i64, desired: i64) -> i64 #cc_c #link_name "InterlockedExchangeAdd64" ---;
|
||||||
|
interlocked_and64 :: proc(dst: ^i64, desired: i64) -> i64 #cc_c #link_name "InterlockedAnd64" ---;
|
||||||
|
interlocked_or64 :: proc(dst: ^i64, desired: i64) -> i64 #cc_c #link_name "InterlockedOr64" ---;
|
||||||
|
|
||||||
|
mm_pause :: proc() #cc_std #link_name "_mm_pause" ---;
|
||||||
|
read_write_barrier :: proc() #cc_std #link_name "ReadWriteBarrier" ---;
|
||||||
|
write_barrier :: proc() #cc_std #link_name "WriteBarrier" ---;
|
||||||
|
read_barrier :: proc() #cc_std #link_name "ReadBarrier" ---;
|
||||||
|
|
||||||
|
create_thread :: proc(thread_attributes: ^Security_Attributes, stack_size: int, start_routine: rawptr,
|
||||||
|
parameter: rawptr, creation_flags: u32, thread_id: ^u32) -> Handle #cc_std #link_name "CreateThread" ---;
|
||||||
|
resume_thread :: proc(thread: Handle) -> u32 #cc_std #link_name "ResumeThread" ---;
|
||||||
|
get_thread_priority :: proc(thread: Handle) -> i32 #cc_std #link_name "GetThreadPriority" ---;
|
||||||
|
set_thread_priority :: proc(thread: Handle, priority: i32) -> Bool #cc_std #link_name "SetThreadPriority" ---;
|
||||||
|
get_exit_code_thread :: proc(thread: Handle, exit_code: ^u32) -> Bool #cc_std #link_name "GetExitCodeThread" ---;
|
||||||
|
|
||||||
|
initialize_critical_section :: proc(critical_section: ^Critical_Section) #cc_std #link_name "InitializeCriticalSection" ---;
|
||||||
|
initialize_critical_section_and_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) #cc_std #link_name "InitializeCriticalSectionAndSpinCount" ---;
|
||||||
|
delete_critical_section :: proc(critical_section: ^Critical_Section) #cc_std #link_name "DeleteCriticalSection" ---;
|
||||||
|
set_critical_section_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> u32 #cc_std #link_name "SetCriticalSectionSpinCount" ---;
|
||||||
|
try_enter_critical_section :: proc(critical_section: ^Critical_Section) -> Bool #cc_std #link_name "TryEnterCriticalSection" ---;
|
||||||
|
enter_critical_section :: proc(critical_section: ^Critical_Section) #cc_std #link_name "EnterCriticalSection" ---;
|
||||||
|
leave_critical_section :: proc(critical_section: ^Critical_Section) #cc_std #link_name "LeaveCriticalSection" ---;
|
||||||
|
|
||||||
|
create_event_a :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: ^u8) -> Handle #cc_std #link_name "CreateEventA" ---;
|
||||||
|
|
||||||
|
load_library_a :: proc(c_str: ^u8) -> Hmodule #cc_std #link_name "LoadLibraryA" ---;
|
||||||
|
free_library :: proc(h: Hmodule) #cc_std #link_name "FreeLibrary" ---;
|
||||||
|
get_proc_address :: proc(h: Hmodule, c_str: ^u8) -> rawptr #cc_std #link_name "GetProcAddress" ---;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign user32 {
|
||||||
|
get_desktop_window :: proc() -> Hwnd #cc_std #link_name "GetDesktopWindow" ---;
|
||||||
|
show_cursor :: proc(show : Bool) #cc_std #link_name "ShowCursor" ---;
|
||||||
|
get_cursor_pos :: proc(p: ^Point) -> i32 #cc_std #link_name "GetCursorPos" ---;
|
||||||
|
screen_to_client :: proc(h: Hwnd, p: ^Point) -> i32 #cc_std #link_name "ScreenToClient" ---;
|
||||||
|
post_quit_message :: proc(exit_code: i32) #cc_std #link_name "PostQuitMessage" ---;
|
||||||
|
set_window_text_a :: proc(hwnd: Hwnd, c_string: ^u8) -> Bool #cc_std #link_name "SetWindowTextA" ---;
|
||||||
|
register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 #cc_std #link_name "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 #cc_std #link_name "CreateWindowExA" ---;
|
||||||
|
|
||||||
|
show_window :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool #cc_std #link_name "ShowWindow" ---;
|
||||||
|
translate_message :: proc(msg: ^Msg) -> Bool #cc_std #link_name "TranslateMessage" ---;
|
||||||
|
dispatch_message_a :: proc(msg: ^Msg) -> Lresult #cc_std #link_name "DispatchMessageA" ---;
|
||||||
|
update_window :: proc(hwnd: Hwnd) -> Bool #cc_std #link_name "UpdateWindow" ---;
|
||||||
|
get_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max : u32) -> Bool #cc_std #link_name "GetMessageA" ---;
|
||||||
|
peek_message_a :: proc(msg: ^Msg, hwnd: Hwnd,
|
||||||
|
msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool #cc_std #link_name "PeekMessageA" ---;
|
||||||
|
|
||||||
|
|
||||||
|
post_message :: proc(hwnd: Hwnd, msg, wparam, lparam : u32) -> Bool #cc_std #link_name "PostMessageA" ---;
|
||||||
|
|
||||||
|
def_window_proc_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult #cc_std #link_name "DefWindowProcA" ---;
|
||||||
|
|
||||||
|
adjust_window_rect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool #cc_std #link_name "AdjustWindowRect" ---;
|
||||||
|
get_active_window :: proc() -> Hwnd #cc_std #link_name "GetActiveWindow" ---;
|
||||||
|
|
||||||
|
destroy_window :: proc(wnd: Hwnd) -> Bool #cc_std #link_name "DestroyWindow" ---;
|
||||||
|
describe_pixel_format :: proc(dc: Hdc, pixel_format: i32, bytes: u32, pfd: ^Pixel_Format_Descriptor) -> i32 #cc_std #link_name "DescribePixelFormat" ---;
|
||||||
|
|
||||||
|
get_monitor_info_a :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool #cc_std #link_name "GetMonitor_InfoA" ---;
|
||||||
|
monitor_from_window :: proc(wnd: Hwnd, flags : u32) -> Hmonitor #cc_std #link_name "MonitorFromWindow" ---;
|
||||||
|
|
||||||
|
set_window_pos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) #cc_std #link_name "SetWindowPos" ---;
|
||||||
|
|
||||||
|
get_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #cc_std #link_name "GetWindowPlacement" ---;
|
||||||
|
set_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #cc_std #link_name "SetWindowPlacement" ---;
|
||||||
|
get_window_rect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool #cc_std #link_name "GetWindowRect" ---;
|
||||||
|
|
||||||
|
get_window_long_ptr_a :: proc(wnd: Hwnd, index: i32) -> i64 #cc_std #link_name "GetWindowLongPtrA" ---;
|
||||||
|
set_window_long_ptr_a :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #cc_std #link_name "SetWindowLongPtrA" ---;
|
||||||
|
|
||||||
|
get_window_text :: proc(wnd: Hwnd, str: ^u8, maxCount: i32) -> i32 #cc_std #link_name "GetWindowText" ---;
|
||||||
|
|
||||||
|
get_client_rect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool #cc_std #link_name "GetClientRect" ---;
|
||||||
|
|
||||||
|
get_dc :: proc(h: Hwnd) -> Hdc #cc_std #link_name "GetDC" ---;
|
||||||
|
release_dc :: proc(wnd: Hwnd, hdc: Hdc) -> i32 #cc_std #link_name "ReleaseDC" ---;
|
||||||
|
|
||||||
|
map_virtual_key :: proc(scancode : u32, map_type : u32) -> u32 #cc_std #link_name "MapVirtualKeyA" ---;
|
||||||
|
|
||||||
|
get_key_state :: proc(v_key: i32) -> i16 #cc_std #link_name "GetKeyState" ---;
|
||||||
|
get_async_key_state :: proc(v_key: i32) -> i16 #cc_std #link_name "GetAsyncKeyState" ---;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign gdi32 {
|
||||||
|
get_stock_object :: proc(fn_object: i32) -> Hgdiobj #cc_std #link_name "GetStockObject" ---;
|
||||||
|
|
||||||
|
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 #cc_std #link_name "StretchDIBits" ---;
|
||||||
|
|
||||||
|
set_pixel_format :: proc(hdc: Hdc, pixel_format: i32, pfd: ^Pixel_Format_Descriptor) -> Bool #cc_std #link_name "SetPixelFormat" ---;
|
||||||
|
choose_pixel_format :: proc(hdc: Hdc, pfd: ^Pixel_Format_Descriptor) -> i32 #cc_std #link_name "ChoosePixelFormat" ---;
|
||||||
|
swap_buffers :: proc(hdc: Hdc) -> Bool #cc_std #link_name "SwapBuffers" ---;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign shell32 {
|
||||||
|
command_line_to_argv_w :: proc(cmd_list: ^u16, num_args: ^i32) -> ^^u16 #cc_std #link_name "CommandLineToArgvW" ---;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign winmm {
|
||||||
|
time_get_time :: proc() -> u32 #cc_std #link_name "timeGetTime" ---;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
get_query_performance_frequency :: proc() -> i64 {
|
||||||
|
r: i64;
|
||||||
|
query_performance_frequency(&r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
is_key_down :: proc(key: Key_Code) -> bool #inline { return get_async_key_state(i32(key)) < 0; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
Monitor_Info :: struct #ordered {
|
||||||
|
size: u32;
|
||||||
|
monitor: Rect;
|
||||||
|
work: Rect;
|
||||||
|
flags: u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window_Placement :: struct #ordered {
|
||||||
|
length: u32;
|
||||||
|
flags: u32;
|
||||||
|
show_cmd: u32;
|
||||||
|
min_pos: Point;
|
||||||
|
max_pos: Point;
|
||||||
|
normal_pos: Rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bitmap_Info_Header :: 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: Bitmap_Info_Header;
|
||||||
|
colors: [1]Rgb_Quad;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rgb_Quad :: struct #ordered {blue, green, red, reserved: u8}
|
||||||
|
|
||||||
|
|
||||||
|
Key_Code :: 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,76 @@
|
|||||||
|
_ :: compile_assert(ODIN_OS == "windows");
|
||||||
|
|
||||||
|
import win32 "sys/windows.odin";
|
||||||
|
|
||||||
|
Thread :: struct {
|
||||||
|
using specific: Os_Specific;
|
||||||
|
procedure: Proc;
|
||||||
|
data: any;
|
||||||
|
user_index: int;
|
||||||
|
|
||||||
|
init_context: Context;
|
||||||
|
use_init_context: bool;
|
||||||
|
|
||||||
|
Proc :: #type proc(^Thread) -> int;
|
||||||
|
Os_Specific :: struct {
|
||||||
|
win32_thread: win32.Handle;
|
||||||
|
win32_thread_id: u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
create :: proc(procedure: Thread.Proc) -> ^Thread {
|
||||||
|
win32_thread_id: u32;
|
||||||
|
|
||||||
|
__windows_thread_entry_proc :: proc(data: rawptr) -> i32 #cc_c {
|
||||||
|
if data == nil do return 0;
|
||||||
|
|
||||||
|
t := cast(^Thread)data;
|
||||||
|
|
||||||
|
c := context;
|
||||||
|
if t.use_init_context {
|
||||||
|
c = t.init_context;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit := 0;
|
||||||
|
push_context c {
|
||||||
|
exit = t.procedure(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cast(i32)exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
win32_thread_proc := cast(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);
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
is_signed :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
match i in type_info_base(info).variant {
|
||||||
|
case Type_Info.Integer: return i.signed;
|
||||||
|
case Type_Info.Float: return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_integer :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Integer);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_rune :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Rune);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_float :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Float);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_complex :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Complex);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_any :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Any);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_string :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.String);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_boolean :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Boolean);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_pointer :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Pointer);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_procedure :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Procedure);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_array :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Array);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Dynamic_Array);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Map);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Slice);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_vector :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Vector);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_tuple :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Tuple);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_struct :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
s, ok := type_info_base(info).variant.(Type_Info.Struct);
|
||||||
|
return ok && !s.is_raw_union;
|
||||||
|
}
|
||||||
|
is_raw_union :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
s, ok := type_info_base(info).variant.(Type_Info.Struct);
|
||||||
|
return ok && s.is_raw_union;
|
||||||
|
}
|
||||||
|
is_union :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Union);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
is_enum :: proc(info: ^Type_Info) -> bool {
|
||||||
|
if info == nil do return false;
|
||||||
|
_, ok := type_info_base(info).variant.(Type_Info.Enum);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
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 do if r >= _surr_self do n += 1;
|
||||||
|
|
||||||
|
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 += 1;
|
||||||
|
|
||||||
|
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 += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+161
-111
@@ -1,18 +1,34 @@
|
|||||||
RUNE_ERROR :: '\ufffd'
|
RUNE_ERROR :: '\ufffd';
|
||||||
RUNE_SELF :: 0x80
|
RUNE_SELF :: 0x80;
|
||||||
RUNE_BOM :: 0xfeff
|
RUNE_BOM :: 0xfeff;
|
||||||
RUNE_EOF :: ~(0 as rune)
|
RUNE_EOF :: ~rune(0);
|
||||||
MAX_RUNE :: '\U0010ffff'
|
MAX_RUNE :: '\U0010ffff';
|
||||||
UTF_MAX :: 4
|
UTF_MAX :: 4;
|
||||||
|
|
||||||
|
SURROGATE_MIN :: 0xd800;
|
||||||
|
SURROGATE_MAX :: 0xdfff;
|
||||||
|
|
||||||
SURROGATE_MIN :: 0xd800
|
T1 :: 0b0000_0000;
|
||||||
SURROGATE_MAX :: 0xdfff
|
TX :: 0b1000_0000;
|
||||||
|
T2 :: 0b1100_0000;
|
||||||
|
T3 :: 0b1110_0000;
|
||||||
|
T4 :: 0b1111_0000;
|
||||||
|
T5 :: 0b1111_1000;
|
||||||
|
|
||||||
|
MASKX :: 0b0011_1111;
|
||||||
|
MASK2 :: 0b0001_1111;
|
||||||
|
MASK3 :: 0b0000_1111;
|
||||||
|
MASK4 :: 0b0000_0111;
|
||||||
|
|
||||||
Accept_Range :: struct {
|
RUNE1_MAX :: 1<<7 - 1;
|
||||||
lo, hi: u8
|
RUNE2_MAX :: 1<<11 - 1;
|
||||||
}
|
RUNE3_MAX :: 1<<16 - 1;
|
||||||
|
|
||||||
|
// The default lowest and highest continuation byte.
|
||||||
|
LOCB :: 0b1000_0000;
|
||||||
|
HICB :: 0b1011_1111;
|
||||||
|
|
||||||
|
Accept_Range :: struct {lo, hi: u8};
|
||||||
|
|
||||||
accept_ranges := [5]Accept_Range{
|
accept_ranges := [5]Accept_Range{
|
||||||
{0x80, 0xbf},
|
{0x80, 0xbf},
|
||||||
@@ -20,9 +36,9 @@ accept_ranges := [5]Accept_Range{
|
|||||||
{0x80, 0x9f},
|
{0x80, 0x9f},
|
||||||
{0x90, 0xbf},
|
{0x90, 0xbf},
|
||||||
{0x80, 0x8f},
|
{0x80, 0x8f},
|
||||||
}
|
};
|
||||||
|
|
||||||
accept_sizes := [256]byte{
|
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, // 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, // 0x10-0x1f
|
||||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||||
@@ -40,177 +56,211 @@ accept_sizes := [256]byte{
|
|||||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
|
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
|
||||||
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
|
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
|
||||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||||
}
|
};
|
||||||
|
|
||||||
encode_rune :: proc(r_: rune) -> ([4]byte, int) {
|
encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||||
r := r_
|
buf: [4]u8;
|
||||||
buf: [4]byte
|
i := u32(r);
|
||||||
i := r as u32
|
mask: u8 : 0x3f;
|
||||||
mask: byte : 0x3f
|
|
||||||
if i <= 1<<7-1 {
|
if i <= 1<<7-1 {
|
||||||
buf[0] = r as byte
|
buf[0] = u8(r);
|
||||||
return buf, 1
|
return buf, 1;
|
||||||
}
|
}
|
||||||
if i <= 1<<11-1 {
|
if i <= 1<<11-1 {
|
||||||
buf[0] = 0xc0 | (r>>6) as byte
|
buf[0] = 0xc0 | u8(r>>6);
|
||||||
buf[1] = 0x80 | (r) as byte & mask
|
buf[1] = 0x80 | u8(r) & mask;
|
||||||
return buf, 2
|
return buf, 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid or Surrogate range
|
// Invalid or Surrogate range
|
||||||
if i > 0x0010ffff ||
|
if i > 0x0010ffff ||
|
||||||
(0xd800 <= i && i <= 0xdfff) {
|
(0xd800 <= i && i <= 0xdfff) {
|
||||||
r = 0xfffd
|
r = 0xfffd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i <= 1<<16-1 {
|
if i <= 1<<16-1 {
|
||||||
buf[0] = 0xe0 | (r>>12) as byte
|
buf[0] = 0xe0 | u8(r>>12);
|
||||||
buf[1] = 0x80 | (r>>6) as byte & mask
|
buf[1] = 0x80 | u8(r>>6) & mask;
|
||||||
buf[2] = 0x80 | (r) as byte & mask
|
buf[2] = 0x80 | u8(r) & mask;
|
||||||
return buf, 3
|
return buf, 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[0] = 0xf0 | (r>>18) as byte
|
buf[0] = 0xf0 | u8(r>>18);
|
||||||
buf[1] = 0x80 | (r>>12) as byte & mask
|
buf[1] = 0x80 | u8(r>>12) & mask;
|
||||||
buf[2] = 0x80 | (r>>6) as byte & mask
|
buf[2] = 0x80 | u8(r>>6) & mask;
|
||||||
buf[3] = 0x80 | (r) as byte & mask
|
buf[3] = 0x80 | u8(r) & mask;
|
||||||
return buf, 4
|
return buf, 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
decode_rune :: proc(s: string) -> (rune, int) {
|
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]u8)s); }
|
||||||
n := s.count
|
decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||||
|
n := len(s);
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return RUNE_ERROR, 0
|
return RUNE_ERROR, 0;
|
||||||
}
|
}
|
||||||
b0 := s[0]
|
s0 := s[0];
|
||||||
x := accept_sizes[b0]
|
x := accept_sizes[s0];
|
||||||
if x >= 0xf0 {
|
if x >= 0xF0 {
|
||||||
mask := (x as rune << 31) >> 31 // all zeros or all ones
|
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
|
||||||
return (b0 as rune) &~ mask | RUNE_ERROR&mask, 1
|
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
|
||||||
}
|
}
|
||||||
size := x & 7
|
sz := x & 7;
|
||||||
ar := accept_ranges[x>>4]
|
accept := accept_ranges[x>>4];
|
||||||
if n < size as int {
|
if n < int(sz) {
|
||||||
return RUNE_ERROR, 1
|
return RUNE_ERROR, 1;
|
||||||
}
|
}
|
||||||
b1 := s[1]
|
b1 := s[1];
|
||||||
if b1 < ar.lo || ar.hi < b1 {
|
if b1 < accept.lo || accept.hi < b1 {
|
||||||
return RUNE_ERROR, 1
|
return RUNE_ERROR, 1;
|
||||||
}
|
}
|
||||||
|
if sz == 2 {
|
||||||
MASK_X :: 0b00111111
|
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
|
||||||
MASK_2 :: 0b00011111
|
|
||||||
MASK_3 :: 0b00001111
|
|
||||||
MASK_4 :: 0b00000111
|
|
||||||
|
|
||||||
if size == 2 {
|
|
||||||
return (b0&MASK_2) as rune <<6 | (b1&MASK_X) as rune, 2
|
|
||||||
}
|
}
|
||||||
b2 := s[2]
|
b2 := s[2];
|
||||||
if b2 < 0x80 || 0xbf < b2 {
|
if b2 < LOCB || HICB < b2 {
|
||||||
return RUNE_ERROR, 1
|
return RUNE_ERROR, 1;
|
||||||
}
|
}
|
||||||
if size == 3 {
|
if sz == 3 {
|
||||||
return (b0&MASK_3) as rune <<12 | (b1&MASK_X) as rune <<6 | (b2&MASK_X) as rune, 3
|
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
|
||||||
}
|
}
|
||||||
b3 := s[3]
|
b3 := s[3];
|
||||||
if b3 < 0x80 || 0xbf < b3 {
|
if b3 < LOCB || HICB < b3 {
|
||||||
return RUNE_ERROR, 1
|
return RUNE_ERROR, 1;
|
||||||
}
|
}
|
||||||
return (b0&MASK_4) as rune <<18 | (b1&MASK_X) as rune <<12 | (b3&MASK_X) as rune <<6 | (b3&MASK_X) as rune, 4
|
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]u8)s); }
|
||||||
|
decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||||
|
r: rune;
|
||||||
|
size: int;
|
||||||
|
start, end, limit: int;
|
||||||
|
|
||||||
|
end = len(s);
|
||||||
|
if end == 0 {
|
||||||
|
return RUNE_ERROR, 0;
|
||||||
|
}
|
||||||
|
start = end-1;
|
||||||
|
r = rune(s[start]);
|
||||||
|
if r < RUNE_SELF {
|
||||||
|
return r, 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
limit = max(end - UTF_MAX, 0);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
if start+size != end {
|
||||||
|
return RUNE_ERROR, 1;
|
||||||
|
}
|
||||||
|
return r, size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
valid_rune :: proc(r: rune) -> bool {
|
valid_rune :: proc(r: rune) -> bool {
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
return false
|
return false;
|
||||||
} else if SURROGATE_MIN <= r && r <= SURROGATE_MAX {
|
} else if SURROGATE_MIN <= r && r <= SURROGATE_MAX {
|
||||||
return false
|
return false;
|
||||||
} else if r > MAX_RUNE {
|
} else if r > MAX_RUNE {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
valid_string :: proc(s: string) -> bool {
|
valid_string :: proc(s: string) -> bool {
|
||||||
n := s.count
|
n := len(s);
|
||||||
for i := 0; i < n; {
|
for i := 0; i < n; {
|
||||||
si := s[i]
|
si := s[i];
|
||||||
if si < RUNE_SELF { // ascii
|
if si < RUNE_SELF { // ascii
|
||||||
i++
|
i += 1;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
x := accept_sizes[si]
|
x := accept_sizes[si];
|
||||||
if x == 0xf1 {
|
if x == 0xf1 {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
size := (x & 7) as int
|
size := int(x & 7);
|
||||||
if i+size > n {
|
if i+size > n {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
ar := accept_ranges[x>>4]
|
ar := accept_ranges[x>>4];
|
||||||
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
||||||
return false
|
return false;
|
||||||
} else if size == 2 {
|
} else if size == 2 {
|
||||||
// Okay
|
// Okay
|
||||||
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
||||||
return false
|
return false;
|
||||||
} else if size == 3 {
|
} else if size == 3 {
|
||||||
// Okay
|
// Okay
|
||||||
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
i += size
|
i += size;
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
rune_count :: proc(s: string) -> int {
|
rune_start :: proc(b: u8) -> bool #inline { return b&0xc0 != 0x80; }
|
||||||
count := 0
|
|
||||||
n := s.count
|
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]u8)s); }
|
||||||
for i := 0; i < n; count++ {
|
rune_count :: proc(s: []u8) -> int {
|
||||||
si := s[i]
|
count := 0;
|
||||||
|
n := len(s);
|
||||||
|
|
||||||
|
for i := 0; i < n; {
|
||||||
|
defer count += 1;
|
||||||
|
si := s[i];
|
||||||
if si < RUNE_SELF { // ascii
|
if si < RUNE_SELF { // ascii
|
||||||
i++
|
i += 1;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
x := accept_sizes[si]
|
x := accept_sizes[si];
|
||||||
if x == 0xf1 {
|
if x == 0xf1 {
|
||||||
i++
|
i += 1;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
size := (x & 7) as int
|
size := int(x & 7);
|
||||||
if i+size > n {
|
if i+size > n {
|
||||||
i++
|
i += 1;
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
ar := accept_ranges[x>>4]
|
ar := accept_ranges[x>>4];
|
||||||
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
||||||
size = 1
|
size = 1;
|
||||||
} else if size == 2 {
|
} else if size == 2 {
|
||||||
// Okay
|
// Okay
|
||||||
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
||||||
size = 1
|
size = 1;
|
||||||
} else if size == 3 {
|
} else if size == 3 {
|
||||||
// Okay
|
// Okay
|
||||||
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
||||||
size = 1
|
size = 1;
|
||||||
}
|
}
|
||||||
i += size
|
i += size;
|
||||||
}
|
}
|
||||||
return count
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rune_size :: proc(r: rune) -> int {
|
rune_size :: proc(r: rune) -> int {
|
||||||
match {
|
match {
|
||||||
case r < 0: return -1
|
case r < 0: return -1;
|
||||||
case r <= 1<<7 - 1: return 1
|
case r <= 1<<7 - 1: return 1;
|
||||||
case r <= 1<<11 - 1: return 2
|
case r <= 1<<11 - 1: return 2;
|
||||||
case SURROGATE_MIN <= r && r <= SURROGATE_MAX: return -1
|
case SURROGATE_MIN <= r && r <= SURROGATE_MAX: return -1;
|
||||||
case r <= 1<<16 - 1: return 3
|
case r <= 1<<16 - 1: return 3;
|
||||||
case r <= MAX_RUNE: return 4
|
case r <= MAX_RUNE: return 4;
|
||||||
}
|
}
|
||||||
return -1
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
-495
@@ -1,495 +0,0 @@
|
|||||||
#foreign_system_library "user32"
|
|
||||||
#foreign_system_library "gdi32"
|
|
||||||
|
|
||||||
_:= compile_assert(ODIN_OS == "windows")
|
|
||||||
|
|
||||||
HANDLE :: type rawptr
|
|
||||||
HWND :: type HANDLE
|
|
||||||
HDC :: type HANDLE
|
|
||||||
HINSTANCE :: type HANDLE
|
|
||||||
HICON :: type HANDLE
|
|
||||||
HCURSOR :: type HANDLE
|
|
||||||
HMENU :: type HANDLE
|
|
||||||
HBRUSH :: type HANDLE
|
|
||||||
HGDIOBJ :: type HANDLE
|
|
||||||
HMODULE :: type HANDLE
|
|
||||||
WPARAM :: type uint
|
|
||||||
LPARAM :: type int
|
|
||||||
LRESULT :: type int
|
|
||||||
ATOM :: type i16
|
|
||||||
BOOL :: type i32
|
|
||||||
WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
|
|
||||||
|
|
||||||
INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE
|
|
||||||
|
|
||||||
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_CLOSE :: 0x0010
|
|
||||||
WM_QUIT :: 0x0012
|
|
||||||
WM_KEYDOWN :: 0x0100
|
|
||||||
WM_KEYUP :: 0x0101
|
|
||||||
|
|
||||||
PM_REMOVE :: 1
|
|
||||||
|
|
||||||
COLOR_BACKGROUND :: 1 as HBRUSH
|
|
||||||
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 {
|
|
||||||
low_date_time, high_date_time: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
BY_HANDLE_FILE_INFORMATION :: 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
|
|
||||||
}
|
|
||||||
|
|
||||||
WIN32_FILE_ATTRIBUTE_DATA :: struct #ordered {
|
|
||||||
file_attributes: u32
|
|
||||||
creation_time,
|
|
||||||
last_access_time,
|
|
||||||
last_write_time: FILETIME
|
|
||||||
file_size_high,
|
|
||||||
file_size_low: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
GET_FILEEX_INFO_LEVELS :: type i32
|
|
||||||
GetFileExInfoStandard : GET_FILEEX_INFO_LEVELS : 0
|
|
||||||
GetFileExMaxInfoLevel : GET_FILEEX_INFO_LEVELS : 1
|
|
||||||
|
|
||||||
GetLastError :: proc() -> i32 #foreign #dll_import
|
|
||||||
ExitProcess :: proc(exit_code: u32) #foreign #dll_import
|
|
||||||
GetDesktopWindow :: proc() -> HWND #foreign #dll_import
|
|
||||||
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign #dll_import
|
|
||||||
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign #dll_import
|
|
||||||
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign #dll_import
|
|
||||||
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign #dll_import
|
|
||||||
PostQuitMessage :: proc(exit_code: i32) #foreign #dll_import
|
|
||||||
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign #dll_import
|
|
||||||
|
|
||||||
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign #dll_import
|
|
||||||
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign #dll_import
|
|
||||||
|
|
||||||
Sleep :: proc(ms: i32) -> i32 #foreign #dll_import
|
|
||||||
|
|
||||||
OutputDebugStringA :: proc(c_str: ^u8) #foreign #dll_import
|
|
||||||
|
|
||||||
|
|
||||||
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign #dll_import
|
|
||||||
CreateWindowExA :: 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 #dll_import
|
|
||||||
|
|
||||||
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign #dll_import
|
|
||||||
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign #dll_import
|
|
||||||
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign #dll_import
|
|
||||||
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign #dll_import
|
|
||||||
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND,
|
|
||||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign #dll_import
|
|
||||||
|
|
||||||
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign #dll_import
|
|
||||||
|
|
||||||
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign #dll_import
|
|
||||||
|
|
||||||
|
|
||||||
GetQueryPerformanceFrequency :: proc() -> i64 {
|
|
||||||
r: i64
|
|
||||||
QueryPerformanceFrequency(^r)
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
GetCommandLineA :: proc() -> ^u8 #foreign #dll_import
|
|
||||||
GetSystemMetrics :: proc(index: i32) -> i32 #foreign #dll_import
|
|
||||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
|
||||||
|
|
||||||
// File Stuff
|
|
||||||
|
|
||||||
CloseHandle :: proc(h: HANDLE) -> i32 #foreign #dll_import
|
|
||||||
GetStdHandle :: proc(h: i32) -> HANDLE #foreign #dll_import
|
|
||||||
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
|
|
||||||
security: rawptr,
|
|
||||||
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign #dll_import
|
|
||||||
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign #dll_import
|
|
||||||
WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> i32 #foreign #dll_import
|
|
||||||
|
|
||||||
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign #dll_import
|
|
||||||
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign #dll_import
|
|
||||||
GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign #dll_import
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HeapAlloc :: proc(h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign #dll_import
|
|
||||||
HeapReAlloc :: proc(h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign #dll_import
|
|
||||||
HeapFree :: proc(h: HANDLE, flags: u32, memory: rawptr) -> BOOL #foreign #dll_import
|
|
||||||
GetProcessHeap :: proc() -> HANDLE #foreign #dll_import
|
|
||||||
|
|
||||||
|
|
||||||
HEAP_ZERO_MEMORY :: 0x00000008
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GDI
|
|
||||||
|
|
||||||
BITMAPINFO :: struct #ordered {
|
|
||||||
HEADER :: 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
|
|
||||||
}
|
|
||||||
using header: HEADER
|
|
||||||
colors: [1]RGBQUAD
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
RGBQUAD :: struct #ordered {
|
|
||||||
blue, green, red, reserved: byte
|
|
||||||
}
|
|
||||||
|
|
||||||
BI_RGB :: 0
|
|
||||||
DIB_RGB_COLORS :: 0x00
|
|
||||||
SRCCOPY : u32 : 0x00cc0020
|
|
||||||
|
|
||||||
StretchDIBits :: 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 #dll_import
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LoadLibraryA :: proc(c_str: ^u8) -> HMODULE #foreign
|
|
||||||
FreeLibrary :: proc(h: HMODULE) #foreign
|
|
||||||
GetProcAddress :: proc(h: HMODULE, c_str: ^u8) -> proc() #foreign
|
|
||||||
|
|
||||||
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
HGLRC :: type HANDLE
|
|
||||||
PROC :: type proc()
|
|
||||||
wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC
|
|
||||||
|
|
||||||
|
|
||||||
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: byte
|
|
||||||
|
|
||||||
layer_mask,
|
|
||||||
visible_mask,
|
|
||||||
damage_mask: u32
|
|
||||||
}
|
|
||||||
|
|
||||||
GetDC :: proc(h: HANDLE) -> HDC #foreign
|
|
||||||
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR ) -> BOOL #foreign #dll_import
|
|
||||||
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign #dll_import
|
|
||||||
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign #dll_import
|
|
||||||
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign #dll_import
|
|
||||||
|
|
||||||
WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091
|
|
||||||
WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092
|
|
||||||
WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126
|
|
||||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001
|
|
||||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002
|
|
||||||
|
|
||||||
wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign #dll_import
|
|
||||||
wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign #dll_import
|
|
||||||
wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign #dll_import
|
|
||||||
wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign #dll_import
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
GetKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
|
||||||
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
|
||||||
|
|
||||||
is_key_down :: proc(key: Key_Code) -> bool {
|
|
||||||
return GetAsyncKeyState(key as i32) < 0
|
|
||||||
}
|
|
||||||
|
|
||||||
Key_Code :: 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,
|
|
||||||
OEM_CLEAR = 0xFE,
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
@echo off
|
||||||
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
|
set file_input=%1
|
||||||
|
set name=%1
|
||||||
|
FOR %%f IN (name) do (
|
||||||
|
FOR %%g in (!%%f!) do set "%%f=%%~ng"
|
||||||
|
)
|
||||||
|
|
||||||
|
call clang -O2 -c %file_input% -o %name%.o ^
|
||||||
|
&& call ar %name%.o -rcs %name%.lib
|
||||||
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
@@ -0,0 +1,21 @@
|
|||||||
|
# Odin Roadmap
|
||||||
|
|
||||||
|
Not in any particular order
|
||||||
|
|
||||||
|
* Custom backend to replace LLVM
|
||||||
|
- Improve SSA design to accommodate for lowering to a "bytecode"
|
||||||
|
- SSA optimizations
|
||||||
|
- COFF generation
|
||||||
|
- linker
|
||||||
|
* Type safe "macros"
|
||||||
|
* Documentation generator for "Entities"
|
||||||
|
* Multiple architecture support
|
||||||
|
* Inline assembly
|
||||||
|
* Linking options
|
||||||
|
- Executable
|
||||||
|
- Static/Dynamic Library
|
||||||
|
* Debug information
|
||||||
|
- pdb format too
|
||||||
|
* Command line tooling
|
||||||
|
* Compiler internals:
|
||||||
|
- Big numbers library
|
||||||
+4
-2
@@ -1,8 +1,10 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
|
|
||||||
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
|
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 _NO_DEBUG_HEAP=1
|
||||||
|
|
||||||
set path=w:\Odin\misc;%path%
|
set path=w:\Odin\misc;%path%
|
||||||
|
|||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
# Odin Roadmap
|
|
||||||
|
|
||||||
Not in any particular order
|
|
||||||
|
|
||||||
* 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
|
|
||||||
* Parametric Polymorphism
|
|
||||||
* Documentation Generator for "Entities"
|
|
||||||
* Multiple Architecture support
|
|
||||||
* Linking Options
|
|
||||||
- Executable
|
|
||||||
- Static/Dynamic Library
|
|
||||||
* Debug Information
|
|
||||||
- pdb format too
|
|
||||||
* Command Line Tooling
|
|
||||||
* Compiler Internals:
|
|
||||||
- Big numbers library
|
|
||||||
- Cyclic Type Checking (at the moment will cause compiler to go into an infinite loop)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
|
|
||||||
rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h
|
|
||||||
+102
-105
@@ -1,104 +1,7 @@
|
|||||||
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
|
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
|
||||||
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
||||||
|
|
||||||
#define Array(Type_) struct { \
|
#if 1
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// TODO(bill): Resize rather than copy and delete
|
|
||||||
void *new_data = gb_alloc(x->allocator, element_size*capacity);
|
|
||||||
gb_memmove(new_data, x->e, element_size*x->count);
|
|
||||||
gb_free(x->allocator, x->e);
|
|
||||||
x->capacity = capacity;
|
|
||||||
x->e = new_data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Array {
|
struct Array {
|
||||||
gbAllocator allocator;
|
gbAllocator allocator;
|
||||||
@@ -107,12 +10,16 @@ struct Array {
|
|||||||
isize capacity;
|
isize capacity;
|
||||||
|
|
||||||
T &operator[](isize index) {
|
T &operator[](isize index) {
|
||||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
#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];
|
return data[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
T const &operator[](isize index) const {
|
T const &operator[](isize index) const {
|
||||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
#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];
|
return data[index];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -128,7 +35,6 @@ template <typename T> void array_reserve (Array<T> *array, isize capacit
|
|||||||
template <typename T> void array_resize (Array<T> *array, isize count);
|
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_set_capacity(Array<T> *array, isize capacity);
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
||||||
array->allocator = a;
|
array->allocator = a;
|
||||||
@@ -158,7 +64,7 @@ Array<T> array_make(T *data, isize count, isize capacity) {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void array_free(Array<T> *array) {
|
void array_free(Array<T> *array) {
|
||||||
if (array->allocator.proc != NULL) {
|
if (array->allocator.proc != nullptr) {
|
||||||
gb_free(array->allocator, array->data);
|
gb_free(array->allocator, array->data);
|
||||||
}
|
}
|
||||||
array->count = 0;
|
array->count = 0;
|
||||||
@@ -220,7 +126,7 @@ void array_set_capacity(Array<T> *array, isize capacity) {
|
|||||||
array_resize(array, capacity);
|
array_resize(array, capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
T *new_data = NULL;
|
T *new_data = nullptr;
|
||||||
if (capacity > 0) {
|
if (capacity > 0) {
|
||||||
new_data = gb_alloc_array(array->allocator, T, capacity);
|
new_data = gb_alloc_array(array->allocator, T, capacity);
|
||||||
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
|
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
|
||||||
@@ -230,6 +136,97 @@ void array_set_capacity(Array<T> *array, isize capacity) {
|
|||||||
array->capacity = capacity;
|
array->capacity = capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if 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_) != 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
|
||||||
|
|
||||||
@@ -0,0 +1,383 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
// 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 opt_flags;
|
||||||
|
String llc_flags;
|
||||||
|
String link_flags;
|
||||||
|
bool is_dll;
|
||||||
|
bool generate_docs;
|
||||||
|
i32 optimization_level;
|
||||||
|
bool show_timings;
|
||||||
|
bool keep_temp_files;
|
||||||
|
|
||||||
|
gbAffinity affinity;
|
||||||
|
isize thread_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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(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;
|
||||||
|
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 (;;) {
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
gb_mutex_lock(&string_buffer_mutex);
|
||||||
|
p = realpath(cast(char *)s.text, 0);
|
||||||
|
gb_mutex_unlock(&string_buffer_mutex);
|
||||||
|
if(p == nullptr) 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[0], base_dir.len); i += base_dir.len;
|
||||||
|
gb_memmove(str+i, &path[0], 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[0], module_dir.len);
|
||||||
|
gb_memmove(str+module_dir.len, core, core_len);
|
||||||
|
gb_memmove(str+module_dir.len+core_len, &path[0], path.len);
|
||||||
|
str[str_len] = '\0';
|
||||||
|
|
||||||
|
res = path_to_fullpath(a, make_string(str, str_len));
|
||||||
|
gb_free(heap_allocator(), str);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String const ODIN_VERSION = str_lit("0.6.2");
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
bc->ODIN_OS = str_lit("windows");
|
||||||
|
#elif defined(GB_SYSTEM_OSX)
|
||||||
|
bc->ODIN_OS = str_lit("osx");
|
||||||
|
#else
|
||||||
|
bc->ODIN_OS = str_lit("linux");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GB_ARCH_64_BIT)
|
||||||
|
bc->ODIN_ARCH = str_lit("amd64");
|
||||||
|
#else
|
||||||
|
bc->ODIN_ARCH = str_lit("x86");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
u16 x = 1;
|
||||||
|
bool big = !(*cast(u8 *)&x);
|
||||||
|
bc->ODIN_ENDIAN = big ? str_lit("big") : str_lit("little");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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(bc->ODIN_ARCH == "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 (bc->ODIN_ARCH == "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 (bc->ODIN_ARCH == "x86") {
|
||||||
|
bc->word_size = 4;
|
||||||
|
bc->max_align = 8;
|
||||||
|
bc->llc_flags = str_lit("-march=x86 ");
|
||||||
|
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
|
||||||
|
} else {
|
||||||
|
gb_printf_err("This current architecture is not supported");
|
||||||
|
gb_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
isize opt_max = 1023;
|
||||||
|
char *opt_flags_string = gb_alloc_array(heap_allocator(), char, opt_max+1);
|
||||||
|
isize opt_len = 0;
|
||||||
|
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
||||||
|
if (bc->optimization_level != 0) {
|
||||||
|
opt_len = gb_snprintf(opt_flags_string, opt_max, "-O%d", bc->optimization_level);
|
||||||
|
} else {
|
||||||
|
opt_len = gb_snprintf(opt_flags_string, opt_max, "");
|
||||||
|
}
|
||||||
|
if (opt_len > 0) {
|
||||||
|
opt_len--;
|
||||||
|
}
|
||||||
|
bc->opt_flags = make_string(cast(u8 *)opt_flags_string, opt_len);
|
||||||
|
|
||||||
|
|
||||||
|
#undef LINK_FLAG_X64
|
||||||
|
#undef LINK_FLAG_X86
|
||||||
|
}
|
||||||
@@ -0,0 +1,768 @@
|
|||||||
|
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(operand->expr,
|
||||||
|
"Cannot assign built-in procedure `%s` in %.*s",
|
||||||
|
expr_str,
|
||||||
|
LIT(context_name));
|
||||||
|
|
||||||
|
operand->mode = Addressing_Invalid;
|
||||||
|
|
||||||
|
gb_string_free(expr_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (operand->mode == Addressing_Overload) {
|
||||||
|
if (e->type == nullptr) {
|
||||||
|
error(operand->expr, "Cannot determine type from overloaded procedure `%.*s`", LIT(operand->overload_entities[0]->token.string));
|
||||||
|
} else {
|
||||||
|
check_assignment(c, operand, e->type, str_lit("variable assignment"));
|
||||||
|
if (operand->mode != Addressing_Type) {
|
||||||
|
return operand->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->type == nullptr) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (e->type == nullptr) {
|
||||||
|
|
||||||
|
// 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, "Invalid use of untyped nil in %.*s", LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (t == t_invalid || is_type_untyped_undef(t)) {
|
||||||
|
error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
t = default_type(t);
|
||||||
|
}
|
||||||
|
if (is_type_polymorphic(t)) {
|
||||||
|
gbString str = type_to_string(t);
|
||||||
|
defer (gb_string_free(str));
|
||||||
|
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
return nullptr;
|
||||||
|
} else if (is_type_empty_union(t)) {
|
||||||
|
gbString str = type_to_string(t);
|
||||||
|
defer (gb_string_free(str));
|
||||||
|
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
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 nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNode *> inits, String context_name) {
|
||||||
|
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||||
|
defer (gb_temp_arena_memory_end(tmp));
|
||||||
|
|
||||||
|
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||||
|
// an extra allocation
|
||||||
|
Array<Operand> operands = {};
|
||||||
|
array_init(&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[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[i], context_name);
|
||||||
|
}
|
||||||
|
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||||
|
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 == nullptr) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operand->mode != Addressing_Constant) {
|
||||||
|
// TODO(bill): better error
|
||||||
|
gbString str = expr_to_string(operand->expr);
|
||||||
|
error(operand->expr, "`%s` is not a constant", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
if (e->type == nullptr) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!is_type_constant_type(operand->type)) {
|
||||||
|
gbString type_str = type_to_string(operand->type);
|
||||||
|
error(operand->expr, "Invalid constant type: `%s`", type_str);
|
||||||
|
gb_string_free(type_str);
|
||||||
|
if (e->type == nullptr) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->type == nullptr) { // 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 == nullptr);
|
||||||
|
String name = e->token.string;
|
||||||
|
Type *named = make_type_named(c->allocator, name, nullptr, e);
|
||||||
|
named->Named.type_name = e;
|
||||||
|
if (def != nullptr && 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(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 == nullptr);
|
||||||
|
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(type_expr, "Invalid constant type `%s`", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
e->type = t_invalid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e->type = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand operand = {};
|
||||||
|
|
||||||
|
if (init != nullptr) {
|
||||||
|
Entity *entity = nullptr;
|
||||||
|
if (init->kind == AstNode_Ident) {
|
||||||
|
entity = check_ident(c, &operand, init, nullptr, e->type, true);
|
||||||
|
} else if (init->kind == AstNode_SelectorExpr) {
|
||||||
|
entity = check_selector(c, &operand, init, e->type);
|
||||||
|
} else {
|
||||||
|
check_expr_or_type(c, &operand, init, e->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (operand.mode) {
|
||||||
|
case Addressing_Type: {
|
||||||
|
e->kind = Entity_TypeName;
|
||||||
|
|
||||||
|
DeclInfo *d = c->context.decl;
|
||||||
|
if (d->type_expr != nullptr) {
|
||||||
|
error(e->token, "A type declaration cannot have an type parameter");
|
||||||
|
}
|
||||||
|
d->type_expr = d->init_expr;
|
||||||
|
check_type_decl(c, e, d->type_expr, named_type);
|
||||||
|
return;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
// NOTE(bill): Check to see if the expression it to be aliases
|
||||||
|
#if 1
|
||||||
|
case Addressing_Builtin:
|
||||||
|
if (e->type != nullptr) {
|
||||||
|
error(type_expr, "A constant alias of a built-in procedure may not have a type initializer");
|
||||||
|
}
|
||||||
|
e->kind = Entity_Builtin;
|
||||||
|
e->Builtin.id = operand.builtin_id;
|
||||||
|
e->type = t_invalid;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case Addressing_Overload:
|
||||||
|
e->kind = Entity_Alias;
|
||||||
|
e->Alias.base = operand.overload_entities[0];
|
||||||
|
e->type = t_invalid;
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
if (entity != nullptr) {
|
||||||
|
switch (entity->kind) {
|
||||||
|
case Entity_Alias:
|
||||||
|
e->kind = Entity_Alias;
|
||||||
|
e->type = entity->type;
|
||||||
|
e->Alias.base = entity->Alias.base;
|
||||||
|
return;
|
||||||
|
case Entity_Procedure:
|
||||||
|
e->kind = Entity_Alias;
|
||||||
|
e->type = entity->type;
|
||||||
|
e->Alias.base = entity;
|
||||||
|
return;
|
||||||
|
case Entity_ImportName:
|
||||||
|
e->kind = Entity_ImportName;
|
||||||
|
e->type = entity->type;
|
||||||
|
e->ImportName.path = entity->ImportName.path;
|
||||||
|
e->ImportName.name = entity->ImportName.path;
|
||||||
|
e->ImportName.scope = entity->ImportName.scope;
|
||||||
|
e->ImportName.used = false;
|
||||||
|
return;
|
||||||
|
case Entity_LibraryName:
|
||||||
|
e->kind = Entity_LibraryName;
|
||||||
|
e->type = entity->type;
|
||||||
|
e->LibraryName.path = entity->LibraryName.path;
|
||||||
|
e->LibraryName.name = entity->LibraryName.path;
|
||||||
|
e->LibraryName.used = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init != nullptr) {
|
||||||
|
check_expr_or_type(c, &operand, init, e->type);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_init_constant(c, e, &operand);
|
||||||
|
|
||||||
|
if (operand.mode == Addressing_Invalid ||
|
||||||
|
base_type(operand.type) == t_invalid) {
|
||||||
|
gbString str = expr_to_string(init);
|
||||||
|
error(e->token, "Invalid declaration type `%s`", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
i64 sx = type_size_of(heap_allocator(), x);
|
||||||
|
i64 sy = type_size_of(heap_allocator(), y);
|
||||||
|
if (sx == sy) 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);
|
||||||
|
i64 sx = type_size_of(heap_allocator(), x);
|
||||||
|
i64 sy = type_size_of(heap_allocator(), y);
|
||||||
|
if (sx == sy) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!are_types_identical(x, y)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_entity_foreign_library(Checker *c, Entity *e) {
|
||||||
|
AstNode *ident = nullptr;
|
||||||
|
Entity **foreign_library = nullptr;
|
||||||
|
|
||||||
|
switch (e->kind) {
|
||||||
|
case Entity_Procedure:
|
||||||
|
ident = e->Procedure.foreign_library_ident;
|
||||||
|
foreign_library = &e->Procedure.foreign_library;
|
||||||
|
break;
|
||||||
|
case Entity_Variable:
|
||||||
|
ident = e->Variable.foreign_library_ident;
|
||||||
|
foreign_library = &e->Variable.foreign_library;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ident == nullptr) {
|
||||||
|
error(e->token, "foreign entiies must declare which library they are from");
|
||||||
|
} else if (ident->kind != AstNode_Ident) {
|
||||||
|
error(ident, "foreign library names must be an identifier");
|
||||||
|
} else {
|
||||||
|
String name = ident->Ident.token.string;
|
||||||
|
Entity *found = scope_lookup_entity(c->context.scope, name);
|
||||||
|
if (found == nullptr) {
|
||||||
|
if (is_blank_ident(name)) {
|
||||||
|
error(ident, "`_` cannot be used as a value type");
|
||||||
|
} else {
|
||||||
|
error(ident, "Undeclared name: %.*s", LIT(name));
|
||||||
|
}
|
||||||
|
} else if (found->kind != Entity_LibraryName) {
|
||||||
|
error(ident, "`%.*s` cannot be used as a library name", LIT(name));
|
||||||
|
} else {
|
||||||
|
// TODO(bill): Extra stuff to do with library names?
|
||||||
|
*foreign_library = found;
|
||||||
|
add_entity_use(c, ident, found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||||
|
GB_ASSERT(e->type == nullptr);
|
||||||
|
if (d->proc_lit->kind != AstNode_ProcLit) {
|
||||||
|
// TOOD(bill): Better error message
|
||||||
|
error(d->proc_lit, "Expected a procedure to check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *proc_type = e->type;
|
||||||
|
if (d->gen_proc_type != nullptr) {
|
||||||
|
proc_type = d->gen_proc_type;
|
||||||
|
} else {
|
||||||
|
proc_type = make_type_proc(c->allocator, e->scope, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
|
||||||
|
}
|
||||||
|
e->type = proc_type;
|
||||||
|
ast_node(pl, ProcLit, d->proc_lit);
|
||||||
|
|
||||||
|
check_open_scope(c, pl->type);
|
||||||
|
defer (check_close_scope(c));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auto prev_context = c->context;
|
||||||
|
c->context.allow_polymorphic_types = true;
|
||||||
|
check_procedure_type(c, proc_type, pl->type);
|
||||||
|
c->context = prev_context;
|
||||||
|
|
||||||
|
TypeProc *pt = &proc_type->Proc;
|
||||||
|
|
||||||
|
bool is_foreign = (pl->tags & ProcTag_foreign) != 0;
|
||||||
|
bool is_link_name = (pl->tags & ProcTag_link_name) != 0;
|
||||||
|
bool is_export = (pl->tags & ProcTag_export) != 0;
|
||||||
|
bool is_inline = (pl->tags & ProcTag_inline) != 0;
|
||||||
|
bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0;
|
||||||
|
bool is_require_results = (pl->tags & ProcTag_require_results) != 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (d->scope->is_file && e->token.string == "main") {
|
||||||
|
if (pt->param_count != 0 ||
|
||||||
|
pt->result_count != 0) {
|
||||||
|
gbString str = type_to_string(proc_type);
|
||||||
|
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||||
|
gb_string_free(str);
|
||||||
|
}
|
||||||
|
if (pt->calling_convention != ProcCC_Odin &&
|
||||||
|
pt->calling_convention != ProcCC_Contextless) {
|
||||||
|
error(e->token, "Procedure `main` cannot have a custom calling convention");
|
||||||
|
}
|
||||||
|
pt->calling_convention = ProcCC_Contextless;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_inline && is_no_inline) {
|
||||||
|
error(pl->type, "You cannot apply both `inline` and `no_inline` to a procedure");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_foreign && is_export) {
|
||||||
|
error(pl->type, "A foreign procedure cannot have an `export` tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->is_polymorphic) {
|
||||||
|
if (pl->body == nullptr) {
|
||||||
|
error(e->token, "Polymorphic procedures must have a body");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_foreign) {
|
||||||
|
error(e->token, "A foreign procedure cannot be a polymorphic");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->body != nullptr) {
|
||||||
|
if (is_foreign) {
|
||||||
|
error(pl->body, "A foreign procedure cannot have a body");
|
||||||
|
}
|
||||||
|
if (proc_type->Proc.c_vararg) {
|
||||||
|
error(pl->body, "A procedure with a `#c_vararg` field cannot have a body and must be foreign");
|
||||||
|
}
|
||||||
|
|
||||||
|
d->scope = c->context.scope;
|
||||||
|
|
||||||
|
GB_ASSERT(pl->body->kind == AstNode_BlockStmt);
|
||||||
|
if (!pt->is_polymorphic) {
|
||||||
|
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags);
|
||||||
|
}
|
||||||
|
} else if (!is_foreign) {
|
||||||
|
error(e->token, "Only a foreign procedure cannot have a body");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pt->result_count == 0 && is_require_results) {
|
||||||
|
error(pl->type, "`#require_results` is not needed on a procedure with no results");
|
||||||
|
} else {
|
||||||
|
pt->require_results = is_require_results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (is_foreign) {
|
||||||
|
String name = e->token.string;
|
||||||
|
if (pl->link_name.len > 0) {
|
||||||
|
name = pl->link_name;
|
||||||
|
}
|
||||||
|
e->Procedure.is_foreign = true;
|
||||||
|
e->Procedure.link_name = name;
|
||||||
|
|
||||||
|
init_entity_foreign_library(c, e);
|
||||||
|
|
||||||
|
|
||||||
|
auto *fp = &c->info.foreigns;
|
||||||
|
HashKey key = hash_string(name);
|
||||||
|
Entity **found = map_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 (is_type_proc(this_type) && is_type_proc(other_type)) {
|
||||||
|
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||||
|
error(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 if (!are_types_identical(this_type, other_type)) {
|
||||||
|
error(d->proc_lit,
|
||||||
|
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
|
||||||
|
"\tat %.*s(%td:%td)",
|
||||||
|
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map_set(fp, key, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String name = e->token.string;
|
||||||
|
if (is_link_name) {
|
||||||
|
name = pl->link_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_link_name || is_export) {
|
||||||
|
auto *fp = &c->info.foreigns;
|
||||||
|
|
||||||
|
e->Procedure.link_name = name;
|
||||||
|
|
||||||
|
HashKey key = hash_string(name);
|
||||||
|
Entity **found = map_get(fp, key);
|
||||||
|
if (found) {
|
||||||
|
Entity *f = *found;
|
||||||
|
TokenPos pos = f->token.pos;
|
||||||
|
// TODO(bill): Better error message?
|
||||||
|
error(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_set(fp, key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||||
|
GB_ASSERT(e->type == nullptr);
|
||||||
|
GB_ASSERT(e->kind == Entity_Variable);
|
||||||
|
|
||||||
|
if (e->flags & EntityFlag_Visited) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
e->flags |= EntityFlag_Visited;
|
||||||
|
|
||||||
|
String context_name = str_lit("variable declaration");
|
||||||
|
|
||||||
|
if (type_expr != nullptr) {
|
||||||
|
e->type = check_type(c, type_expr);
|
||||||
|
}
|
||||||
|
if (e->type != nullptr) {
|
||||||
|
if (is_type_polymorphic(base_type(e->type))) {
|
||||||
|
gbString str = type_to_string(e->type);
|
||||||
|
defer (gb_string_free(str));
|
||||||
|
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
} else if (is_type_empty_union(e->type)) {
|
||||||
|
gbString str = type_to_string(e->type);
|
||||||
|
defer (gb_string_free(str));
|
||||||
|
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (e->Variable.is_foreign) {
|
||||||
|
if (init_expr != nullptr) {
|
||||||
|
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||||
|
}
|
||||||
|
init_entity_foreign_library(c, e);
|
||||||
|
|
||||||
|
String name = e->token.string;
|
||||||
|
auto *fp = &c->info.foreigns;
|
||||||
|
HashKey key = hash_string(name);
|
||||||
|
Entity **found = map_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_types_identical(this_type, other_type)) {
|
||||||
|
error(e->token,
|
||||||
|
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
|
||||||
|
"\tat %.*s(%td:%td)",
|
||||||
|
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
map_set(fp, key, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_expr == nullptr) {
|
||||||
|
if (type_expr == nullptr) {
|
||||||
|
e->type = t_invalid;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entities == nullptr || entity_count == 1) {
|
||||||
|
GB_ASSERT(entities == nullptr || entities[0] == e);
|
||||||
|
Operand operand = {};
|
||||||
|
check_expr(c, &operand, init_expr);
|
||||||
|
check_init_variable(c, e, &operand, context_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_expr != nullptr) {
|
||||||
|
for (isize i = 0; i < entity_count; i++) {
|
||||||
|
entities[i]->type = e->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Array<AstNode *> inits;
|
||||||
|
array_init(&inits, c->allocator, 1);
|
||||||
|
array_add(&inits, init_expr);
|
||||||
|
check_init_variables(c, entities, entity_count, inits, context_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||||
|
if (e->type != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d == nullptr) {
|
||||||
|
d = decl_info_of_entity(&c->info, e);
|
||||||
|
if (d == nullptr) {
|
||||||
|
// 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_decl(c, e, d);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->context = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
||||||
|
if (body == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
||||||
|
|
||||||
|
String proc_name = {};
|
||||||
|
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;
|
||||||
|
defer (c->context = old_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_array(i, params->variables) {
|
||||||
|
Entity *e = params->variables[i];
|
||||||
|
if (e->kind != Entity_Variable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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 (t->kind == Type_Struct) {
|
||||||
|
Scope *scope = t->Struct.scope;
|
||||||
|
if (scope == nullptr) {
|
||||||
|
scope = scope_of_node(&c->info, t->Struct.node);
|
||||||
|
}
|
||||||
|
GB_ASSERT(scope != nullptr);
|
||||||
|
for_array(i, scope->elements.entries) {
|
||||||
|
Entity *f = scope->elements.entries[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 != nullptr) {
|
||||||
|
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");
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (decl->parent != nullptr) {
|
||||||
|
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||||
|
for_array(i, decl->deps.entries) {
|
||||||
|
HashKey key = decl->deps.entries[i].key;
|
||||||
|
Entity *e = cast(Entity *)key.ptr;
|
||||||
|
map_set(&decl->parent->deps, key, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
+8620
File diff suppressed because it is too large
Load Diff
+1833
File diff suppressed because it is too large
Load Diff
+2434
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,545 +0,0 @@
|
|||||||
bool check_is_terminating(AstNode *node);
|
|
||||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
|
||||||
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
|
|
||||||
void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
|
|
||||||
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
|
|
||||||
void check_proc_decl (Checker *c, Entity *e, DeclInfo *d);
|
|
||||||
void check_var_decl (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr);
|
|
||||||
|
|
||||||
// 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?
|
|
||||||
error(ast_node_token(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);
|
|
||||||
}
|
|
||||||
e->type = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
Array(Operand) operands;
|
|
||||||
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
|
|
||||||
|
|
||||||
for_array(i, inits) {
|
|
||||||
AstNode *rhs = inits.e[i];
|
|
||||||
Operand o = {0};
|
|
||||||
check_multi_expr(c, &o, rhs);
|
|
||||||
if (o.type->kind != Type_Tuple) {
|
|
||||||
array_add(&operands, o);
|
|
||||||
} else {
|
|
||||||
TypeTuple *tuple = &o.type->Tuple;
|
|
||||||
for (isize j = 0; j < tuple->variable_count; j++) {
|
|
||||||
o.type = tuple->variables[j]->type;
|
|
||||||
array_add(&operands, o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) {
|
|
||||||
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 {
|
|
||||||
e->type = t_invalid;
|
|
||||||
set_base_type(named_type, t_invalid);
|
|
||||||
return;
|
|
||||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e->kind == Entity_Procedure) {
|
|
||||||
check_proc_decl(c, e, d);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CheckerContext prev = c->context;
|
|
||||||
c->context.scope = d->scope;
|
|
||||||
c->context.decl = d;
|
|
||||||
|
|
||||||
switch (e->kind) {
|
|
||||||
case Entity_Constant:
|
|
||||||
check_const_decl(c, e, d->type_expr, d->init_expr);
|
|
||||||
break;
|
|
||||||
case Entity_Variable:
|
|
||||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
|
||||||
break;
|
|
||||||
case Entity_TypeName:
|
|
||||||
check_type_decl(c, e, d->type_expr, named_type, cycle_checker);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->context = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void check_var_decl_node(Checker *c, AstNode *node) {
|
|
||||||
ast_node(vd, VarDecl, node);
|
|
||||||
isize entity_count = vd->names.count;
|
|
||||||
isize entity_index = 0;
|
|
||||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
|
||||||
|
|
||||||
for_array(i, vd->names) {
|
|
||||||
AstNode *name = vd->names.e[i];
|
|
||||||
Entity *entity = NULL;
|
|
||||||
if (name->kind == AstNode_Ident) {
|
|
||||||
Token token = name->Ident;
|
|
||||||
String str = token.string;
|
|
||||||
Entity *found = NULL;
|
|
||||||
// NOTE(bill): Ignore assignments to `_`
|
|
||||||
if (str_ne(str, str_lit("_"))) {
|
|
||||||
found = current_scope_lookup_entity(c->context.scope, str);
|
|
||||||
}
|
|
||||||
if (found == NULL) {
|
|
||||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
|
||||||
add_entity_definition(&c->info, name, entity);
|
|
||||||
} else {
|
|
||||||
TokenPos pos = found->token.pos;
|
|
||||||
error(token,
|
|
||||||
"Redeclaration of `%.*s` in this scope\n"
|
|
||||||
"\tat %.*s(%td:%td)",
|
|
||||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
|
||||||
entity = found;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error(ast_node_token(name), "A variable declaration must be an identifier");
|
|
||||||
}
|
|
||||||
if (entity == NULL) {
|
|
||||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
|
||||||
}
|
|
||||||
entities[entity_index++] = entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type *init_type = NULL;
|
|
||||||
if (vd->type) {
|
|
||||||
init_type = check_type_extra(c, vd->type, NULL, NULL);
|
|
||||||
if (init_type == NULL)
|
|
||||||
init_type = t_invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (isize i = 0; i < entity_count; i++) {
|
|
||||||
Entity *e = entities[i];
|
|
||||||
GB_ASSERT(e != NULL);
|
|
||||||
if (e->flags & EntityFlag_Visited) {
|
|
||||||
e->type = t_invalid;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
e->flags |= EntityFlag_Visited;
|
|
||||||
|
|
||||||
if (e->type == NULL)
|
|
||||||
e->type = init_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
|
||||||
|
|
||||||
for_array(i, vd->names) {
|
|
||||||
if (entities[i] != NULL) {
|
|
||||||
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
error(ast_node_token(operand->expr),
|
|
||||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
|
|
||||||
if (e->type == NULL) {
|
|
||||||
e->type = t_invalid;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// if (!is_type_constant_type(operand->type)) {
|
|
||||||
// gbString type_str = type_to_string(operand->type);
|
|
||||||
// defer (gb_string_free(type_str));
|
|
||||||
// error(ast_node_token(operand->expr),
|
|
||||||
// "Invalid constant type: `%s`", 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->Constant.value = operand->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) {
|
|
||||||
GB_ASSERT(e->type == NULL);
|
|
||||||
|
|
||||||
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);
|
|
||||||
// defer (gb_string_free(str));
|
|
||||||
// error(ast_node_token(type_expr),
|
|
||||||
// "Invalid constant type `%s`", str);
|
|
||||||
// e->type = t_invalid;
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
e->type = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand operand = {0};
|
|
||||||
if (init_expr) {
|
|
||||||
check_expr(c, &operand, init_expr);
|
|
||||||
}
|
|
||||||
check_init_constant(c, e, &operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) {
|
|
||||||
GB_ASSERT(e->type == NULL);
|
|
||||||
Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
|
|
||||||
named->Named.type_name = e;
|
|
||||||
if (def != NULL && def->kind == Type_Named) {
|
|
||||||
def->Named.base = named;
|
|
||||||
}
|
|
||||||
e->type = named;
|
|
||||||
|
|
||||||
CycleChecker local_cycle_checker = {0};
|
|
||||||
if (cycle_checker == NULL) {
|
|
||||||
cycle_checker = &local_cycle_checker;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e));
|
|
||||||
named->Named.base = bt;
|
|
||||||
named->Named.base = base_type(named->Named.base);
|
|
||||||
if (named->Named.base == t_invalid) {
|
|
||||||
gb_printf("check_type_decl: %s\n", type_to_string(named));
|
|
||||||
}
|
|
||||||
|
|
||||||
cycle_checker_destroy(&local_cycle_checker);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 = base_type(a->params->Tuple.variables[i]->type);
|
|
||||||
Type *y = base_type(b->params->Tuple.variables[i]->type);
|
|
||||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
|
||||||
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 (!are_types_identical(x, y)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
|
||||||
GB_ASSERT(e->type == NULL);
|
|
||||||
|
|
||||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
|
|
||||||
e->type = proc_type;
|
|
||||||
ast_node(pd, ProcDecl, d->proc_decl);
|
|
||||||
|
|
||||||
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_inline = (pd->tags & ProcTag_inline) != 0;
|
|
||||||
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
|
|
||||||
|
|
||||||
if ((d->scope->is_file || d->scope->is_global) &&
|
|
||||||
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) {
|
|
||||||
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(ast_node_token(pd->type),
|
|
||||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_foreign && is_link_name) {
|
|
||||||
error(ast_node_token(pd->type),
|
|
||||||
"You cannot apply both `foreign` and `link_name` to a procedure");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pd->body != NULL) {
|
|
||||||
if (is_foreign) {
|
|
||||||
error(ast_node_token(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 (is_foreign) {
|
|
||||||
MapEntity *fp = &c->info.foreign_procs;
|
|
||||||
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
|
|
||||||
String name = proc_decl->name->Ident.string;
|
|
||||||
if (proc_decl->foreign_name.len > 0) {
|
|
||||||
name = proc_decl->foreign_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(ast_node_token(d->proc_decl),
|
|
||||||
"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 if (is_link_name) {
|
|
||||||
MapEntity *fp = &c->info.foreign_procs;
|
|
||||||
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
|
|
||||||
String name = proc_decl->link_name;
|
|
||||||
|
|
||||||
HashKey key = hash_string(name);
|
|
||||||
Entity **found = map_entity_get(fp, key);
|
|
||||||
if (found) {
|
|
||||||
Entity *f = *found;
|
|
||||||
TokenPos pos = f->token.pos;
|
|
||||||
error(ast_node_token(d->proc_decl),
|
|
||||||
"Non unique #link_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, 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_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
|
||||||
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
|
||||||
|
|
||||||
CheckerContext old_context = c->context;
|
|
||||||
c->context.scope = decl->scope;
|
|
||||||
c->context.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_Anonymous)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
// TODO(bill): Check declarations first (except mutable variable declarations)
|
|
||||||
check_stmt_list(c, bs->stmts, 0);
|
|
||||||
if (type->Proc.result_count > 0) {
|
|
||||||
if (!check_is_terminating(body)) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
typedef struct Scope Scope;
|
|
||||||
typedef struct Checker Checker;
|
|
||||||
typedef struct Type Type;
|
|
||||||
typedef enum BuiltinProcId BuiltinProcId;
|
|
||||||
typedef enum ImplicitValueId ImplicitValueId;
|
|
||||||
|
|
||||||
#define ENTITY_KINDS \
|
|
||||||
ENTITY_KIND(Invalid) \
|
|
||||||
ENTITY_KIND(Constant) \
|
|
||||||
ENTITY_KIND(Variable) \
|
|
||||||
ENTITY_KIND(TypeName) \
|
|
||||||
ENTITY_KIND(Procedure) \
|
|
||||||
ENTITY_KIND(Builtin) \
|
|
||||||
ENTITY_KIND(ImportName) \
|
|
||||||
ENTITY_KIND(Nil) \
|
|
||||||
ENTITY_KIND(ImplicitValue) \
|
|
||||||
ENTITY_KIND(Count)
|
|
||||||
|
|
||||||
typedef enum EntityKind {
|
|
||||||
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
|
|
||||||
ENTITY_KINDS
|
|
||||||
#undef ENTITY_KIND
|
|
||||||
} EntityKind;
|
|
||||||
|
|
||||||
String const entity_strings[] = {
|
|
||||||
#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
|
||||||
ENTITY_KINDS
|
|
||||||
#undef ENTITY_KIND
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef enum EntityFlag {
|
|
||||||
EntityFlag_Visited = 1<<0,
|
|
||||||
EntityFlag_Used = 1<<1,
|
|
||||||
EntityFlag_Anonymous = 1<<2,
|
|
||||||
EntityFlag_Field = 1<<3,
|
|
||||||
EntityFlag_Param = 1<<4,
|
|
||||||
EntityFlag_VectorElem = 1<<5,
|
|
||||||
} EntityFlag;
|
|
||||||
|
|
||||||
typedef struct Entity Entity;
|
|
||||||
struct Entity {
|
|
||||||
EntityKind kind;
|
|
||||||
u32 flags;
|
|
||||||
Token token;
|
|
||||||
Scope * scope;
|
|
||||||
Type * type;
|
|
||||||
AstNode * identifier; // Can be NULL
|
|
||||||
|
|
||||||
// TODO(bill): Cleanup how `using` works for entities
|
|
||||||
Entity * using_parent;
|
|
||||||
AstNode * using_expr;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
ExactValue value;
|
|
||||||
} Constant;
|
|
||||||
struct {
|
|
||||||
i32 field_index;
|
|
||||||
i32 field_src_index;
|
|
||||||
} Variable;
|
|
||||||
i32 TypeName;
|
|
||||||
i32 Procedure;
|
|
||||||
struct {
|
|
||||||
BuiltinProcId id;
|
|
||||||
} Builtin;
|
|
||||||
struct {
|
|
||||||
String path;
|
|
||||||
String name;
|
|
||||||
Scope *scope;
|
|
||||||
bool used;
|
|
||||||
} ImportName;
|
|
||||||
i32 Nil;
|
|
||||||
struct {
|
|
||||||
// TODO(bill): Should this be a user-level construct rather than compiler-level?
|
|
||||||
ImplicitValueId id;
|
|
||||||
Entity * backing;
|
|
||||||
} ImplicitValue;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
|
|
||||||
Entity *entity = gb_alloc_item(a, Entity);
|
|
||||||
entity->kind = kind;
|
|
||||||
entity->scope = scope;
|
|
||||||
entity->token = token;
|
|
||||||
entity->type = type;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
|
|
||||||
GB_ASSERT(parent != NULL);
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
|
|
||||||
entity->using_parent = parent;
|
|
||||||
entity->flags |= EntityFlag_Anonymous;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type);
|
|
||||||
entity->Constant.value = value;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
|
|
||||||
Entity *entity = make_entity_variable(a, scope, token, type);
|
|
||||||
entity->flags |= EntityFlag_Used;
|
|
||||||
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
|
|
||||||
entity->flags |= EntityFlag_Param;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
|
|
||||||
Entity *entity = make_entity_variable(a, scope, token, type);
|
|
||||||
entity->Variable.field_src_index = field_src_index;
|
|
||||||
entity->Variable.field_index = field_src_index;
|
|
||||||
entity->flags |= EntityFlag_Field;
|
|
||||||
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
|
|
||||||
Entity *entity = make_entity_variable(a, scope, token, type);
|
|
||||||
entity->Variable.field_src_index = field_src_index;
|
|
||||||
entity->Variable.field_index = field_src_index;
|
|
||||||
entity->flags |= EntityFlag_Field;
|
|
||||||
entity->flags |= EntityFlag_VectorElem;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
|
|
||||||
entity->Builtin.id = id;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
|
|
||||||
String path, String name, Scope *import_scope) {
|
|
||||||
Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
|
|
||||||
entity->ImportName.path = path;
|
|
||||||
entity->ImportName.name = name;
|
|
||||||
entity->ImportName.scope = import_scope;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
|
|
||||||
Token token = make_token_ident(name);
|
|
||||||
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) {
|
|
||||||
Token token = make_token_ident(name);
|
|
||||||
Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
|
|
||||||
entity->ImplicitValue.id = id;
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) {
|
|
||||||
token.string = str_lit("_");
|
|
||||||
return make_entity_variable(a, file_scope, token, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
-4559
File diff suppressed because it is too large
Load Diff
-1130
File diff suppressed because it is too large
Load Diff
-1488
File diff suppressed because it is too large
Load Diff
-201
@@ -1,201 +0,0 @@
|
|||||||
#define GB_NO_DEFER
|
|
||||||
#define GB_IMPLEMENTATION
|
|
||||||
#include "gb/gb.h"
|
|
||||||
|
|
||||||
gbAllocator heap_allocator(void) {
|
|
||||||
return gb_heap_allocator();
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "string.c"
|
|
||||||
#include "array.c"
|
|
||||||
|
|
||||||
gb_global String global_module_path = {0};
|
|
||||||
gb_global bool global_module_path_set = false;
|
|
||||||
|
|
||||||
|
|
||||||
String get_module_dir() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#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
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|
||||||
#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"
|
|
||||||
+463
@@ -0,0 +1,463 @@
|
|||||||
|
#if defined(GB_SYSTEM_UNIX)
|
||||||
|
// Required for intrinsics on GCC
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define GB_IMPLEMENTATION
|
||||||
|
#include "gb/gb.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
GB_ALLOCATOR_PROC(heap_allocator_proc);
|
||||||
|
|
||||||
|
gbAllocator heap_allocator(void) {
|
||||||
|
gbAllocator a;
|
||||||
|
a.proc = heap_allocator_proc;
|
||||||
|
a.data = NULL;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GB_ALLOCATOR_PROC(heap_allocator_proc) {
|
||||||
|
void *ptr = NULL;
|
||||||
|
gb_unused(allocator_data);
|
||||||
|
gb_unused(old_size);
|
||||||
|
// TODO(bill): Throughly test!
|
||||||
|
switch (type) {
|
||||||
|
#if defined(GB_COMPILER_MSVC)
|
||||||
|
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;
|
||||||
|
|
||||||
|
#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 "integer128.cpp"
|
||||||
|
#include "murmurhash3.cpp"
|
||||||
|
|
||||||
|
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||||
|
|
||||||
|
|
||||||
|
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.lo ^= bytes[i];
|
||||||
|
h = h * o;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "map.cpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Pool {
|
||||||
|
isize memblock_size;
|
||||||
|
isize out_of_band_size;
|
||||||
|
isize alignment;
|
||||||
|
|
||||||
|
Array<u8 *> unused_memblock;
|
||||||
|
Array<u8 *> used_memblock;
|
||||||
|
Array<u8 *> out_of_band_allocations;
|
||||||
|
|
||||||
|
u8 * current_memblock;
|
||||||
|
u8 * current_pos;
|
||||||
|
isize bytes_left;
|
||||||
|
|
||||||
|
gbAllocator block_allocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
POOL_BUCKET_SIZE_DEFAULT = 65536,
|
||||||
|
POOL_OUT_OF_BAND_SIZE_DEFAULT = 6554,
|
||||||
|
};
|
||||||
|
|
||||||
|
void pool_init(Pool *pool,
|
||||||
|
isize memblock_size = POOL_BUCKET_SIZE_DEFAULT,
|
||||||
|
isize out_of_band_size = POOL_OUT_OF_BAND_SIZE_DEFAULT,
|
||||||
|
isize alignment = 8,
|
||||||
|
gbAllocator block_allocator = heap_allocator(),
|
||||||
|
gbAllocator array_allocator = heap_allocator()) {
|
||||||
|
pool->memblock_size = memblock_size;
|
||||||
|
pool->out_of_band_size = out_of_band_size;
|
||||||
|
pool->alignment = alignment;
|
||||||
|
pool->block_allocator = block_allocator;
|
||||||
|
|
||||||
|
array_init(&pool->unused_memblock, array_allocator);
|
||||||
|
array_init(&pool->used_memblock, array_allocator);
|
||||||
|
array_init(&pool->out_of_band_allocations, array_allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pool_free_all(Pool *p) {
|
||||||
|
if (p->current_memblock != nullptr) {
|
||||||
|
array_add(&p->unused_memblock, p->current_memblock);
|
||||||
|
p->current_memblock = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_array(i, p->used_memblock) {
|
||||||
|
array_add(&p->unused_memblock, p->used_memblock[i]);
|
||||||
|
}
|
||||||
|
array_clear(&p->unused_memblock);
|
||||||
|
|
||||||
|
for_array(i, p->out_of_band_allocations) {
|
||||||
|
gb_free(p->block_allocator, p->out_of_band_allocations[i]);
|
||||||
|
}
|
||||||
|
array_clear(&p->out_of_band_allocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pool_destroy(Pool *p) {
|
||||||
|
pool_free_all(p);
|
||||||
|
|
||||||
|
for_array(i, p->unused_memblock) {
|
||||||
|
gb_free(p->block_allocator, p->unused_memblock[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pool_cycle_new_block(Pool *p) {
|
||||||
|
GB_ASSERT_MSG(p->block_allocator.proc != nullptr,
|
||||||
|
"You must call pool_init on a Pool before using it!");
|
||||||
|
|
||||||
|
if (p->current_memblock != nullptr) {
|
||||||
|
array_add(&p->used_memblock, p->current_memblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *new_block = nullptr;
|
||||||
|
|
||||||
|
if (p->unused_memblock.count > 0) {
|
||||||
|
new_block = array_pop(&p->unused_memblock);
|
||||||
|
} else {
|
||||||
|
GB_ASSERT(p->block_allocator.proc != nullptr);
|
||||||
|
new_block = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, p->alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->bytes_left = p->memblock_size;
|
||||||
|
p->current_memblock = new_block;
|
||||||
|
p->current_memblock = new_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *pool_get(Pool *p,
|
||||||
|
isize size, isize alignment = 0) {
|
||||||
|
if (alignment <= 0) alignment = p->alignment;
|
||||||
|
|
||||||
|
isize extra = alignment - (size & alignment);
|
||||||
|
size += extra;
|
||||||
|
if (size >= p->out_of_band_size) {
|
||||||
|
GB_ASSERT(p->block_allocator.proc != nullptr);
|
||||||
|
u8 *memory = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, alignment);
|
||||||
|
if (memory != nullptr) {
|
||||||
|
array_add(&p->out_of_band_allocations, memory);
|
||||||
|
}
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->bytes_left < size) {
|
||||||
|
pool_cycle_new_block(p);
|
||||||
|
if (p->current_memblock != nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *res = p->current_pos;
|
||||||
|
p->current_pos += size;
|
||||||
|
p->bytes_left -= size;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gbAllocator pool_allocator(Pool *pool);
|
||||||
|
|
||||||
|
GB_ALLOCATOR_PROC(pool_allocator_procedure) {
|
||||||
|
Pool *p = cast(Pool *)allocator_data;
|
||||||
|
void *ptr = nullptr;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case gbAllocation_Alloc:
|
||||||
|
return pool_get(p, size, alignment);
|
||||||
|
case gbAllocation_Free:
|
||||||
|
// Does nothing
|
||||||
|
break;
|
||||||
|
case gbAllocation_FreeAll:
|
||||||
|
pool_free_all(p);
|
||||||
|
break;
|
||||||
|
case gbAllocation_Resize:
|
||||||
|
return gb_default_resize_align(pool_allocator(p), old_memory, old_size, size, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbAllocator pool_allocator(Pool *pool) {
|
||||||
|
gbAllocator allocator;
|
||||||
|
allocator.proc = pool_allocator_procedure;
|
||||||
|
allocator.data = pool;
|
||||||
|
return allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 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
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
// Generates Documentation
|
||||||
|
|
||||||
|
gbString expr_to_string(AstNode *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(AstNode *spec) {
|
||||||
|
ast_node(ts, TypeSpec, spec);
|
||||||
|
GB_ASSERT(ts->name->kind == AstNode_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(AstNodeProcDecl *pd) {
|
||||||
|
GB_ASSERT(pd->name->kind == AstNode_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(AstNode *decl) {
|
||||||
|
switch (decl->kind) {
|
||||||
|
case_ast_node(gd, GenDecl, decl);
|
||||||
|
for_array(spec_index, gd->specs) {
|
||||||
|
AstNode *spec = gd->specs[spec_index];
|
||||||
|
switch(gd->token.kind) {
|
||||||
|
case Token_import:
|
||||||
|
case Token_import_load:
|
||||||
|
break;
|
||||||
|
case Token_foreign_library:
|
||||||
|
case Token_foreign_system_library:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case_end;
|
||||||
|
|
||||||
|
// case_ast_node(pd, ProcDecl, decl);
|
||||||
|
// print_proc_decl(pd);
|
||||||
|
// case_end;
|
||||||
|
|
||||||
|
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||||
|
// TODO(bill)
|
||||||
|
case_end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
AstNode *decl = file->decls[decl_index];
|
||||||
|
print_declaration(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+287
@@ -0,0 +1,287 @@
|
|||||||
|
struct Scope;
|
||||||
|
struct Checker;
|
||||||
|
struct Type;
|
||||||
|
struct DeclInfo;
|
||||||
|
// typedef enum BuiltinProcId BuiltinProcId;
|
||||||
|
|
||||||
|
|
||||||
|
#define ENTITY_KINDS \
|
||||||
|
ENTITY_KIND(Invalid) \
|
||||||
|
ENTITY_KIND(Constant) \
|
||||||
|
ENTITY_KIND(Variable) \
|
||||||
|
ENTITY_KIND(TypeName) \
|
||||||
|
ENTITY_KIND(Procedure) \
|
||||||
|
ENTITY_KIND(Builtin) \
|
||||||
|
ENTITY_KIND(Alias) \
|
||||||
|
ENTITY_KIND(ImportName) \
|
||||||
|
ENTITY_KIND(LibraryName) \
|
||||||
|
ENTITY_KIND(Nil) \
|
||||||
|
ENTITY_KIND(Label)
|
||||||
|
|
||||||
|
enum EntityKind {
|
||||||
|
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
|
||||||
|
ENTITY_KINDS
|
||||||
|
#undef ENTITY_KIND
|
||||||
|
Entity_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
String const entity_strings[] = {
|
||||||
|
#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
||||||
|
ENTITY_KINDS
|
||||||
|
#undef ENTITY_KIND
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EntityFlag {
|
||||||
|
EntityFlag_Visited = 1<<0,
|
||||||
|
EntityFlag_Used = 1<<1,
|
||||||
|
EntityFlag_Using = 1<<2,
|
||||||
|
EntityFlag_Field = 1<<3,
|
||||||
|
EntityFlag_Param = 1<<4,
|
||||||
|
EntityFlag_VectorElem = 1<<5,
|
||||||
|
EntityFlag_Ellipsis = 1<<6,
|
||||||
|
EntityFlag_NoAlias = 1<<7,
|
||||||
|
EntityFlag_TypeField = 1<<8,
|
||||||
|
EntityFlag_Value = 1<<9,
|
||||||
|
EntityFlag_Sret = 1<<10,
|
||||||
|
EntityFlag_BitFieldValue = 1<<11,
|
||||||
|
|
||||||
|
EntityFlag_CVarArg = 1<<20,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// Zero value means the overloading process is not yet done
|
||||||
|
enum OverloadKind {
|
||||||
|
Overload_Unknown,
|
||||||
|
Overload_No,
|
||||||
|
Overload_Yes,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EntityAliasKind {
|
||||||
|
EntityAlias_Invalid,
|
||||||
|
EntityAlias_Type,
|
||||||
|
EntityAlias_Entity,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// An Entity is a named "thing" in the language
|
||||||
|
struct Entity {
|
||||||
|
EntityKind kind;
|
||||||
|
u64 id;
|
||||||
|
u32 flags;
|
||||||
|
Token token;
|
||||||
|
Scope * scope;
|
||||||
|
Type * type;
|
||||||
|
AstNode * identifier; // Can be nullptr
|
||||||
|
DeclInfo * parent_proc_decl; // nullptr if in file/global scope
|
||||||
|
|
||||||
|
// TODO(bill): Cleanup how `using` works for entities
|
||||||
|
Entity * using_parent;
|
||||||
|
AstNode * using_expr;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
ExactValue value;
|
||||||
|
} Constant;
|
||||||
|
struct {
|
||||||
|
i32 field_index;
|
||||||
|
i32 field_src_index;
|
||||||
|
ExactValue default_value;
|
||||||
|
bool default_is_nil;
|
||||||
|
bool default_is_undef;
|
||||||
|
bool default_is_location;
|
||||||
|
bool is_immutable;
|
||||||
|
bool is_thread_local;
|
||||||
|
bool is_foreign;
|
||||||
|
Entity * foreign_library;
|
||||||
|
AstNode * foreign_library_ident;
|
||||||
|
String link_name;
|
||||||
|
} Variable;
|
||||||
|
struct {
|
||||||
|
bool is_type_alias;
|
||||||
|
Type *type_parameter_specialization;
|
||||||
|
} TypeName;
|
||||||
|
struct {
|
||||||
|
OverloadKind overload_kind;
|
||||||
|
String link_name;
|
||||||
|
u64 tags;
|
||||||
|
bool is_foreign;
|
||||||
|
Entity * foreign_library;
|
||||||
|
AstNode * foreign_library_ident;
|
||||||
|
} Procedure;
|
||||||
|
struct {
|
||||||
|
i32 id;
|
||||||
|
} Builtin;
|
||||||
|
struct {
|
||||||
|
Entity *base;
|
||||||
|
} Alias;
|
||||||
|
struct {
|
||||||
|
String path;
|
||||||
|
String name;
|
||||||
|
Scope *scope;
|
||||||
|
bool used;
|
||||||
|
} ImportName;
|
||||||
|
struct {
|
||||||
|
String path;
|
||||||
|
String name;
|
||||||
|
bool used;
|
||||||
|
} LibraryName;
|
||||||
|
i32 Nil;
|
||||||
|
struct {
|
||||||
|
String name;
|
||||||
|
AstNode *node;
|
||||||
|
} Label;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
gb_global Entity *e_context = nullptr;
|
||||||
|
|
||||||
|
bool is_entity_kind_exported(EntityKind kind) {
|
||||||
|
switch (kind) {
|
||||||
|
case Entity_Builtin:
|
||||||
|
case Entity_ImportName:
|
||||||
|
case Entity_LibraryName:
|
||||||
|
case Entity_Nil:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_entity_exported(Entity *e) {
|
||||||
|
// TODO(bill): Determine the actual exportation rules for imports of entities
|
||||||
|
GB_ASSERT(e != nullptr);
|
||||||
|
if (!is_entity_kind_exported(e->kind)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = e->token.string;
|
||||||
|
if (name.len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return name[0] != '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_global u64 global_entity_id = 0;
|
||||||
|
|
||||||
|
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
|
||||||
|
Entity *entity = gb_alloc_item(a, Entity);
|
||||||
|
entity->kind = kind;
|
||||||
|
entity->scope = scope;
|
||||||
|
entity->token = token;
|
||||||
|
entity->type = type;
|
||||||
|
entity->id = ++global_entity_id;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type, bool is_immutable) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
|
||||||
|
entity->Variable.is_immutable = is_immutable;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
|
||||||
|
GB_ASSERT(parent != nullptr);
|
||||||
|
token.pos = parent->token.pos;
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
|
||||||
|
entity->using_parent = parent;
|
||||||
|
entity->parent_proc_decl = parent->parent_proc_decl;
|
||||||
|
entity->flags |= EntityFlag_Using;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type);
|
||||||
|
entity->Constant.value = value;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
|
||||||
|
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
|
||||||
|
entity->flags |= EntityFlag_Used;
|
||||||
|
if (is_using) entity->flags |= EntityFlag_Using;
|
||||||
|
entity->flags |= EntityFlag_Param;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
|
||||||
|
Entity *entity = make_entity_variable(a, scope, token, type, false);
|
||||||
|
entity->Variable.field_src_index = field_src_index;
|
||||||
|
entity->Variable.field_index = field_src_index;
|
||||||
|
if (is_using) entity->flags |= EntityFlag_Using;
|
||||||
|
entity->flags |= EntityFlag_Field;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
|
||||||
|
Entity *entity = make_entity_variable(a, scope, token, type, false);
|
||||||
|
entity->Variable.field_src_index = field_src_index;
|
||||||
|
entity->Variable.field_index = field_src_index;
|
||||||
|
entity->flags |= EntityFlag_Field;
|
||||||
|
entity->flags |= EntityFlag_VectorElem;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type, u64 tags) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
|
||||||
|
entity->Procedure.tags = tags;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, i32 id) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
|
||||||
|
entity->Builtin.id = id;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type, Entity *base) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
|
||||||
|
entity->Alias.base = base;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||||
|
String path, String name, Scope *import_scope) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
|
||||||
|
entity->ImportName.path = path;
|
||||||
|
entity->ImportName.name = name;
|
||||||
|
entity->ImportName.scope = import_scope;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||||
|
String path, String name) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_LibraryName, scope, token, type);
|
||||||
|
entity->LibraryName.path = path;
|
||||||
|
entity->LibraryName.name = name;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
|
||||||
|
Token token = make_token_ident(name);
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Nil, nullptr, token, type);
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||||
|
AstNode *node) {
|
||||||
|
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
|
||||||
|
entity->Label.node = node;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||||
|
token.string = str_lit("_");
|
||||||
|
return make_entity_variable(a, scope, token, nullptr, false);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,400 +0,0 @@
|
|||||||
#include <math.h>
|
|
||||||
|
|
||||||
// TODO(bill): Big numbers
|
|
||||||
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
|
|
||||||
|
|
||||||
typedef struct AstNode AstNode;
|
|
||||||
|
|
||||||
typedef enum ExactValueKind {
|
|
||||||
ExactValue_Invalid,
|
|
||||||
|
|
||||||
ExactValue_Bool,
|
|
||||||
ExactValue_String,
|
|
||||||
ExactValue_Integer,
|
|
||||||
ExactValue_Float,
|
|
||||||
ExactValue_Pointer,
|
|
||||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
|
||||||
|
|
||||||
ExactValue_Count,
|
|
||||||
} ExactValueKind;
|
|
||||||
|
|
||||||
typedef struct ExactValue {
|
|
||||||
ExactValueKind kind;
|
|
||||||
union {
|
|
||||||
bool value_bool;
|
|
||||||
String value_string;
|
|
||||||
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
|
||||||
f64 value_float;
|
|
||||||
i64 value_pointer;
|
|
||||||
AstNode *value_compound;
|
|
||||||
};
|
|
||||||
} ExactValue;
|
|
||||||
|
|
||||||
HashKey hash_exact_value(ExactValue v) {
|
|
||||||
return hashing_proc(&v, gb_size_of(ExactValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ExactValue make_exact_value_compound(AstNode *node) {
|
|
||||||
ExactValue result = {ExactValue_Compound};
|
|
||||||
result.value_compound = node;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_bool(bool b) {
|
|
||||||
ExactValue result = {ExactValue_Bool};
|
|
||||||
result.value_bool = (b != 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_string(String string) {
|
|
||||||
// TODO(bill): Allow for numbers with underscores in them
|
|
||||||
ExactValue result = {ExactValue_String};
|
|
||||||
result.value_string = string;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_integer_from_string(String string) {
|
|
||||||
// TODO(bill): Allow for numbers with underscores in them
|
|
||||||
ExactValue result = {ExactValue_Integer};
|
|
||||||
i32 base = 10;
|
|
||||||
if (string.text[0] == '0') {
|
|
||||||
switch (string.text[1]) {
|
|
||||||
case 'b': base = 2; break;
|
|
||||||
case 'o': base = 8; break;
|
|
||||||
case 'd': base = 10; break;
|
|
||||||
case 'x': base = 16; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_integer(i64 i) {
|
|
||||||
ExactValue result = {ExactValue_Integer};
|
|
||||||
result.value_integer = i;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_float_from_string(String string) {
|
|
||||||
// TODO(bill): Allow for numbers with underscores in them
|
|
||||||
ExactValue result = {ExactValue_Float};
|
|
||||||
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_float(f64 f) {
|
|
||||||
ExactValue result = {ExactValue_Float};
|
|
||||||
result.value_float = f;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_pointer(i64 ptr) {
|
|
||||||
ExactValue result = {ExactValue_Pointer};
|
|
||||||
result.value_pointer = ptr;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue make_exact_value_from_basic_literal(Token token) {
|
|
||||||
switch (token.kind) {
|
|
||||||
case Token_String: return make_exact_value_string(token.string);
|
|
||||||
case Token_Integer: return make_exact_value_integer_from_string(token.string);
|
|
||||||
case Token_Float: return make_exact_value_float_from_string(token.string);
|
|
||||||
case Token_Rune: {
|
|
||||||
Rune r = GB_RUNE_INVALID;
|
|
||||||
gb_utf8_decode(token.string.text, token.string.len, &r);
|
|
||||||
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
|
|
||||||
return make_exact_value_integer(r);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
GB_PANIC("Invalid token for basic literal");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue result = {ExactValue_Invalid};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue exact_value_to_integer(ExactValue v) {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Integer:
|
|
||||||
return v;
|
|
||||||
case ExactValue_Float: {
|
|
||||||
i64 i = cast(i64)v.value_float;
|
|
||||||
f64 f = cast(f64)i;
|
|
||||||
if (f == v.value_float) {
|
|
||||||
return make_exact_value_integer(i);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ExactValue_Pointer:
|
|
||||||
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
|
|
||||||
}
|
|
||||||
ExactValue r = {ExactValue_Invalid};
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExactValue exact_value_to_float(ExactValue v) {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Integer:
|
|
||||||
return make_exact_value_float(cast(i64)v.value_integer);
|
|
||||||
case ExactValue_Float:
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
ExactValue r = {ExactValue_Invalid};
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
|
||||||
switch (op) {
|
|
||||||
case Token_Add: {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
case ExactValue_Integer:
|
|
||||||
case ExactValue_Float:
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Token_Sub: {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
return v;
|
|
||||||
case ExactValue_Integer: {
|
|
||||||
ExactValue i = v;
|
|
||||||
i.value_integer = -i.value_integer;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
case ExactValue_Float: {
|
|
||||||
ExactValue i = v;
|
|
||||||
i.value_float = -i.value_float;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Token_Xor: {
|
|
||||||
i64 i = 0;
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
return v;
|
|
||||||
case ExactValue_Integer:
|
|
||||||
i = v.value_integer;
|
|
||||||
i = ~i;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(bill): unsigned integers will be negative and will need to be
|
|
||||||
// limited to the types precision
|
|
||||||
if (precision > 0)
|
|
||||||
i &= ~((~0ll)<<precision);
|
|
||||||
|
|
||||||
return make_exact_value_integer(i);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case Token_Not: {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Invalid: return v;
|
|
||||||
case ExactValue_Bool:
|
|
||||||
return make_exact_value_bool(!v.value_bool);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
failure:
|
|
||||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
|
||||||
|
|
||||||
ExactValue error_value = {0};
|
|
||||||
return error_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(bill): Make sure things are evaluated in correct order
|
|
||||||
i32 exact_value_order(ExactValue v) {
|
|
||||||
switch (v.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
return 0;
|
|
||||||
case ExactValue_Bool:
|
|
||||||
case ExactValue_String:
|
|
||||||
return 1;
|
|
||||||
case ExactValue_Integer:
|
|
||||||
return 2;
|
|
||||||
case ExactValue_Float:
|
|
||||||
return 3;
|
|
||||||
case ExactValue_Pointer:
|
|
||||||
return 4;
|
|
||||||
|
|
||||||
default:
|
|
||||||
GB_PANIC("How'd you get here? Invalid Value.kind");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void match_exact_values(ExactValue *x, ExactValue *y) {
|
|
||||||
if (exact_value_order(*y) < exact_value_order(*x)) {
|
|
||||||
match_exact_values(y, x);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (x->kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
*y = *x;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ExactValue_Bool:
|
|
||||||
case ExactValue_String:
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ExactValue_Integer:
|
|
||||||
switch (y->kind) {
|
|
||||||
case ExactValue_Integer:
|
|
||||||
return;
|
|
||||||
case ExactValue_Float:
|
|
||||||
// TODO(bill): Is this good enough?
|
|
||||||
*x = make_exact_value_float(cast(f64)x->value_integer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExactValue_Float:
|
|
||||||
if (y->kind == ExactValue_Float)
|
|
||||||
return;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
compiler_error("How'd you get here? Invalid ExactValueKind");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
|
||||||
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
|
|
||||||
match_exact_values(&x, &y);
|
|
||||||
|
|
||||||
switch (x.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
return x;
|
|
||||||
|
|
||||||
case ExactValue_Bool:
|
|
||||||
switch (op) {
|
|
||||||
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
|
|
||||||
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
|
|
||||||
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool);
|
|
||||||
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool);
|
|
||||||
default: goto error;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExactValue_Integer: {
|
|
||||||
i64 a = x.value_integer;
|
|
||||||
i64 b = y.value_integer;
|
|
||||||
i64 c = 0;
|
|
||||||
switch (op) {
|
|
||||||
case Token_Add: c = a + b; break;
|
|
||||||
case Token_Sub: c = a - b; break;
|
|
||||||
case Token_Mul: c = a * b; break;
|
|
||||||
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
|
|
||||||
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
|
||||||
case Token_Mod: c = a % b; break;
|
|
||||||
case Token_And: c = a & b; break;
|
|
||||||
case Token_Or: c = a | b; break;
|
|
||||||
case Token_Xor: c = a ^ b; break;
|
|
||||||
case Token_AndNot: c = a&(~b); break;
|
|
||||||
case Token_Shl: c = a << b; break;
|
|
||||||
case Token_Shr: c = a >> b; break;
|
|
||||||
default: goto error;
|
|
||||||
}
|
|
||||||
return make_exact_value_integer(c);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ExactValue_Float: {
|
|
||||||
f64 a = x.value_float;
|
|
||||||
f64 b = y.value_float;
|
|
||||||
switch (op) {
|
|
||||||
case Token_Add: return make_exact_value_float(a + b);
|
|
||||||
case Token_Sub: return make_exact_value_float(a - b);
|
|
||||||
case Token_Mul: return make_exact_value_float(a * b);
|
|
||||||
case Token_Quo: return make_exact_value_float(a / b);
|
|
||||||
default: goto error;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
ExactValue error_value = {0};
|
|
||||||
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
|
|
||||||
return error_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
|
|
||||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
|
|
||||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
|
|
||||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
|
|
||||||
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
|
||||||
|
|
||||||
|
|
||||||
i32 cmp_f64(f64 a, f64 b) {
|
|
||||||
return (a > b) - (a < b);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
|
||||||
match_exact_values(&x, &y);
|
|
||||||
|
|
||||||
switch (x.kind) {
|
|
||||||
case ExactValue_Invalid:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case ExactValue_Bool:
|
|
||||||
switch (op) {
|
|
||||||
case Token_CmpEq: return x.value_bool == y.value_bool;
|
|
||||||
case Token_NotEq: return x.value_bool != y.value_bool;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ExactValue_Integer: {
|
|
||||||
i64 a = x.value_integer;
|
|
||||||
i64 b = y.value_integer;
|
|
||||||
switch (op) {
|
|
||||||
case Token_CmpEq: return a == b;
|
|
||||||
case Token_NotEq: return a != b;
|
|
||||||
case Token_Lt: return a < b;
|
|
||||||
case Token_LtEq: return a <= b;
|
|
||||||
case Token_Gt: return a > b;
|
|
||||||
case Token_GtEq: return a >= b;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ExactValue_Float: {
|
|
||||||
f64 a = x.value_float;
|
|
||||||
f64 b = y.value_float;
|
|
||||||
switch (op) {
|
|
||||||
case Token_CmpEq: return cmp_f64(a, b) == 0;
|
|
||||||
case Token_NotEq: return cmp_f64(a, b) != 0;
|
|
||||||
case Token_Lt: return cmp_f64(a, b) < 0;
|
|
||||||
case Token_LtEq: return cmp_f64(a, b) <= 0;
|
|
||||||
case Token_Gt: return cmp_f64(a, b) > 0;
|
|
||||||
case Token_GtEq: return cmp_f64(a, b) >= 0;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ExactValue_String: {
|
|
||||||
String a = x.value_string;
|
|
||||||
String b = y.value_string;
|
|
||||||
isize len = gb_min(a.len, b.len);
|
|
||||||
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
|
||||||
switch (op) {
|
|
||||||
case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
|
|
||||||
case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
|
|
||||||
case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0;
|
|
||||||
case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0;
|
|
||||||
case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0;
|
|
||||||
case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_PANIC("Invalid comparison");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,673 @@
|
|||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
// TODO(bill): Big numbers
|
||||||
|
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
|
||||||
|
|
||||||
|
struct AstNode;
|
||||||
|
struct HashKey;
|
||||||
|
struct Type;
|
||||||
|
bool are_types_identical(Type *x, Type *y);
|
||||||
|
|
||||||
|
struct Complex128 {
|
||||||
|
f64 real, imag;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ExactValueKind {
|
||||||
|
ExactValue_Invalid,
|
||||||
|
|
||||||
|
ExactValue_Bool,
|
||||||
|
ExactValue_String,
|
||||||
|
ExactValue_Integer,
|
||||||
|
ExactValue_Float,
|
||||||
|
ExactValue_Complex,
|
||||||
|
ExactValue_Pointer,
|
||||||
|
ExactValue_Compound, // TODO(bill): Is this good enough?
|
||||||
|
ExactValue_Type,
|
||||||
|
|
||||||
|
ExactValue_Count,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExactValue {
|
||||||
|
ExactValueKind kind;
|
||||||
|
union {
|
||||||
|
bool value_bool;
|
||||||
|
String value_string;
|
||||||
|
i128 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||||
|
f64 value_float;
|
||||||
|
i64 value_pointer;
|
||||||
|
Complex128 value_complex;
|
||||||
|
AstNode * value_compound;
|
||||||
|
Type * value_type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
gb_global ExactValue const empty_exact_value = {};
|
||||||
|
|
||||||
|
HashKey hash_exact_value(ExactValue v) {
|
||||||
|
return hashing_proc(&v, gb_size_of(ExactValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExactValue exact_value_compound(AstNode *node) {
|
||||||
|
ExactValue result = {ExactValue_Compound};
|
||||||
|
result.value_compound = node;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_bool(bool b) {
|
||||||
|
ExactValue result = {ExactValue_Bool};
|
||||||
|
result.value_bool = (b != 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_string(String string) {
|
||||||
|
// TODO(bill): Allow for numbers with underscores in them
|
||||||
|
ExactValue result = {ExactValue_String};
|
||||||
|
result.value_string = string;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_i64(i64 i) {
|
||||||
|
ExactValue result = {ExactValue_Integer};
|
||||||
|
result.value_integer = i128_from_i64(i);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_i128(i128 i) {
|
||||||
|
ExactValue result = {ExactValue_Integer};
|
||||||
|
result.value_integer = i;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
ExactValue exact_value_u128(u128 i) {
|
||||||
|
ExactValue result = {ExactValue_Integer};
|
||||||
|
result.value_integer = u128_to_i128(i);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_float(f64 f) {
|
||||||
|
ExactValue result = {ExactValue_Float};
|
||||||
|
result.value_float = f;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_complex(f64 real, f64 imag) {
|
||||||
|
ExactValue result = {ExactValue_Complex};
|
||||||
|
result.value_complex.real = real;
|
||||||
|
result.value_complex.imag = imag;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_pointer(i64 ptr) {
|
||||||
|
ExactValue result = {ExactValue_Pointer};
|
||||||
|
result.value_pointer = ptr;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_type(Type *type) {
|
||||||
|
ExactValue result = {ExactValue_Type};
|
||||||
|
result.value_type = type;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExactValue exact_value_integer_from_string(String string) {
|
||||||
|
return exact_value_u128(u128_from_string(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
f64 float_from_string(String string) {
|
||||||
|
isize i = 0;
|
||||||
|
u8 *str = string.text;
|
||||||
|
isize len = string.len;
|
||||||
|
|
||||||
|
f64 sign = 1.0;
|
||||||
|
if (str[i] == '-') {
|
||||||
|
sign = -1.0;
|
||||||
|
i++;
|
||||||
|
} else if (*str == '+') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (len-i > 2 &&
|
||||||
|
str[i] == '0' &&
|
||||||
|
str[i+1] == 'h') {
|
||||||
|
i += 2;
|
||||||
|
u8 *text = string.text;
|
||||||
|
isize len = string.len;
|
||||||
|
if (has_prefix) {
|
||||||
|
text += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 base = 16;
|
||||||
|
|
||||||
|
u64 result = {0};
|
||||||
|
for (isize i = 0; i < len; i++) {
|
||||||
|
Rune r = cast(Rune)text[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u64 v = bit128__digit_value(r);
|
||||||
|
if (v >= base) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result *= base;
|
||||||
|
result += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return *cast(f64 *)&result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
f64 value = 0.0;
|
||||||
|
for (; i < len; i++) {
|
||||||
|
Rune r = cast(Rune)str[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i64 v = digit_value(r);
|
||||||
|
if (v >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value *= 10.0;
|
||||||
|
value += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str[i] == '.') {
|
||||||
|
f64 pow10 = 10.0;
|
||||||
|
i++;
|
||||||
|
for (; i < string.len; i++) {
|
||||||
|
Rune r = cast(Rune)str[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i64 v = digit_value(r);
|
||||||
|
if (v >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value += v/pow10;
|
||||||
|
pow10 *= 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool frac = false;
|
||||||
|
f64 scale = 1.0;
|
||||||
|
if ((str[i] == 'e') || (str[i] == 'E')) {
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (str[i] == '-') {
|
||||||
|
frac = true;
|
||||||
|
i++;
|
||||||
|
} else if (str[i] == '+') {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 exp = 0;
|
||||||
|
for (; i < len; i++) {
|
||||||
|
Rune r = cast(Rune)str[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u32 d = cast(u32)digit_value(r);
|
||||||
|
if (d >= 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exp = exp * 10 + d;
|
||||||
|
}
|
||||||
|
if (exp > 308) exp = 308;
|
||||||
|
|
||||||
|
while (exp >= 50) { scale *= 1e50; exp -= 50; }
|
||||||
|
while (exp >= 8) { scale *= 1e8; exp -= 8; }
|
||||||
|
while (exp > 0) { scale *= 10.0; exp -= 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return sign * (frac ? (value / scale) : (value * scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_float_from_string(String string) {
|
||||||
|
return exact_value_float(float_from_string(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExactValue exact_value_from_basic_literal(Token token) {
|
||||||
|
switch (token.kind) {
|
||||||
|
case Token_String: return exact_value_string(token.string);
|
||||||
|
case Token_Integer: return exact_value_integer_from_string(token.string);
|
||||||
|
case Token_Float: return exact_value_float_from_string(token.string);
|
||||||
|
case Token_Imag: {
|
||||||
|
String str = token.string;
|
||||||
|
Rune last_rune = cast(Rune)str[str.len-1];
|
||||||
|
str.len--; // Ignore the `i|j|k`
|
||||||
|
f64 imag = float_from_string(str);
|
||||||
|
|
||||||
|
if (last_rune == 'i') {
|
||||||
|
return exact_value_complex(0, imag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Token_Rune: {
|
||||||
|
Rune r = GB_RUNE_INVALID;
|
||||||
|
gb_utf8_decode(token.string.text, token.string.len, &r);
|
||||||
|
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
|
||||||
|
return exact_value_i64(r);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
GB_PANIC("Invalid token for basic literal");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue result = {ExactValue_Invalid};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_to_integer(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return v;
|
||||||
|
case ExactValue_Float: {
|
||||||
|
i128 i = i128_from_f64(v.value_float);
|
||||||
|
f64 f = i128_to_f64(i);
|
||||||
|
if (f == v.value_float) {
|
||||||
|
return exact_value_i128(i);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Pointer:
|
||||||
|
return exact_value_i64(cast(i64)cast(intptr)v.value_pointer);
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_to_float(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return exact_value_float(i128_to_f64(v.value_integer));
|
||||||
|
case ExactValue_Float:
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_to_complex(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return exact_value_complex(i128_to_f64(v.value_integer), 0);
|
||||||
|
case ExactValue_Float:
|
||||||
|
return exact_value_complex(v.value_float, 0);
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExactValue exact_value_real(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
case ExactValue_Float:
|
||||||
|
return v;
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return exact_value_float(v.value_complex.real);
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_imag(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
case ExactValue_Float:
|
||||||
|
return exact_value_i64(0);
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return exact_value_float(v.value_complex.imag);
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExactValue exact_value_make_imag(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return exact_value_complex(0, exact_value_to_float(v).value_float);
|
||||||
|
case ExactValue_Float:
|
||||||
|
return exact_value_complex(0, v.value_float);
|
||||||
|
default:
|
||||||
|
GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
|
||||||
|
}
|
||||||
|
ExactValue r = {ExactValue_Invalid};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
||||||
|
switch (op) {
|
||||||
|
case Token_Add: {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
case ExactValue_Integer:
|
||||||
|
case ExactValue_Float:
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Token_Sub: {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
return v;
|
||||||
|
case ExactValue_Integer: {
|
||||||
|
ExactValue i = v;
|
||||||
|
i.value_integer = -i.value_integer;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case ExactValue_Float: {
|
||||||
|
ExactValue i = v;
|
||||||
|
i.value_float = -i.value_float;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case ExactValue_Complex: {
|
||||||
|
f64 real = v.value_complex.real;
|
||||||
|
f64 imag = v.value_complex.imag;
|
||||||
|
return exact_value_complex(-real, -imag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Token_Xor: {
|
||||||
|
i128 i = I128_ZERO;
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
return v;
|
||||||
|
case ExactValue_Integer:
|
||||||
|
i = ~v.value_integer;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): unsigned integers will be negative and will need to be
|
||||||
|
// limited to the types precision
|
||||||
|
// IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored
|
||||||
|
if (0 < precision && precision < 128) {
|
||||||
|
i = i & ~(I128_NEG_ONE << precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exact_value_i128(i);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case Token_Not: {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Invalid: return v;
|
||||||
|
case ExactValue_Bool:
|
||||||
|
return exact_value_bool(!v.value_bool);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
failure:
|
||||||
|
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
||||||
|
|
||||||
|
ExactValue error_value = {};
|
||||||
|
return error_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(bill): Make sure things are evaluated in correct order
|
||||||
|
i32 exact_value_order(ExactValue v) {
|
||||||
|
switch (v.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
return 0;
|
||||||
|
case ExactValue_Bool:
|
||||||
|
case ExactValue_String:
|
||||||
|
return 1;
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return 2;
|
||||||
|
case ExactValue_Float:
|
||||||
|
return 3;
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return 4;
|
||||||
|
case ExactValue_Pointer:
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
default:
|
||||||
|
GB_PANIC("How'd you get here? Invalid Value.kind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||||
|
if (exact_value_order(*y) < exact_value_order(*x)) {
|
||||||
|
match_exact_values(y, x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (x->kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
*y = *x;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ExactValue_Bool:
|
||||||
|
case ExactValue_String:
|
||||||
|
case ExactValue_Complex:
|
||||||
|
return;
|
||||||
|
|
||||||
|
case ExactValue_Integer:
|
||||||
|
switch (y->kind) {
|
||||||
|
case ExactValue_Integer:
|
||||||
|
return;
|
||||||
|
case ExactValue_Float:
|
||||||
|
// TODO(bill): Is this good enough?
|
||||||
|
*x = exact_value_float(i128_to_f64(x->value_integer));
|
||||||
|
return;
|
||||||
|
case ExactValue_Complex:
|
||||||
|
*x = exact_value_complex(i128_to_f64(x->value_integer), 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExactValue_Float:
|
||||||
|
switch (y->kind) {
|
||||||
|
case ExactValue_Float:
|
||||||
|
return;
|
||||||
|
case ExactValue_Complex:
|
||||||
|
*x = exact_value_to_complex(*x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
||||||
|
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
|
||||||
|
match_exact_values(&x, &y);
|
||||||
|
|
||||||
|
switch (x.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
return x;
|
||||||
|
|
||||||
|
case ExactValue_Bool:
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
|
||||||
|
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
|
||||||
|
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
|
||||||
|
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
|
||||||
|
default: goto error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExactValue_Integer: {
|
||||||
|
i128 a = x.value_integer;
|
||||||
|
i128 b = y.value_integer;
|
||||||
|
i128 c = I128_ZERO;
|
||||||
|
switch (op) {
|
||||||
|
case Token_Add: c = a + b; break;
|
||||||
|
case Token_Sub: c = a - b; break;
|
||||||
|
case Token_Mul: c = a * b; break;
|
||||||
|
case Token_Quo: return exact_value_float(fmod(i128_to_f64(a), i128_to_f64(b)));
|
||||||
|
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
||||||
|
case Token_Mod: c = a % b; break;
|
||||||
|
case Token_ModMod: c = ((a % b) + b) % b; break;
|
||||||
|
case Token_And: c = a & b; break;
|
||||||
|
case Token_Or: c = a | b; break;
|
||||||
|
case Token_Xor: c = a ^ b; break;
|
||||||
|
case Token_AndNot: c = i128_and_not(a, b); break;
|
||||||
|
case Token_Shl: c = a << cast(u32)i128_to_u64(b); break;
|
||||||
|
case Token_Shr: c = a >> cast(u32)i128_to_u64(b); break;
|
||||||
|
default: goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exact_value_i128(c);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Float: {
|
||||||
|
f64 a = x.value_float;
|
||||||
|
f64 b = y.value_float;
|
||||||
|
switch (op) {
|
||||||
|
case Token_Add: return exact_value_float(a + b);
|
||||||
|
case Token_Sub: return exact_value_float(a - b);
|
||||||
|
case Token_Mul: return exact_value_float(a * b);
|
||||||
|
case Token_Quo: return exact_value_float(a / b);
|
||||||
|
default: goto error;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Complex: {
|
||||||
|
y = exact_value_to_complex(y);
|
||||||
|
f64 a = x.value_complex.real;
|
||||||
|
f64 b = x.value_complex.imag;
|
||||||
|
f64 c = y.value_complex.real;
|
||||||
|
f64 d = y.value_complex.imag;
|
||||||
|
f64 real = 0;
|
||||||
|
f64 imag = 0;
|
||||||
|
switch (op) {
|
||||||
|
case Token_Add:
|
||||||
|
real = a + c;
|
||||||
|
imag = b + d;
|
||||||
|
break;
|
||||||
|
case Token_Sub:
|
||||||
|
real = a - c;
|
||||||
|
imag = b - d;
|
||||||
|
break;
|
||||||
|
case Token_Mul:
|
||||||
|
real = (a*c - b*d);
|
||||||
|
imag = (b*c + a*d);
|
||||||
|
break;
|
||||||
|
case Token_Quo: {
|
||||||
|
f64 s = c*c + d*d;
|
||||||
|
real = (a*c + b*d)/s;
|
||||||
|
imag = (b*c - a*d)/s;
|
||||||
|
} break;
|
||||||
|
default: goto error;
|
||||||
|
}
|
||||||
|
return exact_value_complex(real, imag);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_String: {
|
||||||
|
if (op != Token_Add) goto error;
|
||||||
|
|
||||||
|
// NOTE(bill): How do you minimize this over allocation?
|
||||||
|
String sx = x.value_string;
|
||||||
|
String sy = y.value_string;
|
||||||
|
isize len = sx.len+sy.len;
|
||||||
|
u8 *data = gb_alloc_array(heap_allocator(), u8, len);
|
||||||
|
gb_memmove(data, sx.text, sx.len);
|
||||||
|
gb_memmove(data+sx.len, sy.text, sy.len);
|
||||||
|
return exact_value_string(make_string(data, len));
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:; // NOTE(bill): MSVC accepts this??? apparently you cannot declare variables immediately after labels...
|
||||||
|
return empty_exact_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
|
||||||
|
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
|
||||||
|
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
|
||||||
|
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
|
||||||
|
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
||||||
|
|
||||||
|
|
||||||
|
i32 cmp_f64(f64 a, f64 b) {
|
||||||
|
return (a > b) - (a < b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||||
|
match_exact_values(&x, &y);
|
||||||
|
|
||||||
|
switch (x.kind) {
|
||||||
|
case ExactValue_Invalid:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case ExactValue_Bool:
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return x.value_bool == y.value_bool;
|
||||||
|
case Token_NotEq: return x.value_bool != y.value_bool;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExactValue_Integer: {
|
||||||
|
i128 a = x.value_integer;
|
||||||
|
i128 b = y.value_integer;
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return a == b;
|
||||||
|
case Token_NotEq: return a != b;
|
||||||
|
case Token_Lt: return a < b;
|
||||||
|
case Token_LtEq: return a <= b;
|
||||||
|
case Token_Gt: return a > b;
|
||||||
|
case Token_GtEq: return a >= b;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Float: {
|
||||||
|
f64 a = x.value_float;
|
||||||
|
f64 b = y.value_float;
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return cmp_f64(a, b) == 0;
|
||||||
|
case Token_NotEq: return cmp_f64(a, b) != 0;
|
||||||
|
case Token_Lt: return cmp_f64(a, b) < 0;
|
||||||
|
case Token_LtEq: return cmp_f64(a, b) <= 0;
|
||||||
|
case Token_Gt: return cmp_f64(a, b) > 0;
|
||||||
|
case Token_GtEq: return cmp_f64(a, b) >= 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Complex: {
|
||||||
|
f64 a = x.value_complex.real;
|
||||||
|
f64 b = x.value_complex.imag;
|
||||||
|
f64 c = y.value_complex.real;
|
||||||
|
f64 d = y.value_complex.imag;
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
|
||||||
|
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_String: {
|
||||||
|
String a = x.value_string;
|
||||||
|
String b = y.value_string;
|
||||||
|
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return a == b;
|
||||||
|
case Token_NotEq: return a != b;
|
||||||
|
case Token_Lt: return a < b;
|
||||||
|
case Token_LtEq: return a <= b;
|
||||||
|
case Token_Gt: return a > b;
|
||||||
|
case Token_GtEq: return a >= b;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case ExactValue_Type:
|
||||||
|
switch (op) {
|
||||||
|
case Token_CmpEq: return are_types_identical(x.value_type, y.value_type);
|
||||||
|
case Token_NotEq: return !are_types_identical(x.value_type, y.value_type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_PANIC("Invalid comparison");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
+654
-239
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
|||||||
|
|
||||||
|
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
|
||||||
|
#define MSVC_AMD64_INTRINSICS
|
||||||
|
#include <intrin.h>
|
||||||
|
#pragma intrinsic(_mul128)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BIT128_U64_HIGHBIT 0x8000000000000000ull
|
||||||
|
#define BIT128_U64_BITS62 0x7fffffffffffffffull
|
||||||
|
#define BIT128_U64_ALLBITS 0xffffffffffffffffull
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct u128 { u64 lo; u64 hi; } u128;
|
||||||
|
typedef struct i128 { u64 lo; i64 hi; } i128;
|
||||||
|
|
||||||
|
|
||||||
|
static u128 const U128_ZERO = {0, 0};
|
||||||
|
static u128 const U128_ONE = {1, 0};
|
||||||
|
static i128 const I128_ZERO = {0, 0};
|
||||||
|
static i128 const I128_ONE = {1, 0};
|
||||||
|
static u128 const U128_NEG_ONE = {BIT128_U64_ALLBITS, BIT128_U64_ALLBITS};
|
||||||
|
static i128 const I128_NEG_ONE = {BIT128_U64_ALLBITS, cast(i64)BIT128_U64_ALLBITS};
|
||||||
|
|
||||||
|
u128 u128_lo_hi (u64 lo, u64 hi);
|
||||||
|
u128 u128_from_u32 (u32 u);
|
||||||
|
u128 u128_from_u64 (u64 u);
|
||||||
|
u128 u128_from_i64 (i64 u);
|
||||||
|
u128 u128_from_f32 (f32 f);
|
||||||
|
u128 u128_from_f64 (f64 f);
|
||||||
|
u128 u128_from_string(String string);
|
||||||
|
|
||||||
|
i128 i128_lo_hi (u64 lo, i64 hi);
|
||||||
|
i128 i128_from_u32 (u32 u);
|
||||||
|
i128 i128_from_u64 (u64 u);
|
||||||
|
i128 i128_from_i64 (i64 u);
|
||||||
|
i128 i128_from_f32 (f32 f);
|
||||||
|
i128 i128_from_f64 (f64 f);
|
||||||
|
i128 i128_from_string(String string);
|
||||||
|
|
||||||
|
u64 u128_to_u64(u128 a);
|
||||||
|
i64 u128_to_i64(u128 a);
|
||||||
|
f64 u128_to_f64(u128 a);
|
||||||
|
i128 u128_to_i128(u128 a);
|
||||||
|
|
||||||
|
u64 i128_to_u64(i128 a);
|
||||||
|
i64 i128_to_i64(i128 a);
|
||||||
|
f64 i128_to_f64(i128 a);
|
||||||
|
u128 i128_to_u128(i128 a);
|
||||||
|
|
||||||
|
String u128_to_string(u128 a, char *buf, isize len);
|
||||||
|
String i128_to_string(i128 a, char *buf, isize len);
|
||||||
|
|
||||||
|
i32 u128_cmp (u128 a, u128 b);
|
||||||
|
bool u128_eq (u128 a, u128 b);
|
||||||
|
bool u128_ne (u128 a, u128 b);
|
||||||
|
bool u128_lt (u128 a, u128 b);
|
||||||
|
bool u128_gt (u128 a, u128 b);
|
||||||
|
bool u128_le (u128 a, u128 b);
|
||||||
|
bool u128_ge (u128 a, u128 b);
|
||||||
|
u128 u128_add (u128 a, u128 b);
|
||||||
|
u128 u128_not (u128 a);
|
||||||
|
u128 u128_neg (u128 a);
|
||||||
|
u128 u128_sub (u128 a, u128 b);
|
||||||
|
u128 u128_and (u128 a, u128 b);
|
||||||
|
u128 u128_or (u128 a, u128 b);
|
||||||
|
u128 u128_xor (u128 a, u128 b);
|
||||||
|
u128 u128_and_not(u128 a, u128 b);
|
||||||
|
u128 u128_shl (u128 a, u32 n);
|
||||||
|
u128 u128_shr (u128 a, u32 n);
|
||||||
|
u128 u128_mul (u128 a, u128 b);
|
||||||
|
void u128_divide (u128 num, u128 den, u128 *quo, u128 *rem);
|
||||||
|
u128 u128_quo (u128 a, u128 b);
|
||||||
|
u128 u128_mod (u128 a, u128 b);
|
||||||
|
|
||||||
|
i128 i128_abs (i128 a);
|
||||||
|
i32 i128_cmp (i128 a, i128 b);
|
||||||
|
bool i128_eq (i128 a, i128 b);
|
||||||
|
bool i128_ne (i128 a, i128 b);
|
||||||
|
bool i128_lt (i128 a, i128 b);
|
||||||
|
bool i128_gt (i128 a, i128 b);
|
||||||
|
bool i128_le (i128 a, i128 b);
|
||||||
|
bool i128_ge (i128 a, i128 b);
|
||||||
|
i128 i128_add (i128 a, i128 b);
|
||||||
|
i128 i128_not (i128 a);
|
||||||
|
i128 i128_neg (i128 a);
|
||||||
|
i128 i128_sub (i128 a, i128 b);
|
||||||
|
i128 i128_and (i128 a, i128 b);
|
||||||
|
i128 i128_or (i128 a, i128 b);
|
||||||
|
i128 i128_xor (i128 a, i128 b);
|
||||||
|
i128 i128_and_not(i128 a, i128 b);
|
||||||
|
i128 i128_shl (i128 a, u32 n);
|
||||||
|
i128 i128_shr (i128 a, u32 n);
|
||||||
|
i128 i128_mul (i128 a, i128 b);
|
||||||
|
void i128_divide (i128 num, i128 den, i128 *quo, i128 *rem);
|
||||||
|
i128 i128_quo (i128 a, i128 b);
|
||||||
|
i128 i128_mod (i128 a, i128 b);
|
||||||
|
|
||||||
|
bool operator==(u128 a, u128 b) { return u128_eq(a, b); }
|
||||||
|
bool operator!=(u128 a, u128 b) { return u128_ne(a, b); }
|
||||||
|
bool operator< (u128 a, u128 b) { return u128_lt(a, b); }
|
||||||
|
bool operator> (u128 a, u128 b) { return u128_gt(a, b); }
|
||||||
|
bool operator<=(u128 a, u128 b) { return u128_le(a, b); }
|
||||||
|
bool operator>=(u128 a, u128 b) { return u128_ge(a, b); }
|
||||||
|
|
||||||
|
u128 operator+(u128 a, u128 b) { return u128_add(a, b); }
|
||||||
|
u128 operator-(u128 a, u128 b) { return u128_sub(a, b); }
|
||||||
|
u128 operator*(u128 a, u128 b) { return u128_mul(a, b); }
|
||||||
|
u128 operator/(u128 a, u128 b) { return u128_quo(a, b); }
|
||||||
|
u128 operator%(u128 a, u128 b) { return u128_mod(a, b); }
|
||||||
|
u128 operator&(u128 a, u128 b) { return u128_and(a, b); }
|
||||||
|
u128 operator|(u128 a, u128 b) { return u128_or (a, b); }
|
||||||
|
u128 operator^(u128 a, u128 b) { return u128_xor(a, b); }
|
||||||
|
u128 operator~(u128 a) { return u128_not(a); }
|
||||||
|
u128 operator+(u128 a) { return a; }
|
||||||
|
u128 operator-(u128 a) { return u128_neg(a); }
|
||||||
|
u128 operator<<(u128 a, u32 b) { return u128_shl(a, b); }
|
||||||
|
u128 operator>>(u128 a, u32 b) { return u128_shr(a, b); }
|
||||||
|
|
||||||
|
|
||||||
|
bool operator==(i128 a, i128 b) { return i128_eq(a, b); }
|
||||||
|
bool operator!=(i128 a, i128 b) { return i128_ne(a, b); }
|
||||||
|
bool operator< (i128 a, i128 b) { return i128_lt(a, b); }
|
||||||
|
bool operator> (i128 a, i128 b) { return i128_gt(a, b); }
|
||||||
|
bool operator<=(i128 a, i128 b) { return i128_le(a, b); }
|
||||||
|
bool operator>=(i128 a, i128 b) { return i128_ge(a, b); }
|
||||||
|
|
||||||
|
i128 operator+(i128 a, i128 b) { return i128_add(a, b); }
|
||||||
|
i128 operator-(i128 a, i128 b) { return i128_sub(a, b); }
|
||||||
|
i128 operator*(i128 a, i128 b) { return i128_mul(a, b); }
|
||||||
|
i128 operator/(i128 a, i128 b) { return i128_quo(a, b); }
|
||||||
|
i128 operator%(i128 a, i128 b) { return i128_mod(a, b); }
|
||||||
|
i128 operator&(i128 a, i128 b) { return i128_and(a, b); }
|
||||||
|
i128 operator|(i128 a, i128 b) { return i128_or (a, b); }
|
||||||
|
i128 operator^(i128 a, i128 b) { return i128_xor(a, b); }
|
||||||
|
i128 operator~(i128 a) { return i128_not(a); }
|
||||||
|
i128 operator+(i128 a) { return a; }
|
||||||
|
i128 operator-(i128 a) { return i128_neg(a); }
|
||||||
|
i128 operator<<(i128 a, u32 b) { return i128_shl(a, b); }
|
||||||
|
i128 operator>>(i128 a, u32 b) { return i128_shr(a, b); }
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
u64 bit128__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
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 u128_lo_hi(u64 lo, u64 hi) {
|
||||||
|
u128 r = {};
|
||||||
|
r.lo = lo;
|
||||||
|
r.hi = hi;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
u128 u128_from_u32(u32 u) { return u128_lo_hi(cast(u64)u, 0); }
|
||||||
|
u128 u128_from_u64(u64 u) { return u128_lo_hi(cast(u64)u, 0); }
|
||||||
|
u128 u128_from_i64(i64 u) { return u128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
|
||||||
|
u128 u128_from_f32(f32 f) { return u128_lo_hi(cast(u64)f, 0); }
|
||||||
|
u128 u128_from_f64(f64 f) { return u128_lo_hi(cast(u64)f, 0); }
|
||||||
|
u128 u128_from_string(String string) {
|
||||||
|
// TODO(bill): Allow for numbers with underscores in them
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *text = string.text;
|
||||||
|
isize len = string.len;
|
||||||
|
if (has_prefix) {
|
||||||
|
text += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 base_ = u128_from_u64(base);
|
||||||
|
|
||||||
|
u128 result = {0};
|
||||||
|
for (isize i = 0; i < len; i++) {
|
||||||
|
Rune r = cast(Rune)text[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u64 v = bit128__digit_value(r);
|
||||||
|
if (v >= base) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = u128_mul(result, base_);
|
||||||
|
result = u128_add(result, u128_from_u64(v));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
i128 i128_lo_hi(u64 lo, i64 hi) {
|
||||||
|
i128 i;
|
||||||
|
i.lo = lo;
|
||||||
|
i.hi = hi;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
i128 i128_from_u32(u32 u) { return i128_lo_hi(cast(u64)u, 0); }
|
||||||
|
i128 i128_from_u64(u64 u) { return i128_lo_hi(cast(u64)u, 0); }
|
||||||
|
i128 i128_from_i64(i64 u) { return i128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
|
||||||
|
i128 i128_from_f32(f32 f) { return i128_lo_hi(cast(u64)f, 0); }
|
||||||
|
i128 i128_from_f64(f64 f) { return i128_lo_hi(cast(u64)f, 0); }
|
||||||
|
i128 i128_from_string(String string) {
|
||||||
|
// TODO(bill): Allow for numbers with underscores in them
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *text = string.text;
|
||||||
|
isize len = string.len;
|
||||||
|
if (has_prefix) {
|
||||||
|
text += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
i128 base_ = i128_from_u64(base);
|
||||||
|
|
||||||
|
i128 result = {0};
|
||||||
|
for (isize i = 0; i < len; i++) {
|
||||||
|
Rune r = cast(Rune)text[i];
|
||||||
|
if (r == '_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u64 v = bit128__digit_value(r);
|
||||||
|
if (v >= base) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = i128_mul(result, base_);
|
||||||
|
result = i128_add(result, i128_from_u64(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
u64 u128_to_u64(u128 a) {
|
||||||
|
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
|
||||||
|
}
|
||||||
|
i64 u128_to_i64(u128 a) {
|
||||||
|
return a.lo;
|
||||||
|
}
|
||||||
|
f64 u128_to_f64(u128 a) {
|
||||||
|
if (a.hi >= 0) {
|
||||||
|
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
|
||||||
|
}
|
||||||
|
i64 h = cast(i64)a.hi;
|
||||||
|
u64 l = a.lo;
|
||||||
|
h = ~h;
|
||||||
|
l = ~l;
|
||||||
|
l += 1;
|
||||||
|
if (l == 0) {
|
||||||
|
h += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
|
||||||
|
}
|
||||||
|
i128 u128_to_i128(u128 a) {
|
||||||
|
return *cast(i128 *)&a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
u64 i128_to_u64(i128 a) {
|
||||||
|
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
|
||||||
|
}
|
||||||
|
i64 i128_to_i64(i128 a) {
|
||||||
|
return cast(i64)a.lo;
|
||||||
|
}
|
||||||
|
f64 i128_to_f64(i128 a) {
|
||||||
|
if (a.hi >= 0) {
|
||||||
|
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
|
||||||
|
}
|
||||||
|
i64 h = a.hi;
|
||||||
|
u64 l = a.lo;
|
||||||
|
h = ~h;
|
||||||
|
l = ~l;
|
||||||
|
l += 1;
|
||||||
|
if (l == 0) {
|
||||||
|
h += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
|
||||||
|
}
|
||||||
|
u128 i128_to_u128(i128 a) {
|
||||||
|
return *cast(u128 *)&a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
String u128_to_string(u128 v, char *out_buf, isize out_buf_len) {
|
||||||
|
char buf[200] = {0};
|
||||||
|
isize i = gb_size_of(buf);
|
||||||
|
|
||||||
|
u128 b = u128_from_u64(10);;
|
||||||
|
while (u128_ge(v, b)) {
|
||||||
|
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||||
|
v = u128_quo(v, b);
|
||||||
|
}
|
||||||
|
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||||
|
|
||||||
|
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||||
|
gb_memcopy(out_buf, &buf[i], len);
|
||||||
|
return make_string(cast(u8 *)out_buf, len);
|
||||||
|
}
|
||||||
|
String i128_to_string(i128 a, char *out_buf, isize out_buf_len) {
|
||||||
|
char buf[200] = {0};
|
||||||
|
isize i = gb_size_of(buf);
|
||||||
|
bool negative = false;
|
||||||
|
if (i128_lt(a, I128_ZERO)) {
|
||||||
|
negative = true;
|
||||||
|
a = i128_neg(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 v = *cast(u128 *)&a;
|
||||||
|
u128 b = u128_from_u64(10);;
|
||||||
|
while (u128_ge(v, b)) {
|
||||||
|
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||||
|
v = u128_quo(v, b);
|
||||||
|
}
|
||||||
|
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
buf[--i] = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||||
|
gb_memcopy(out_buf, &buf[i], len);
|
||||||
|
return make_string(cast(u8 *)out_buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
i32 u128_cmp(u128 a, u128 b) {
|
||||||
|
if (a.hi == b.hi && b.lo == b.lo) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a.hi == b.hi) {
|
||||||
|
return a.lo < b.lo ? -1 : +1;
|
||||||
|
}
|
||||||
|
return a.hi < b.hi ? -1 : +1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
|
||||||
|
bool u128_ne(u128 a, u128 b) { return !u128_eq(a, b); }
|
||||||
|
bool u128_lt(u128 a, u128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
|
||||||
|
bool u128_gt(u128 a, u128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
|
||||||
|
bool u128_le(u128 a, u128 b) { return !u128_gt(a, b); }
|
||||||
|
bool u128_ge(u128 a, u128 b) { return !u128_lt(a, b); }
|
||||||
|
|
||||||
|
u128 u128_add(u128 a, u128 b) {
|
||||||
|
u128 old_a = a;
|
||||||
|
a.lo += b.lo;
|
||||||
|
a.hi += b.hi;
|
||||||
|
if (a.lo < old_a.lo) {
|
||||||
|
a.hi += 1;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
u128 u128_not(u128 a) { return u128_lo_hi(~a.lo, ~a.hi); }
|
||||||
|
|
||||||
|
u128 u128_neg(u128 a) {
|
||||||
|
return u128_add(u128_not(a), u128_from_u64(1));
|
||||||
|
}
|
||||||
|
u128 u128_sub(u128 a, u128 b) {
|
||||||
|
return u128_add(a, u128_neg(b));
|
||||||
|
}
|
||||||
|
u128 u128_and(u128 a, u128 b) { return u128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
|
||||||
|
u128 u128_or (u128 a, u128 b) { return u128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
|
||||||
|
u128 u128_xor(u128 a, u128 b) { return u128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
|
||||||
|
u128 u128_and_not(u128 a, u128 b) { return u128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
|
||||||
|
|
||||||
|
|
||||||
|
u128 u128_shl(u128 a, u32 n) {
|
||||||
|
if (n >= 128) {
|
||||||
|
return u128_lo_hi(0, 0);
|
||||||
|
}
|
||||||
|
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
a.hi = __shiftleft128(a.lo, a.hi, n);
|
||||||
|
a.lo = a.lo << n;
|
||||||
|
return a;
|
||||||
|
#else
|
||||||
|
if (n >= 64) {
|
||||||
|
n -= 64;
|
||||||
|
a.hi = a.lo;
|
||||||
|
a.lo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
u64 mask = ~(BIT128_U64_ALLBITS >> n);
|
||||||
|
|
||||||
|
a.hi <<= n;
|
||||||
|
a.hi |= (a.lo&mask) >> (64 - n);
|
||||||
|
a.lo <<= n;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 u128_shr(u128 a, u32 n) {
|
||||||
|
if (n >= 128) {
|
||||||
|
return u128_lo_hi(0, 0);
|
||||||
|
}
|
||||||
|
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
a.lo = __shiftright128(a.lo, a.hi, n);
|
||||||
|
a.hi = a.hi >> n;
|
||||||
|
return a;
|
||||||
|
#else
|
||||||
|
if (n >= 64) {
|
||||||
|
n -= 64;
|
||||||
|
a.lo = a.hi;
|
||||||
|
a.hi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
u64 mask = ~(BIT128_U64_ALLBITS << n);
|
||||||
|
a.lo >>= n;
|
||||||
|
a.lo |= (a.hi&mask) << (64 - n);
|
||||||
|
a.hi >>= n;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
u128 u128_mul(u128 a, u128 b) {
|
||||||
|
if (a.lo == 0 && a.hi == 0) {
|
||||||
|
return u128_from_u64(0);
|
||||||
|
} else if (b.lo == 0 && b.hi == 0) {
|
||||||
|
return u128_from_u64(0);
|
||||||
|
}
|
||||||
|
if (u128_eq(a, U128_ONE)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (u128_eq(b, U128_ONE)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
if (a.hi == 0 && b.hi == 0) {
|
||||||
|
a.lo = _umul128(a.lo, b.lo, &a.hi);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u128 res = {0};
|
||||||
|
u128 t = b;
|
||||||
|
for (u32 i = 0; i < 128; i++) {
|
||||||
|
if ((t.lo&1) != 0) {
|
||||||
|
res = u128_add(res, u128_shl(a, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
t = u128_shr(t, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool u128_hibit(u128 *d) { return (d->hi & BIT128_U64_HIGHBIT) != 0; }
|
||||||
|
|
||||||
|
void u128_divide(u128 num, u128 den, u128 *quo, u128 *rem) {
|
||||||
|
if (u128_eq(den, U128_ZERO)) {
|
||||||
|
if (quo) *quo = u128_from_u64(num.lo/den.lo);
|
||||||
|
if (rem) *rem = U128_ZERO;
|
||||||
|
} else {
|
||||||
|
u128 n = num;
|
||||||
|
u128 d = den;
|
||||||
|
u128 x = U128_ONE;
|
||||||
|
u128 r = U128_ZERO;
|
||||||
|
|
||||||
|
while (u128_ge(n, d) && !u128_hibit(&d)) {
|
||||||
|
x = u128_shl(x, 1);
|
||||||
|
d = u128_shl(d, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (u128_ne(x, U128_ZERO)) {
|
||||||
|
if (u128_ge(n, d)) {
|
||||||
|
n = u128_sub(n, d);
|
||||||
|
r = u128_or(r, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = u128_shr(x, 1);
|
||||||
|
d = u128_shr(d, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quo) *quo = r;
|
||||||
|
if (rem) *rem = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 u128_quo(u128 a, u128 b) {
|
||||||
|
if (a.hi == 0 && b.hi == 0) {
|
||||||
|
return u128_from_u64(a.lo/b.lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 res = {0};
|
||||||
|
u128_divide(a, b, &res, nullptr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
u128 u128_mod(u128 a, u128 b) {
|
||||||
|
if (a.hi == 0 && b.hi == 0) {
|
||||||
|
return u128_from_u64(a.lo%b.lo);
|
||||||
|
}
|
||||||
|
u128 res = {0};
|
||||||
|
u128_divide(a, b, nullptr, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
i128 i128_abs(i128 a) {
|
||||||
|
if ((a.hi&BIT128_U64_HIGHBIT) != 0) {
|
||||||
|
return i128_neg(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
i32 i128_cmp(i128 a, i128 b) {
|
||||||
|
if (a.hi == b.hi && b.lo == b.lo) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (a.hi == b.hi) {
|
||||||
|
return a.lo < b.lo ? -1 : +1;
|
||||||
|
}
|
||||||
|
return a.hi < b.hi ? -1 : +1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool i128_eq(i128 a, i128 b) { return a.hi == b.hi && a.lo == b.lo; }
|
||||||
|
bool i128_ne(i128 a, i128 b) { return !i128_eq(a, b); }
|
||||||
|
bool i128_lt(i128 a, i128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
|
||||||
|
bool i128_gt(i128 a, i128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
|
||||||
|
bool i128_le(i128 a, i128 b) { return a.hi == b.hi ? a.lo <= b.lo : a.hi <= b.hi; }
|
||||||
|
bool i128_ge(i128 a, i128 b) { return a.hi == b.hi ? a.lo >= b.lo : a.hi >= b.hi; }
|
||||||
|
|
||||||
|
i128 i128_add(i128 a, i128 b) {
|
||||||
|
i128 old_a = a;
|
||||||
|
a.lo += b.lo;
|
||||||
|
a.hi += b.hi;
|
||||||
|
if (a.lo < old_a.lo) {
|
||||||
|
a.hi += 1;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
i128 i128_not(i128 a) { return i128_lo_hi(~a.lo, ~a.hi); }
|
||||||
|
|
||||||
|
i128 i128_neg(i128 a) {
|
||||||
|
return i128_add(i128_not(a), i128_from_u64(1));
|
||||||
|
}
|
||||||
|
i128 i128_sub(i128 a, i128 b) {
|
||||||
|
return i128_add(a, i128_neg(b));
|
||||||
|
}
|
||||||
|
i128 i128_and(i128 a, i128 b) { return i128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
|
||||||
|
i128 i128_or (i128 a, i128 b) { return i128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
|
||||||
|
i128 i128_xor(i128 a, i128 b) { return i128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
|
||||||
|
i128 i128_and_not(i128 a, i128 b) { return i128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
|
||||||
|
|
||||||
|
|
||||||
|
i128 i128_shl(i128 a, u32 n) {
|
||||||
|
if (n >= 128) {
|
||||||
|
return i128_lo_hi(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
a.hi = __shiftleft128(a.lo, a.hi, n);
|
||||||
|
a.lo = a.lo << n;
|
||||||
|
return a;
|
||||||
|
#else
|
||||||
|
if (n >= 64) {
|
||||||
|
n -= 64;
|
||||||
|
a.hi = a.lo;
|
||||||
|
a.lo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
u64 mask = ~(BIT128_U64_ALLBITS >> n);
|
||||||
|
|
||||||
|
a.hi <<= n;
|
||||||
|
a.hi |= (a.lo&mask) >> (64 - n);
|
||||||
|
a.lo <<= n;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
i128 i128_shr(i128 a, u32 n) {
|
||||||
|
if (n >= 128) {
|
||||||
|
return i128_lo_hi(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
a.lo = __shiftright128(a.lo, a.hi, n);
|
||||||
|
a.hi = a.hi >> n;
|
||||||
|
return a;
|
||||||
|
#else
|
||||||
|
if (n >= 64) {
|
||||||
|
n -= 64;
|
||||||
|
a.lo = a.hi;
|
||||||
|
a.hi = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
u64 mask = ~(BIT128_U64_ALLBITS << n);
|
||||||
|
a.lo >>= n;
|
||||||
|
a.lo |= (a.hi&mask) << (64 - n);
|
||||||
|
a.hi >>= n;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
i128 i128_mul(i128 a, i128 b) {
|
||||||
|
if (a.lo == 0 && a.hi == 0) {
|
||||||
|
return i128_from_u64(0);
|
||||||
|
} else if (b.lo == 0 && b.hi == 0) {
|
||||||
|
return i128_from_u64(0);
|
||||||
|
}
|
||||||
|
if (i128_eq(a, I128_ONE)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (i128_eq(b, I128_ONE)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MSVC_AMD64_INTRINSICS)
|
||||||
|
if (a.hi == 0 && b.hi == 0) {
|
||||||
|
a.lo = _mul128(a.lo, b.lo, &a.hi);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
i128 res = {0};
|
||||||
|
i128 t = b;
|
||||||
|
for (u32 i = 0; i < 128; i++) {
|
||||||
|
if ((t.lo&1) != 0) {
|
||||||
|
res = i128_add(res, i128_shl(a, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
t = i128_shr(t, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void i128_divide(i128 a, i128 b, i128 *quo, i128 *rem) {
|
||||||
|
// TODO(bill): Which one is correct?!
|
||||||
|
#if 1
|
||||||
|
i128 s = i128_shr(b, 127);
|
||||||
|
b = i128_sub(i128_xor(b, s), s);
|
||||||
|
s = i128_shr(a, 127);
|
||||||
|
b = i128_sub(i128_xor(a, s), s);
|
||||||
|
|
||||||
|
u128 n, r = {0};
|
||||||
|
u128_divide(*cast(u128 *)&a, *cast(u128 *)&b, &n, &r);
|
||||||
|
i128 ni = *cast(i128 *)&n;
|
||||||
|
i128 ri = *cast(i128 *)&r;
|
||||||
|
|
||||||
|
if (quo) *quo = i128_sub(i128_xor(ni, s), s);
|
||||||
|
if (rem) *rem = i128_sub(i128_xor(ri, s), s);
|
||||||
|
#else
|
||||||
|
if (i128_eq(b, I128_ZERO)) {
|
||||||
|
if (quo) *quo = i128_from_u64(a.lo/b.lo);
|
||||||
|
if (rem) *rem = I128_ZERO;
|
||||||
|
} else {
|
||||||
|
i128 n = a;
|
||||||
|
i128 d = b;
|
||||||
|
i128 x = I128_ONE;
|
||||||
|
i128 r = I128_ZERO;
|
||||||
|
|
||||||
|
while (i128_ge(n, d) && ((i128_shr(d, 128-1).lo&1) == 0)) {
|
||||||
|
x = i128_shl(x, 1);
|
||||||
|
d = i128_shl(d, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i128_ne(x, I128_ZERO)) {
|
||||||
|
if (i128_ge(n, d)) {
|
||||||
|
n = i128_sub(n, d);
|
||||||
|
r = i128_or(r, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
x = i128_shr(x, 1);
|
||||||
|
d = i128_shr(d, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quo) *quo = r;
|
||||||
|
if (rem) *rem = n;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
i128 i128_quo(i128 a, i128 b) {
|
||||||
|
i128 res = {0};
|
||||||
|
i128_divide(a, b, &res, nullptr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
i128 i128_mod(i128 a, i128 b) {
|
||||||
|
i128 res = {0};
|
||||||
|
i128_divide(a, b, nullptr, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
+8346
File diff suppressed because it is too large
Load Diff
+154
-153
@@ -1,95 +1,96 @@
|
|||||||
// Optimizations for the SSA code
|
// Optimizations for the IR code
|
||||||
|
|
||||||
void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
|
void ir_opt_add_operands(Array<irValue *> *ops, irInstr *i) {
|
||||||
switch (i->kind) {
|
switch (i->kind) {
|
||||||
case ssaInstr_Comment:
|
case irInstr_Comment:
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Local:
|
case irInstr_Local:
|
||||||
break;
|
break;
|
||||||
case ssaInstr_ZeroInit:
|
case irInstr_ZeroInit:
|
||||||
array_add(ops, i->ZeroInit.address);
|
array_add(ops, i->ZeroInit.address);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Store:
|
case irInstr_Store:
|
||||||
array_add(ops, i->Store.address);
|
array_add(ops, i->Store.address);
|
||||||
array_add(ops, i->Store.value);
|
array_add(ops, i->Store.value);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Load:
|
case irInstr_Load:
|
||||||
array_add(ops, i->Load.address);
|
array_add(ops, i->Load.address);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_ArrayElementPtr:
|
case irInstr_ArrayElementPtr:
|
||||||
array_add(ops, i->ArrayElementPtr.address);
|
array_add(ops, i->ArrayElementPtr.address);
|
||||||
array_add(ops, i->ArrayElementPtr.elem_index);
|
array_add(ops, i->ArrayElementPtr.elem_index);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_StructElementPtr:
|
case irInstr_StructElementPtr:
|
||||||
array_add(ops, i->StructElementPtr.address);
|
array_add(ops, i->StructElementPtr.address);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_PtrOffset:
|
case irInstr_PtrOffset:
|
||||||
array_add(ops, i->PtrOffset.address);
|
array_add(ops, i->PtrOffset.address);
|
||||||
array_add(ops, i->PtrOffset.offset);
|
array_add(ops, i->PtrOffset.offset);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_ArrayExtractValue:
|
case irInstr_StructExtractValue:
|
||||||
array_add(ops, i->ArrayExtractValue.address);
|
|
||||||
break;
|
|
||||||
case ssaInstr_StructExtractValue:
|
|
||||||
array_add(ops, i->StructExtractValue.address);
|
array_add(ops, i->StructExtractValue.address);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Conv:
|
case irInstr_Conv:
|
||||||
array_add(ops, i->Conv.value);
|
array_add(ops, i->Conv.value);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Jump:
|
case irInstr_Jump:
|
||||||
break;
|
break;
|
||||||
case ssaInstr_If:
|
case irInstr_If:
|
||||||
array_add(ops, i->If.cond);
|
array_add(ops, i->If.cond);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Return:
|
case irInstr_Return:
|
||||||
if (i->Return.value != NULL) {
|
if (i->Return.value != nullptr) {
|
||||||
array_add(ops, i->Return.value);
|
array_add(ops, i->Return.value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Select:
|
case irInstr_Select:
|
||||||
array_add(ops, i->Select.cond);
|
array_add(ops, i->Select.cond);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Phi:
|
case irInstr_Phi:
|
||||||
for_array(j, i->Phi.edges) {
|
for_array(j, i->Phi.edges) {
|
||||||
array_add(ops, i->Phi.edges.e[j]);
|
array_add(ops, i->Phi.edges[j]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Unreachable: break;
|
case irInstr_Unreachable:
|
||||||
case ssaInstr_BinaryOp:
|
break;
|
||||||
|
case irInstr_UnaryOp:
|
||||||
|
array_add(ops, i->UnaryOp.expr);
|
||||||
|
break;
|
||||||
|
case irInstr_BinaryOp:
|
||||||
array_add(ops, i->BinaryOp.left);
|
array_add(ops, i->BinaryOp.left);
|
||||||
array_add(ops, i->BinaryOp.right);
|
array_add(ops, i->BinaryOp.right);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_Call:
|
case irInstr_Call:
|
||||||
array_add(ops, i->Call.value);
|
array_add(ops, i->Call.value);
|
||||||
for (isize j = 0; j < i->Call.arg_count; j++) {
|
for (isize j = 0; j < i->Call.arg_count; j++) {
|
||||||
array_add(ops, i->Call.args[j]);
|
array_add(ops, i->Call.args[j]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ssaInstr_VectorExtractElement:
|
// case irInstr_VectorExtractElement:
|
||||||
array_add(ops, i->VectorExtractElement.vector);
|
// array_add(ops, i->VectorExtractElement.vector);
|
||||||
array_add(ops, i->VectorExtractElement.index);
|
// array_add(ops, i->VectorExtractElement.index);
|
||||||
|
// break;
|
||||||
|
// case irInstr_VectorInsertElement:
|
||||||
|
// array_add(ops, i->VectorInsertElement.vector);
|
||||||
|
// array_add(ops, i->VectorInsertElement.elem);
|
||||||
|
// array_add(ops, i->VectorInsertElement.index);
|
||||||
|
// break;
|
||||||
|
// case irInstr_VectorShuffle:
|
||||||
|
// array_add(ops, i->VectorShuffle.vector);
|
||||||
|
// break;
|
||||||
|
case irInstr_StartupRuntime:
|
||||||
break;
|
break;
|
||||||
case ssaInstr_VectorInsertElement:
|
|
||||||
array_add(ops, i->VectorInsertElement.vector);
|
#if 0
|
||||||
array_add(ops, i->VectorInsertElement.elem);
|
case irInstr_BoundsCheck:
|
||||||
array_add(ops, i->VectorInsertElement.index);
|
|
||||||
break;
|
|
||||||
case ssaInstr_VectorShuffle:
|
|
||||||
array_add(ops, i->VectorShuffle.vector);
|
|
||||||
break;
|
|
||||||
case ssaInstr_StartupRuntime:
|
|
||||||
break;
|
|
||||||
case ssaInstr_BoundsCheck:
|
|
||||||
array_add(ops, i->BoundsCheck.index);
|
array_add(ops, i->BoundsCheck.index);
|
||||||
array_add(ops, i->BoundsCheck.len);
|
array_add(ops, i->BoundsCheck.len);
|
||||||
break;
|
break;
|
||||||
case ssaInstr_SliceBoundsCheck:
|
case irInstr_SliceBoundsCheck:
|
||||||
array_add(ops, i->SliceBoundsCheck.low);
|
array_add(ops, i->SliceBoundsCheck.low);
|
||||||
array_add(ops, i->SliceBoundsCheck.high);
|
array_add(ops, i->SliceBoundsCheck.high);
|
||||||
array_add(ops, i->SliceBoundsCheck.max);
|
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,26 +98,26 @@ void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
|
void ir_opt_block_replace_pred(irBlock *b, irBlock *from, irBlock *to) {
|
||||||
for_array(i, b->preds) {
|
for_array(i, b->preds) {
|
||||||
ssaBlock *pred = b->preds.e[i];
|
irBlock *pred = b->preds[i];
|
||||||
if (pred == from) {
|
if (pred == from) {
|
||||||
b->preds.e[i] = to;
|
b->preds[i] = to;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
|
void ir_opt_block_replace_succ(irBlock *b, irBlock *from, irBlock *to) {
|
||||||
for_array(i, b->succs) {
|
for_array(i, b->succs) {
|
||||||
ssaBlock *succ = b->succs.e[i];
|
irBlock *succ = b->succs[i];
|
||||||
if (succ == from) {
|
if (succ == from) {
|
||||||
b->succs.e[i] = to;
|
b->succs[i] = to;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ssa_opt_block_has_phi(ssaBlock *b) {
|
bool ir_opt_block_has_phi(irBlock *b) {
|
||||||
return b->instrs.e[0]->Instr.kind == ssaInstr_Phi;
|
return b->instrs[0]->Instr.kind == irInstr_Phi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -128,11 +129,11 @@ bool ssa_opt_block_has_phi(ssaBlock *b) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
|
Array<irValue *> ir_get_block_phi_nodes(irBlock *b) {
|
||||||
ssaValueArray phis = {0};
|
Array<irValue *> phis = {0};
|
||||||
for_array(i, b->instrs) {
|
for_array(i, b->instrs) {
|
||||||
ssaInstr *instr = &b->instrs.e[i]->Instr;
|
irInstr *instr = &b->instrs[i]->Instr;
|
||||||
if (instr->kind != ssaInstr_Phi) {
|
if (instr->kind != irInstr_Phi) {
|
||||||
phis = b->instrs;
|
phis = b->instrs;
|
||||||
phis.count = i;
|
phis.count = i;
|
||||||
return phis;
|
return phis;
|
||||||
@@ -141,128 +142,128 @@ ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
|
|||||||
return phis;
|
return phis;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_remove_pred(ssaBlock *b, ssaBlock *p) {
|
void ir_remove_pred(irBlock *b, irBlock *p) {
|
||||||
ssaValueArray phis = ssa_get_block_phi_nodes(b);
|
Array<irValue *> phis = ir_get_block_phi_nodes(b);
|
||||||
isize i = 0;
|
isize i = 0;
|
||||||
for_array(j, b->preds) {
|
for_array(j, b->preds) {
|
||||||
ssaBlock *pred = b->preds.e[j];
|
irBlock *pred = b->preds[j];
|
||||||
if (pred != p) {
|
if (pred != p) {
|
||||||
b->preds.e[i] = b->preds.e[j];
|
b->preds[i] = b->preds[j];
|
||||||
for_array(k, phis) {
|
for_array(k, phis) {
|
||||||
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
irInstrPhi *phi = &phis[k]->Instr.Phi;
|
||||||
phi->edges.e[i] = phi->edges.e[j];
|
phi->edges[i] = phi->edges[j];
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b->preds.count = i;
|
b->preds.count = i;
|
||||||
for_array(k, phis) {
|
for_array(k, phis) {
|
||||||
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
irInstrPhi *phi = &phis[k]->Instr.Phi;
|
||||||
phi->edges.count = i;
|
phi->edges.count = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_remove_dead_blocks(ssaProcedure *proc) {
|
void ir_remove_dead_blocks(irProcedure *proc) {
|
||||||
isize j = 0;
|
isize j = 0;
|
||||||
for_array(i, proc->blocks) {
|
for_array(i, proc->blocks) {
|
||||||
ssaBlock *b = proc->blocks.e[i];
|
irBlock *b = proc->blocks[i];
|
||||||
if (b == NULL) {
|
if (b == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// NOTE(bill): Swap order
|
// NOTE(bill): Swap order
|
||||||
b->index = j;
|
b->index = cast(i32)j;
|
||||||
proc->blocks.e[j++] = b;
|
proc->blocks[j++] = b;
|
||||||
}
|
}
|
||||||
proc->blocks.count = j;
|
proc->blocks.count = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_mark_reachable(ssaBlock *b) {
|
void ir_mark_reachable(irBlock *b) {
|
||||||
isize const WHITE = 0;
|
isize const WHITE = 0;
|
||||||
isize const BLACK = -1;
|
isize const BLACK = -1;
|
||||||
b->index = BLACK;
|
b->index = BLACK;
|
||||||
for_array(i, b->succs) {
|
for_array(i, b->succs) {
|
||||||
ssaBlock *succ = b->succs.e[i];
|
irBlock *succ = b->succs[i];
|
||||||
if (succ->index == WHITE) {
|
if (succ->index == WHITE) {
|
||||||
ssa_mark_reachable(succ);
|
ir_mark_reachable(succ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_remove_unreachable_blocks(ssaProcedure *proc) {
|
void ir_remove_unreachable_blocks(irProcedure *proc) {
|
||||||
isize const WHITE = 0;
|
isize const WHITE = 0;
|
||||||
isize const BLACK = -1;
|
isize const BLACK = -1;
|
||||||
for_array(i, proc->blocks) {
|
for_array(i, proc->blocks) {
|
||||||
proc->blocks.e[i]->index = WHITE;
|
proc->blocks[i]->index = WHITE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssa_mark_reachable(proc->blocks.e[0]);
|
ir_mark_reachable(proc->blocks[0]);
|
||||||
|
|
||||||
for_array(i, proc->blocks) {
|
for_array(i, proc->blocks) {
|
||||||
ssaBlock *b = proc->blocks.e[i];
|
irBlock *b = proc->blocks[i];
|
||||||
if (b->index == WHITE) {
|
if (b->index == WHITE) {
|
||||||
for_array(j, b->succs) {
|
for_array(j, b->succs) {
|
||||||
ssaBlock *c = b->succs.e[j];
|
irBlock *c = b->succs[j];
|
||||||
if (c->index == BLACK) {
|
if (c->index == BLACK) {
|
||||||
ssa_remove_pred(c, b);
|
ir_remove_pred(c, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NOTE(bill): Mark as empty but don't actually free it
|
// NOTE(bill): Mark as empty but don't actually free it
|
||||||
// As it's been allocated with an arena
|
// As it's been allocated with an arena
|
||||||
proc->blocks.e[i] = NULL;
|
proc->blocks[i] = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ssa_remove_dead_blocks(proc);
|
ir_remove_dead_blocks(proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
|
bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
|
||||||
if (a->succs.count != 1) {
|
if (a->succs.count != 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ssaBlock *b = a->succs.e[0];
|
irBlock *b = a->succs[0];
|
||||||
if (b->preds.count != 1) {
|
if (b->preds.count != 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssa_opt_block_has_phi(b)) {
|
if (ir_opt_block_has_phi(b)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
array_pop(&a->instrs); // Remove branch at end
|
array_pop(&a->instrs); // Remove branch at end
|
||||||
for_array(i, b->instrs) {
|
for_array(i, b->instrs) {
|
||||||
array_add(&a->instrs, b->instrs.e[i]);
|
array_add(&a->instrs, b->instrs[i]);
|
||||||
ssa_set_instr_parent(b->instrs.e[i], a);
|
ir_set_instr_parent(b->instrs[i], a);
|
||||||
}
|
}
|
||||||
|
|
||||||
array_clear(&a->succs);
|
array_clear(&a->succs);
|
||||||
for_array(i, b->succs) {
|
for_array(i, b->succs) {
|
||||||
array_add(&a->succs, b->succs.e[i]);
|
array_add(&a->succs, b->succs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix preds links
|
// Fix preds links
|
||||||
for_array(i, b->succs) {
|
for_array(i, b->succs) {
|
||||||
ssa_opt_block_replace_pred(b->succs.e[i], b, a);
|
ir_opt_block_replace_pred(b->succs[i], b, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
proc->blocks.e[b->index] = NULL;
|
proc->blocks[b->index] = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_opt_blocks(ssaProcedure *proc) {
|
void ir_opt_blocks(irProcedure *proc) {
|
||||||
ssa_remove_unreachable_blocks(proc);
|
ir_remove_unreachable_blocks(proc);
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
bool changed = true;
|
bool changed = true;
|
||||||
while (changed) {
|
while (changed) {
|
||||||
changed = false;
|
changed = false;
|
||||||
for_array(i, proc->blocks) {
|
for_array(i, proc->blocks) {
|
||||||
ssaBlock *b = proc->blocks.e[i];
|
irBlock *b = proc->blocks[i];
|
||||||
if (b == NULL) {
|
if (b == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GB_ASSERT(b->index == i);
|
GB_ASSERT_MSG(b->index == i, "%d, %td", b->index, i);
|
||||||
|
|
||||||
if (ssa_opt_block_fusion(proc, b)) {
|
if (ir_opt_block_fusion(proc, b)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
// TODO(bill): other simple block optimizations
|
// TODO(bill): other simple block optimizations
|
||||||
@@ -270,26 +271,26 @@ void ssa_opt_blocks(ssaProcedure *proc) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ssa_remove_dead_blocks(proc);
|
ir_remove_dead_blocks(proc);
|
||||||
}
|
}
|
||||||
void ssa_opt_build_referrers(ssaProcedure *proc) {
|
void ir_opt_build_referrers(irProcedure *proc) {
|
||||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||||
|
|
||||||
ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer
|
Array<irValue *> ops = {0}; // NOTE(bill): Act as a buffer
|
||||||
array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
|
array_init(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
|
||||||
for_array(i, proc->blocks) {
|
for_array(i, proc->blocks) {
|
||||||
ssaBlock *b = proc->blocks.e[i];
|
irBlock *b = proc->blocks[i];
|
||||||
for_array(j, b->instrs) {
|
for_array(j, b->instrs) {
|
||||||
ssaValue *instr = b->instrs.e[j];
|
irValue *instr = b->instrs[j];
|
||||||
array_clear(&ops);
|
array_clear(&ops);
|
||||||
ssa_opt_add_operands(&ops, &instr->Instr);
|
ir_opt_add_operands(&ops, &instr->Instr);
|
||||||
for_array(k, ops) {
|
for_array(k, ops) {
|
||||||
ssaValue *op = ops.e[k];
|
irValue *op = ops[k];
|
||||||
if (op == NULL) {
|
if (op == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ssaValueArray *refs = ssa_value_referrers(op);
|
Array<irValue *> *refs = ir_value_referrers(op);
|
||||||
if (refs != NULL) {
|
if (refs != nullptr) {
|
||||||
array_add(refs, instr);
|
array_add(refs, instr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,38 +308,38 @@ void ssa_opt_build_referrers(ssaProcedure *proc) {
|
|||||||
|
|
||||||
// State of Lengauer-Tarjan algorithm
|
// State of Lengauer-Tarjan algorithm
|
||||||
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
||||||
typedef struct ssaLTState {
|
typedef struct irLTState {
|
||||||
isize count;
|
isize count;
|
||||||
// NOTE(bill): These are arrays
|
// NOTE(bill): These are arrays
|
||||||
ssaBlock **sdom; // Semidominator
|
irBlock **sdom; // Semidominator
|
||||||
ssaBlock **parent; // Parent in DFS traversal of CFG
|
irBlock **parent; // Parent in DFS traversal of CFG
|
||||||
ssaBlock **ancestor;
|
irBlock **ancestor;
|
||||||
} ssaLTState;
|
} irLTState;
|
||||||
|
|
||||||
// §2.2 - bottom of page
|
// §2.2 - bottom of page
|
||||||
void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) {
|
void ir_lt_link(irLTState *lt, irBlock *p, irBlock *q) {
|
||||||
lt->ancestor[q->index] = p;
|
lt->ancestor[q->index] = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) {
|
i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorder) {
|
||||||
preorder[i] = p;
|
preorder[i] = p;
|
||||||
p->dom.pre = i++;
|
p->dom.pre = i++;
|
||||||
lt->sdom[p->index] = p;
|
lt->sdom[p->index] = p;
|
||||||
ssa_lt_link(lt, NULL, p);
|
ir_lt_link(lt, nullptr, p);
|
||||||
for_array(index, p->succs) {
|
for_array(index, p->succs) {
|
||||||
ssaBlock *q = p->succs.e[index];
|
irBlock *q = p->succs[index];
|
||||||
if (lt->sdom[q->index] == NULL) {
|
if (lt->sdom[q->index] == nullptr) {
|
||||||
lt->parent[q->index] = p;
|
lt->parent[q->index] = p;
|
||||||
i = ssa_lt_depth_first_search(lt, q, i, preorder);
|
i = ir_lt_depth_first_search(lt, q, i, preorder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
|
irBlock *ir_lt_eval(irLTState *lt, irBlock *v) {
|
||||||
ssaBlock *u = v;
|
irBlock *u = v;
|
||||||
for (;
|
for (;
|
||||||
lt->ancestor[v->index] != NULL;
|
lt->ancestor[v->index] != nullptr;
|
||||||
v = lt->ancestor[v->index]) {
|
v = lt->ancestor[v->index]) {
|
||||||
if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
|
if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
|
||||||
u = v;
|
u = v;
|
||||||
@@ -347,16 +348,16 @@ ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
|
|||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct ssaDomPrePost {
|
typedef struct irDomPrePost {
|
||||||
i32 pre, post;
|
i32 pre, post;
|
||||||
} ssaDomPrePost;
|
} irDomPrePost;
|
||||||
|
|
||||||
ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
|
irDomPrePost ir_opt_number_dom_tree(irBlock *v, i32 pre, i32 post) {
|
||||||
ssaDomPrePost result = {pre, post};
|
irDomPrePost result = {pre, post};
|
||||||
|
|
||||||
v->dom.pre = pre++;
|
v->dom.pre = pre++;
|
||||||
for_array(i, v->dom.children) {
|
for_array(i, v->dom.children) {
|
||||||
result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
|
result = ir_opt_number_dom_tree(v->dom.children[i], result.pre, result.post);
|
||||||
}
|
}
|
||||||
v->dom.post = post++;
|
v->dom.post = post++;
|
||||||
|
|
||||||
@@ -366,35 +367,35 @@ ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): Requires `ssa_opt_blocks` to be called before this
|
// NOTE(bill): Requires `ir_opt_blocks` to be called before this
|
||||||
void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||||
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
||||||
|
|
||||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||||
|
|
||||||
isize n = proc->blocks.count;
|
i32 n = cast(i32)proc->blocks.count;
|
||||||
ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n);
|
irBlock **buf = gb_alloc_array(proc->module->tmp_allocator, irBlock *, 5*n);
|
||||||
|
|
||||||
ssaLTState lt = {0};
|
irLTState lt = {0};
|
||||||
lt.count = n;
|
lt.count = n;
|
||||||
lt.sdom = &buf[0*n];
|
lt.sdom = &buf[0*n];
|
||||||
lt.parent = &buf[1*n];
|
lt.parent = &buf[1*n];
|
||||||
lt.ancestor = &buf[2*n];
|
lt.ancestor = &buf[2*n];
|
||||||
|
|
||||||
ssaBlock **preorder = &buf[3*n];
|
irBlock **preorder = &buf[3*n];
|
||||||
ssaBlock **buckets = &buf[4*n];
|
irBlock **buckets = &buf[4*n];
|
||||||
ssaBlock *root = proc->blocks.e[0];
|
irBlock *root = proc->blocks[0];
|
||||||
|
|
||||||
// Step 1 - number vertices
|
// Step 1 - number vertices
|
||||||
i32 pre_num = ssa_lt_depth_first_search(<, root, 0, preorder);
|
i32 pre_num = ir_lt_depth_first_search(<, root, 0, preorder);
|
||||||
gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
|
gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
|
||||||
|
|
||||||
for (i32 i = n-1; i > 0; i--) {
|
for (i32 i = n-1; i > 0; i--) {
|
||||||
ssaBlock *w = preorder[i];
|
irBlock *w = preorder[i];
|
||||||
|
|
||||||
// Step 3 - Implicitly define idom for nodes
|
// Step 3 - Implicitly define idom for nodes
|
||||||
for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
|
for (irBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
|
||||||
ssaBlock *u = ssa_lt_eval(<, v);
|
irBlock *u = ir_lt_eval(<, v);
|
||||||
if (lt.sdom[u->index]->dom.pre < i) {
|
if (lt.sdom[u->index]->dom.pre < i) {
|
||||||
v->dom.idom = u;
|
v->dom.idom = u;
|
||||||
} else {
|
} else {
|
||||||
@@ -405,14 +406,14 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
|||||||
// Step 2 - Compute all sdoms
|
// Step 2 - Compute all sdoms
|
||||||
lt.sdom[w->index] = lt.parent[w->index];
|
lt.sdom[w->index] = lt.parent[w->index];
|
||||||
for_array(pred_index, w->preds) {
|
for_array(pred_index, w->preds) {
|
||||||
ssaBlock *v = w->preds.e[pred_index];
|
irBlock *v = w->preds[pred_index];
|
||||||
ssaBlock *u = ssa_lt_eval(<, v);
|
irBlock *u = ir_lt_eval(<, v);
|
||||||
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
|
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
|
||||||
lt.sdom[w->index] = lt.sdom[u->index];
|
lt.sdom[w->index] = lt.sdom[u->index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssa_lt_link(<, lt.parent[w->index], w);
|
ir_lt_link(<, lt.parent[w->index], w);
|
||||||
|
|
||||||
if (lt.parent[w->index] == lt.sdom[w->index]) {
|
if (lt.parent[w->index] == lt.sdom[w->index]) {
|
||||||
w->dom.idom = lt.parent[w->index];
|
w->dom.idom = lt.parent[w->index];
|
||||||
@@ -423,15 +424,15 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The rest of Step 3
|
// The rest of Step 3
|
||||||
for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
|
for (irBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
|
||||||
v->dom.idom = root;
|
v->dom.idom = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4 - Explicitly define idom for nodes (in preorder)
|
// Step 4 - Explicitly define idom for nodes (in preorder)
|
||||||
for (isize i = 1; i < n; i++) {
|
for (isize i = 1; i < n; i++) {
|
||||||
ssaBlock *w = preorder[i];
|
irBlock *w = preorder[i];
|
||||||
if (w == root) {
|
if (w == root) {
|
||||||
w->dom.idom = NULL;
|
w->dom.idom = nullptr;
|
||||||
} else {
|
} else {
|
||||||
// Weird tree relationships here!
|
// Weird tree relationships here!
|
||||||
|
|
||||||
@@ -440,7 +441,7 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate children relation as inverse of idom
|
// Calculate children relation as inverse of idom
|
||||||
if (w->dom.idom->dom.children.e == NULL) {
|
if (w->dom.idom->dom.children.data == nullptr) {
|
||||||
// TODO(bill): Is this good enough for memory allocations?
|
// TODO(bill): Is this good enough for memory allocations?
|
||||||
array_init(&w->dom.idom->dom.children, heap_allocator());
|
array_init(&w->dom.idom->dom.children, heap_allocator());
|
||||||
}
|
}
|
||||||
@@ -448,32 +449,32 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssa_opt_number_dom_tree(root, 0, 0);
|
ir_opt_number_dom_tree(root, 0, 0);
|
||||||
|
|
||||||
gb_temp_arena_memory_end(tmp);
|
gb_temp_arena_memory_end(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ssa_opt_mem2reg(ssaProcedure *proc) {
|
void ir_opt_mem2reg(irProcedure *proc) {
|
||||||
// TODO(bill): ssa_opt_mem2reg
|
// TODO(bill): ir_opt_mem2reg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ssa_opt_tree(ssaGen *s) {
|
void ir_opt_tree(irGen *s) {
|
||||||
s->opt_called = true;
|
s->opt_called = true;
|
||||||
|
|
||||||
for_array(member_index, s->module.procs) {
|
for_array(member_index, s->module.procs) {
|
||||||
ssaProcedure *proc = s->module.procs.e[member_index];
|
irProcedure *proc = s->module.procs[member_index];
|
||||||
if (proc->blocks.count == 0) { // Prototype/external procedure
|
if (proc->blocks.count == 0) { // Prototype/external procedure
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssa_opt_blocks(proc);
|
ir_opt_blocks(proc);
|
||||||
#if 1
|
#if 0
|
||||||
ssa_opt_build_referrers(proc);
|
ir_opt_build_referrers(proc);
|
||||||
ssa_opt_build_dom_tree(proc);
|
ir_opt_build_dom_tree(proc);
|
||||||
|
|
||||||
// TODO(bill): ssa optimization
|
// TODO(bill): ir optimization
|
||||||
// [ ] cse (common-subexpression) elim
|
// [ ] cse (common-subexpression) elim
|
||||||
// [ ] copy elim
|
// [ ] copy elim
|
||||||
// [ ] dead code elim
|
// [ ] dead code elim
|
||||||
@@ -484,10 +485,10 @@ void ssa_opt_tree(ssaGen *s) {
|
|||||||
// [ ] lift/mem2reg
|
// [ ] lift/mem2reg
|
||||||
// [ ] lift/mem2reg
|
// [ ] lift/mem2reg
|
||||||
|
|
||||||
ssa_opt_mem2reg(proc);
|
ir_opt_mem2reg(proc);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GB_ASSERT(proc->blocks.count > 0);
|
GB_ASSERT(proc->blocks.count > 0);
|
||||||
ssa_number_proc_registers(proc);
|
ir_number_proc_registers(proc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1957
File diff suppressed because it is too large
Load Diff
-272
@@ -1,272 +0,0 @@
|
|||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
#define VERSION_STRING "v0.0.3d"
|
|
||||||
|
|
||||||
#include "common.c"
|
|
||||||
#include "timings.c"
|
|
||||||
#include "unicode.c"
|
|
||||||
#include "tokenizer.c"
|
|
||||||
#include "parser.c"
|
|
||||||
// #include "printer.c"
|
|
||||||
#include "checker/checker.c"
|
|
||||||
#include "ssa.c"
|
|
||||||
#include "ssa_opt.c"
|
|
||||||
#include "ssa_print.c"
|
|
||||||
// #include "vm.c"
|
|
||||||
|
|
||||||
// NOTE(bill): `name` is used in debugging and profiling modes
|
|
||||||
i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
|
|
||||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
|
||||||
PROCESS_INFORMATION pi = {0};
|
|
||||||
char cmd_line[4096] = {0};
|
|
||||||
isize cmd_len;
|
|
||||||
va_list va;
|
|
||||||
gbTempArenaMemory tmp;
|
|
||||||
String16 cmd;
|
|
||||||
i32 exit_code = 0;
|
|
||||||
|
|
||||||
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
|
||||||
start_info.wShowWindow = SW_SHOW;
|
|
||||||
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
||||||
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
|
|
||||||
va_start(va, fmt);
|
|
||||||
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
|
||||||
va_end(va);
|
|
||||||
// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
|
|
||||||
|
|
||||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
|
||||||
|
|
||||||
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
|
|
||||||
|
|
||||||
if (CreateProcessW(NULL, cmd.text,
|
|
||||||
NULL, NULL, true, 0, NULL, NULL,
|
|
||||||
&start_info, &pi)) {
|
|
||||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
||||||
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
|
||||||
|
|
||||||
CloseHandle(pi.hProcess);
|
|
||||||
CloseHandle(pi.hThread);
|
|
||||||
} else {
|
|
||||||
// NOTE(bill): failed to create process
|
|
||||||
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
|
||||||
exit_code = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_temp_arena_memory_end(tmp);
|
|
||||||
return exit_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum ArchKind {
|
|
||||||
ArchKind_x64,
|
|
||||||
ArchKind_x86,
|
|
||||||
} ArchKind;
|
|
||||||
|
|
||||||
typedef struct ArchData {
|
|
||||||
BaseTypeSizes sizes;
|
|
||||||
String llc_flags;
|
|
||||||
String link_flags;
|
|
||||||
} ArchData;
|
|
||||||
|
|
||||||
ArchData make_arch_data(ArchKind kind) {
|
|
||||||
ArchData data = {0};
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case ArchKind_x64:
|
|
||||||
default:
|
|
||||||
data.sizes.word_size = 8;
|
|
||||||
data.sizes.max_align = 16;
|
|
||||||
data.llc_flags = str_lit("-march=x86-64 ");
|
|
||||||
data.link_flags = str_lit("/machine:x64 ");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ArchKind_x86:
|
|
||||||
data.sizes.word_size = 4;
|
|
||||||
data.sizes.max_align = 8;
|
|
||||||
data.llc_flags = str_lit("-march=x86 ");
|
|
||||||
data.link_flags = str_lit("/machine:x86 ");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(char *argv0) {
|
|
||||||
gb_printf_err("%s is a tool for managing Odin source code\n", argv0);
|
|
||||||
gb_printf_err("Usage:");
|
|
||||||
gb_printf_err("\n\t%s command [arguments]\n", argv0);
|
|
||||||
gb_printf_err("Commands:");
|
|
||||||
gb_printf_err("\n\tbuild compile .odin file");
|
|
||||||
gb_printf_err("\n\trun compile and run .odin file");
|
|
||||||
gb_printf_err("\n\tversion print Odin version");
|
|
||||||
gb_printf_err("\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
if (argc < 2) {
|
|
||||||
usage(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timings timings = {0};
|
|
||||||
timings_init(&timings, str_lit("Total Time"), 128);
|
|
||||||
// defer (timings_destroy(&timings));
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
init_string_buffer_memory();
|
|
||||||
init_global_error_collector();
|
|
||||||
|
|
||||||
String module_dir = get_module_dir();
|
|
||||||
|
|
||||||
init_universal_scope();
|
|
||||||
|
|
||||||
char *init_filename = NULL;
|
|
||||||
bool run_output = false;
|
|
||||||
String arg1 = make_string_c(argv[1]);
|
|
||||||
if (str_eq(arg1, str_lit("run"))) {
|
|
||||||
run_output = true;
|
|
||||||
init_filename = argv[2];
|
|
||||||
} else if (str_eq(arg1, str_lit("build"))) {
|
|
||||||
init_filename = argv[2];
|
|
||||||
} else if (str_eq(arg1, str_lit("version"))) {
|
|
||||||
gb_printf("%s version %s", argv[0], VERSION_STRING);
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
usage(argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): prevent compiling without a linker
|
|
||||||
|
|
||||||
timings_start_section(&timings, str_lit("parse files"));
|
|
||||||
|
|
||||||
Parser parser = {0};
|
|
||||||
if (!init_parser(&parser)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// defer (destroy_parser(&parser));
|
|
||||||
|
|
||||||
if (parse_files(&parser, init_filename) != ParseFile_None) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
timings_start_section(&timings, str_lit("type check"));
|
|
||||||
|
|
||||||
Checker checker = {0};
|
|
||||||
ArchData arch_data = make_arch_data(ArchKind_x64);
|
|
||||||
|
|
||||||
init_checker(&checker, &parser, arch_data.sizes);
|
|
||||||
// defer (destroy_checker(&checker));
|
|
||||||
|
|
||||||
check_parsed_files(&checker);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#if 1
|
|
||||||
|
|
||||||
ssaGen ssa = {0};
|
|
||||||
if (!ssa_gen_init(&ssa, &checker)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
// defer (ssa_gen_destroy(&ssa));
|
|
||||||
|
|
||||||
timings_start_section(&timings, str_lit("ssa gen"));
|
|
||||||
ssa_gen_tree(&ssa);
|
|
||||||
|
|
||||||
timings_start_section(&timings, str_lit("ssa opt"));
|
|
||||||
ssa_opt_tree(&ssa);
|
|
||||||
|
|
||||||
timings_start_section(&timings, str_lit("ssa print"));
|
|
||||||
ssa_print_llvm_ir(&ssa);
|
|
||||||
|
|
||||||
// prof_print_all();
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
timings_start_section(&timings, str_lit("llvm-opt"));
|
|
||||||
|
|
||||||
char const *output_name = ssa.output_file.filename;
|
|
||||||
isize base_name_len = gb_path_extension(output_name)-1 - output_name;
|
|
||||||
String output = make_string(cast(u8 *)output_name, base_name_len);
|
|
||||||
|
|
||||||
i32 optimization_level = 0;
|
|
||||||
optimization_level = gb_clamp(optimization_level, 0, 3);
|
|
||||||
|
|
||||||
i32 exit_code = 0;
|
|
||||||
// For more passes arguments: http://llvm.org/docs/Passes.html
|
|
||||||
exit_code = win32_exec_command_line_app("llvm-opt",
|
|
||||||
"%.*sbin/opt %s -o %.*s.bc "
|
|
||||||
"-mem2reg "
|
|
||||||
"-memcpyopt "
|
|
||||||
"-die "
|
|
||||||
// "-dse "
|
|
||||||
// "-dce "
|
|
||||||
// "-S "
|
|
||||||
"",
|
|
||||||
LIT(module_dir),
|
|
||||||
output_name, LIT(output));
|
|
||||||
if (exit_code != 0) {
|
|
||||||
return exit_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
|
||||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
|
||||||
exit_code = win32_exec_command_line_app("llvm-llc",
|
|
||||||
"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
|
|
||||||
"%.*s "
|
|
||||||
// "-debug-pass=Arguments "
|
|
||||||
"",
|
|
||||||
LIT(module_dir),
|
|
||||||
LIT(output),
|
|
||||||
optimization_level,
|
|
||||||
LIT(arch_data.llc_flags));
|
|
||||||
if (exit_code != 0) {
|
|
||||||
return exit_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
timings_start_section(&timings, str_lit("msvc-link"));
|
|
||||||
|
|
||||||
gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
|
|
||||||
// defer (gb_string_free(lib_str));
|
|
||||||
char lib_str_buf[1024] = {0};
|
|
||||||
for_array(i, parser.foreign_libraries) {
|
|
||||||
String lib = parser.foreign_libraries.e[i];
|
|
||||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
|
||||||
" %.*s.lib", LIT(lib));
|
|
||||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_code = win32_exec_command_line_app("msvc-link",
|
|
||||||
"link %.*s.obj -OUT:%.*s.exe %s "
|
|
||||||
"/defaultlib:libcmt "
|
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:console "
|
|
||||||
" %.*s "
|
|
||||||
"",
|
|
||||||
LIT(output), LIT(output),
|
|
||||||
lib_str, LIT(arch_data.link_flags));
|
|
||||||
if (exit_code != 0) {
|
|
||||||
return exit_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// timings_print_all(&timings);
|
|
||||||
|
|
||||||
if (run_output) {
|
|
||||||
win32_exec_command_line_app("odin run",
|
|
||||||
"%.*s.exe", cast(int)base_name_len, output_name);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
+796
@@ -0,0 +1,796 @@
|
|||||||
|
#define USE_CUSTOM_BACKEND 0
|
||||||
|
// #define NO_ARRAY_BOUNDS_CHECK
|
||||||
|
#if !defined(USE_THREADED_PARSER)
|
||||||
|
#define USE_THREADED_PARSER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "common.cpp"
|
||||||
|
#include "timings.cpp"
|
||||||
|
#include "build_settings.cpp"
|
||||||
|
#include "tokenizer.cpp"
|
||||||
|
#include "parser.cpp"
|
||||||
|
#include "docs.cpp"
|
||||||
|
#include "checker.cpp"
|
||||||
|
#include "ssa.cpp"
|
||||||
|
#include "ir.cpp"
|
||||||
|
#include "ir_opt.cpp"
|
||||||
|
#include "ir_print.cpp"
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
// NOTE(bill): `name` is used in debugging and profiling modes
|
||||||
|
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||||
|
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||||
|
PROCESS_INFORMATION pi = {0};
|
||||||
|
char cmd_line[4096] = {0};
|
||||||
|
isize cmd_len;
|
||||||
|
va_list va;
|
||||||
|
gbTempArenaMemory tmp;
|
||||||
|
String16 cmd;
|
||||||
|
i32 exit_code = 0;
|
||||||
|
|
||||||
|
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||||
|
start_info.wShowWindow = SW_SHOW;
|
||||||
|
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
|
||||||
|
va_start(va, fmt);
|
||||||
|
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
|
||||||
|
// gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
|
||||||
|
|
||||||
|
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||||
|
|
||||||
|
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||||
|
|
||||||
|
if (CreateProcessW(nullptr, cmd.text,
|
||||||
|
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||||
|
&start_info, &pi)) {
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
||||||
|
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
} else {
|
||||||
|
// NOTE(bill): failed to create process
|
||||||
|
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
||||||
|
exit_code = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_temp_arena_memory_end(tmp);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||||
|
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||||
|
|
||||||
|
char cmd_line[4096] = {0};
|
||||||
|
isize cmd_len;
|
||||||
|
va_list va;
|
||||||
|
String cmd;
|
||||||
|
i32 exit_code = 0;
|
||||||
|
|
||||||
|
va_start(va, fmt);
|
||||||
|
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
|
||||||
|
|
||||||
|
exit_code = system(&cmd_line[0]);
|
||||||
|
|
||||||
|
// pid_t pid = fork();
|
||||||
|
// int status = 0;
|
||||||
|
|
||||||
|
// if(pid == 0) {
|
||||||
|
// // in child, pid == 0.
|
||||||
|
// int ret = execvp(cmd.text, (char* const*) cmd.text);
|
||||||
|
|
||||||
|
// if(ret == -1) {
|
||||||
|
// gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
||||||
|
|
||||||
|
// // we're in the child, so returning won't do us any good -- just quit.
|
||||||
|
// exit(-1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // unreachable
|
||||||
|
// abort();
|
||||||
|
// } else {
|
||||||
|
// // wait for child to finish, then we can continue cleanup
|
||||||
|
|
||||||
|
// int s = 0;
|
||||||
|
// waitpid(pid, &s, 0);
|
||||||
|
|
||||||
|
// status = WEXITSTATUS(s);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// exit_code = status;
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Array<String> setup_args(int argc, char **argv) {
|
||||||
|
Array<String> args = {};
|
||||||
|
gbAllocator a = heap_allocator();
|
||||||
|
int i;
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
int wargc = 0;
|
||||||
|
wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
|
||||||
|
array_init(&args, a, wargc);
|
||||||
|
for (i = 0; i < wargc; i++) {
|
||||||
|
wchar_t *warg = wargv[i];
|
||||||
|
isize wlen = string16_len(warg);
|
||||||
|
String16 wstr = make_string16(warg, wlen);
|
||||||
|
String arg = string16_to_string(a, wstr);
|
||||||
|
if (arg.len > 0) {
|
||||||
|
array_add(&args, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
array_init(&args, a, argc);
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
String arg = make_string_c(argv[i]);
|
||||||
|
if (arg.len > 0) {
|
||||||
|
array_add(&args, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void print_usage_line(i32 indent, char *fmt, ...) {
|
||||||
|
while (indent --> 0) {
|
||||||
|
gb_printf_err("\t");
|
||||||
|
}
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
gb_printf_err_va(fmt, va);
|
||||||
|
va_end(va);
|
||||||
|
gb_printf_err("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void usage(String argv0) {
|
||||||
|
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
|
||||||
|
print_usage_line(0, "Usage:");
|
||||||
|
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
|
||||||
|
print_usage_line(0, "Commands:");
|
||||||
|
print_usage_line(1, "build compile .odin file as executable");
|
||||||
|
print_usage_line(1, "build_dll compile .odin file as dll");
|
||||||
|
print_usage_line(1, "run compile and run .odin file");
|
||||||
|
print_usage_line(1, "docs generate documentation for a .odin file");
|
||||||
|
print_usage_line(1, "version print version");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum BuildFlagKind {
|
||||||
|
BuildFlag_Invalid,
|
||||||
|
|
||||||
|
BuildFlag_OptimizationLevel,
|
||||||
|
BuildFlag_ShowTimings,
|
||||||
|
BuildFlag_ThreadCount,
|
||||||
|
BuildFlag_KeepTempFiles,
|
||||||
|
|
||||||
|
BuildFlag_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BuildFlagParamKind {
|
||||||
|
BuildFlagParam_None,
|
||||||
|
|
||||||
|
BuildFlagParam_Boolean,
|
||||||
|
BuildFlagParam_Integer,
|
||||||
|
BuildFlagParam_Float,
|
||||||
|
BuildFlagParam_String,
|
||||||
|
|
||||||
|
BuildFlagParam_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuildFlag {
|
||||||
|
BuildFlagKind kind;
|
||||||
|
String name;
|
||||||
|
BuildFlagParamKind param_kind;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
|
||||||
|
BuildFlag flag = {kind, name, param_kind};
|
||||||
|
array_add(build_flags, flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_build_flags(Array<String> args) {
|
||||||
|
Array<BuildFlag> build_flags = {};
|
||||||
|
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
|
||||||
|
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
|
||||||
|
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
|
||||||
|
add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
|
||||||
|
add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
|
||||||
|
|
||||||
|
|
||||||
|
Array<String> flag_args = args;
|
||||||
|
flag_args.data += 3;
|
||||||
|
flag_args.count -= 3;
|
||||||
|
|
||||||
|
bool set_flags[BuildFlag_COUNT] = {};
|
||||||
|
|
||||||
|
bool bad_flags = false;
|
||||||
|
for_array(i, flag_args) {
|
||||||
|
String flag = flag_args[i];
|
||||||
|
if (flag[0] != '-') {
|
||||||
|
gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String name = substring(flag, 1, flag.len);
|
||||||
|
isize end = 0;
|
||||||
|
for (; end < name.len; end++) {
|
||||||
|
if (name[end] == '=') break;
|
||||||
|
}
|
||||||
|
name = substring(name, 0, end);
|
||||||
|
String param = {};
|
||||||
|
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
for_array(build_flag_index, build_flags) {
|
||||||
|
BuildFlag bf = build_flags[build_flag_index];
|
||||||
|
if (bf.name == name) {
|
||||||
|
found = true;
|
||||||
|
if (set_flags[bf.kind]) {
|
||||||
|
gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
|
||||||
|
bad_flags = true;
|
||||||
|
} else {
|
||||||
|
ExactValue value = {};
|
||||||
|
bool ok = false;
|
||||||
|
if (bf.param_kind == BuildFlagParam_None) {
|
||||||
|
if (param.len == 0) {
|
||||||
|
ok = true;
|
||||||
|
} else {
|
||||||
|
gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
}
|
||||||
|
} else if (param.len == 0) {
|
||||||
|
gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
|
||||||
|
bad_flags = true;
|
||||||
|
} else {
|
||||||
|
ok = true;
|
||||||
|
switch (bf.param_kind) {
|
||||||
|
default: ok = false; break;
|
||||||
|
case BuildFlagParam_Boolean: {
|
||||||
|
if (param == "t") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "T") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "true") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "TRUE") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "1") {
|
||||||
|
value = exact_value_bool(true);
|
||||||
|
} else if (param == "f") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "F") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "false") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "FALSE") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else if (param == "0") {
|
||||||
|
value = exact_value_bool(false);
|
||||||
|
} else {
|
||||||
|
gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case BuildFlagParam_Integer:
|
||||||
|
value = exact_value_integer_from_string(param);
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_Float:
|
||||||
|
value = exact_value_float_from_string(param);
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_String:
|
||||||
|
value = exact_value_string(param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
switch (bf.param_kind) {
|
||||||
|
case BuildFlagParam_None:
|
||||||
|
if (value.kind != ExactValue_Invalid) {
|
||||||
|
gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_Boolean:
|
||||||
|
if (value.kind != ExactValue_Bool) {
|
||||||
|
gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_Integer:
|
||||||
|
if (value.kind != ExactValue_Integer) {
|
||||||
|
gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_Float:
|
||||||
|
if (value.kind != ExactValue_Float) {
|
||||||
|
gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BuildFlagParam_String:
|
||||||
|
if (value.kind != ExactValue_String) {
|
||||||
|
gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
|
||||||
|
bad_flags = true;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok) switch (bf.kind) {
|
||||||
|
case BuildFlag_OptimizationLevel:
|
||||||
|
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||||
|
build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
|
||||||
|
break;
|
||||||
|
case BuildFlag_ShowTimings:
|
||||||
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
|
build_context.show_timings = true;
|
||||||
|
break;
|
||||||
|
case BuildFlag_ThreadCount: {
|
||||||
|
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||||
|
isize count = cast(isize)i128_to_i64(value.value_integer);
|
||||||
|
if (count <= 0) {
|
||||||
|
gb_printf_err("%.*s expected a positive non-zero number, got %.*s", LIT(name), LIT(param));
|
||||||
|
build_context.thread_count = 0;
|
||||||
|
} else {
|
||||||
|
build_context.thread_count = count;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case BuildFlag_KeepTempFiles:
|
||||||
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
|
build_context.keep_temp_files = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set_flags[bf.kind] = ok;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
|
||||||
|
bad_flags = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !bad_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void show_timings(Checker *c, Timings *t) {
|
||||||
|
Parser *p = c->parser;
|
||||||
|
isize lines = p->total_line_count;
|
||||||
|
isize tokens = p->total_token_count;
|
||||||
|
isize files = p->files.count;
|
||||||
|
{
|
||||||
|
timings_print_all(t);
|
||||||
|
gb_printf("\n");
|
||||||
|
gb_printf("Total Lines - %td\n", lines);
|
||||||
|
gb_printf("Total Tokens - %td\n", tokens);
|
||||||
|
gb_printf("Total Files - %td\n", files);
|
||||||
|
gb_printf("\n");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TimeStamp ts = t->sections[0];
|
||||||
|
GB_ASSERT(ts.label == "parse files");
|
||||||
|
f64 parse_time = time_stamp_as_second(ts, t->freq);
|
||||||
|
gb_printf("Parse pass\n");
|
||||||
|
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
|
||||||
|
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
|
||||||
|
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
|
||||||
|
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
|
||||||
|
gb_printf("\n");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
f64 total_time = t->total_time_seconds;
|
||||||
|
gb_printf("Total pass\n");
|
||||||
|
gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
|
||||||
|
gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
|
||||||
|
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
|
||||||
|
gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
|
||||||
|
gb_printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_temp_files(String output_base) {
|
||||||
|
if (build_context.keep_temp_files) return;
|
||||||
|
|
||||||
|
Array<u8> data = {};
|
||||||
|
array_init_count(&data, heap_allocator(), output_base.len + 10);
|
||||||
|
defer (array_free(&data));
|
||||||
|
|
||||||
|
isize n = output_base.len;
|
||||||
|
gb_memcopy(data.data, output_base.text, n);
|
||||||
|
#define EXT_REMOVE(s) do { \
|
||||||
|
gb_memcopy(data.data+n, s, gb_size_of(s)); \
|
||||||
|
gb_file_remove(cast(char *)data.data); \
|
||||||
|
} while (0)
|
||||||
|
EXT_REMOVE(".ll");
|
||||||
|
EXT_REMOVE(".bc");
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
EXT_REMOVE(".obj");
|
||||||
|
#else
|
||||||
|
EXT_REMOVE(".o");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef EXT_REMOVE
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int arg_count, char **arg_ptr) {
|
||||||
|
if (arg_count < 2) {
|
||||||
|
usage(make_string_c(arg_ptr[0]));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timings timings = {0};
|
||||||
|
timings_init(&timings, str_lit("Total Time"), 128);
|
||||||
|
defer (timings_destroy(&timings));
|
||||||
|
init_string_buffer_memory();
|
||||||
|
init_scratch_memory(gb_megabytes(10));
|
||||||
|
init_global_error_collector();
|
||||||
|
|
||||||
|
Array<String> args = setup_args(arg_count, arg_ptr);
|
||||||
|
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
|
||||||
|
String init_filename = {};
|
||||||
|
bool run_output = false;
|
||||||
|
if (args[1] == "run") {
|
||||||
|
if (args.count < 3) {
|
||||||
|
usage(args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
init_filename = args[2];
|
||||||
|
run_output = true;
|
||||||
|
} else if (args[1] == "build_dll") {
|
||||||
|
if (args.count < 3) {
|
||||||
|
usage(args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
init_filename = args[2];
|
||||||
|
build_context.is_dll = true;
|
||||||
|
} else if (args[1] == "build") {
|
||||||
|
if (args.count < 3) {
|
||||||
|
usage(args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
init_filename = args[2];
|
||||||
|
} else if (args[1] == "docs") {
|
||||||
|
if (args.count < 3) {
|
||||||
|
usage(args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_filename = args[2];
|
||||||
|
build_context.generate_docs = true;
|
||||||
|
#if 1
|
||||||
|
print_usage_line(0, "Documentation generation is not yet supported");
|
||||||
|
return 1;
|
||||||
|
#endif
|
||||||
|
} else if (args[1] == "version") {
|
||||||
|
gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
usage(args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parse_build_flags(args)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init_build_context();
|
||||||
|
if (build_context.word_size == 4) {
|
||||||
|
print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
init_universal_scope();
|
||||||
|
|
||||||
|
// TODO(bill): prevent compiling without a linker
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("parse files"));
|
||||||
|
|
||||||
|
Parser parser = {0};
|
||||||
|
if (!init_parser(&parser)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
defer (destroy_parser(&parser));
|
||||||
|
|
||||||
|
if (parse_files(&parser, init_filename) != ParseFile_None) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.generate_docs) {
|
||||||
|
generate_documentation(&parser);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
timings_start_section(&timings, str_lit("type check"));
|
||||||
|
|
||||||
|
Checker checker = {0};
|
||||||
|
|
||||||
|
init_checker(&checker, &parser);
|
||||||
|
defer (destroy_checker(&checker));
|
||||||
|
|
||||||
|
check_parsed_files(&checker);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#if defined(USE_CUSTOM_BACKEND) && USE_CUSTOM_BACKEND
|
||||||
|
if (global_error_collector.count != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checker.parser->total_token_count < 2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ssa_generate(&parser, &checker.info)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
irGen ir_gen = {0};
|
||||||
|
if (!ir_gen_init(&ir_gen, &checker)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
defer (ir_gen_destroy(&ir_gen));
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("llvm ir gen"));
|
||||||
|
ir_gen_tree(&ir_gen);
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("llvm ir opt tree"));
|
||||||
|
ir_opt_tree(&ir_gen);
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("llvm ir print"));
|
||||||
|
print_llvm_ir(&ir_gen);
|
||||||
|
|
||||||
|
// prof_print_all();
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
timings_start_section(&timings, str_lit("llvm-opt"));
|
||||||
|
|
||||||
|
String output_name = ir_gen.output_name;
|
||||||
|
String output_base = ir_gen.output_base;
|
||||||
|
int base_name_len = cast(int)output_base.len;
|
||||||
|
|
||||||
|
build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
|
||||||
|
|
||||||
|
i32 exit_code = 0;
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
// For more passes arguments: http://llvm.org/docs/Passes.html
|
||||||
|
exit_code = system_exec_command_line_app("llvm-opt", false,
|
||||||
|
"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc %.*s "
|
||||||
|
"-mem2reg "
|
||||||
|
"-memcpyopt "
|
||||||
|
"-die "
|
||||||
|
"",
|
||||||
|
LIT(build_context.ODIN_ROOT),
|
||||||
|
LIT(output_base), LIT(output_base),
|
||||||
|
LIT(build_context.opt_flags));
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
|
||||||
|
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
|
||||||
|
exit_code = system_exec_command_line_app("llvm-opt", false,
|
||||||
|
"opt \"%.*s\".ll -o \"%.*s\".bc %.*s "
|
||||||
|
"-mem2reg "
|
||||||
|
"-memcpyopt "
|
||||||
|
"-die "
|
||||||
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||||
|
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||||
|
// make sure to also change the `macosx_version_min` param passed to `llc`
|
||||||
|
"-mtriple=x86_64-apple-macosx10.8 "
|
||||||
|
#endif
|
||||||
|
"",
|
||||||
|
LIT(output_base), LIT(output_base),
|
||||||
|
LIT(build_context.opt_flags));
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||||
|
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||||
|
exit_code = system_exec_command_line_app("llvm-llc", false,
|
||||||
|
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
|
||||||
|
"%.*s "
|
||||||
|
// "-debug-pass=Arguments "
|
||||||
|
"",
|
||||||
|
LIT(build_context.ODIN_ROOT),
|
||||||
|
LIT(output_base),
|
||||||
|
build_context.optimization_level,
|
||||||
|
LIT(build_context.llc_flags));
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("msvc-link"));
|
||||||
|
|
||||||
|
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||||
|
defer (gb_string_free(lib_str));
|
||||||
|
char lib_str_buf[1024] = {0};
|
||||||
|
for_array(i, ir_gen.module.foreign_library_paths) {
|
||||||
|
String lib = ir_gen.module.foreign_library_paths[i];
|
||||||
|
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
|
||||||
|
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||||
|
" \"%.*s\"", LIT(lib));
|
||||||
|
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *output_ext = "exe";
|
||||||
|
char *link_settings = "";
|
||||||
|
if (build_context.is_dll) {
|
||||||
|
output_ext = "dll";
|
||||||
|
link_settings = "/DLL";
|
||||||
|
} else {
|
||||||
|
link_settings = "/ENTRY:mainCRTStartup";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||||
|
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
|
||||||
|
"/defaultlib:libcmt "
|
||||||
|
// "/nodefaultlib "
|
||||||
|
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||||
|
" %.*s "
|
||||||
|
" %s "
|
||||||
|
"",
|
||||||
|
LIT(output_base), LIT(output_base), output_ext,
|
||||||
|
lib_str, LIT(build_context.link_flags),
|
||||||
|
link_settings
|
||||||
|
);
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.show_timings) {
|
||||||
|
show_timings(&checker, &timings);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_temp_files(output_base);
|
||||||
|
|
||||||
|
if (run_output) {
|
||||||
|
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
|
||||||
|
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||||
|
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||||
|
exit_code = system_exec_command_line_app("llc", false,
|
||||||
|
"llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
|
||||||
|
"%.*s "
|
||||||
|
// "-debug-pass=Arguments "
|
||||||
|
"",
|
||||||
|
LIT(output_base),
|
||||||
|
build_context.optimization_level,
|
||||||
|
LIT(build_context.llc_flags));
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
timings_start_section(&timings, str_lit("ld-link"));
|
||||||
|
|
||||||
|
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||||
|
defer (gb_string_free(lib_str));
|
||||||
|
char lib_str_buf[1024] = {0};
|
||||||
|
for_array(i, ir_gen.module.foreign_library_paths) {
|
||||||
|
String lib = ir_gen.module.foreign_library_paths[i];
|
||||||
|
|
||||||
|
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
||||||
|
// This allows you to specify '-f' in a #foreign_system_library,
|
||||||
|
// without having to implement any new syntax specifically for MacOS.
|
||||||
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
isize len;
|
||||||
|
if(lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
|
||||||
|
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||||
|
" -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
|
||||||
|
} else {
|
||||||
|
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||||
|
" -l%.*s ", LIT(lib));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||||
|
" -l%.*s ", LIT(lib));
|
||||||
|
#endif
|
||||||
|
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlike the Win32 linker code, the output_ext includes the dot, because
|
||||||
|
// typically executable files on *NIX systems don't have extensions.
|
||||||
|
char *output_ext = "";
|
||||||
|
char *link_settings = "";
|
||||||
|
char *linker;
|
||||||
|
if (build_context.is_dll) {
|
||||||
|
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||||
|
// TODO(zangent): Is that statement entirely truthful?
|
||||||
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
output_ext = ".dylib";
|
||||||
|
#else
|
||||||
|
output_ext = ".so";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
link_settings = "-shared";
|
||||||
|
} else {
|
||||||
|
// TODO: Do I need anything here?
|
||||||
|
link_settings = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
linker = "ld";
|
||||||
|
#else
|
||||||
|
// TODO(zangent): Figure out how to make ld work on Linux.
|
||||||
|
// It probably has to do with including the entire CRT, but
|
||||||
|
// that's quite a complicated issue to solve while remaining distro-agnostic.
|
||||||
|
// Clang can figure out linker flags for us, and that's good enough _for now_.
|
||||||
|
linker = "clang -Wno-unused-command-line-argument";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
exit_code = system_exec_command_line_app("ld-link", true,
|
||||||
|
"%s \"%.*s\".o -o \"%.*s%s\" %s "
|
||||||
|
"-lc -lm "
|
||||||
|
" %.*s "
|
||||||
|
" %s "
|
||||||
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||||
|
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||||
|
// make sure to also change the `mtriple` param passed to `opt`
|
||||||
|
" -macosx_version_min 10.8.0 "
|
||||||
|
// This points the linker to where the entry point is
|
||||||
|
" -e _main "
|
||||||
|
#endif
|
||||||
|
, linker, LIT(output_base), LIT(output_base), output_ext,
|
||||||
|
lib_str, LIT(build_context.link_flags),
|
||||||
|
link_settings
|
||||||
|
);
|
||||||
|
if (exit_code != 0) {
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.show_timings) {
|
||||||
|
show_timings(&checker, &timings);
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_temp_files(output_base);
|
||||||
|
|
||||||
|
if (run_output) {
|
||||||
|
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
Example of usage:
|
|
||||||
|
|
||||||
#define MAP_TYPE String
|
|
||||||
#define MAP_PROC map_string_
|
|
||||||
#define MAP_NAME MapString
|
|
||||||
#include "map.c"
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MAP_UTIL_STUFF
|
|
||||||
#define MAP_UTIL_STUFF
|
|
||||||
// NOTE(bill): This util stuff is the same for every `Map`
|
|
||||||
typedef struct MapFindResult {
|
|
||||||
isize hash_index;
|
|
||||||
isize entry_prev;
|
|
||||||
isize entry_index;
|
|
||||||
} MapFindResult;
|
|
||||||
|
|
||||||
typedef enum HashKeyKind {
|
|
||||||
HashKey_Default,
|
|
||||||
HashKey_String,
|
|
||||||
HashKey_Pointer,
|
|
||||||
} HashKeyKind;
|
|
||||||
|
|
||||||
typedef struct HashKey {
|
|
||||||
HashKeyKind kind;
|
|
||||||
u64 key;
|
|
||||||
union {
|
|
||||||
String string; // if String, s.len > 0
|
|
||||||
void * ptr;
|
|
||||||
};
|
|
||||||
} HashKey;
|
|
||||||
|
|
||||||
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
|
||||||
HashKey h = {HashKey_Default};
|
|
||||||
h.kind = HashKey_Default;
|
|
||||||
// h.key = gb_murmur64(data, len);
|
|
||||||
h.key = gb_fnv64a(data, len);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline HashKey hash_string(String s) {
|
|
||||||
HashKey h = hashing_proc(s.text, s.len);
|
|
||||||
h.kind = HashKey_String;
|
|
||||||
h.string = s;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline HashKey hash_pointer(void *ptr) {
|
|
||||||
HashKey h = {HashKey_Default};
|
|
||||||
h.key = cast(u64)cast(uintptr)ptr;
|
|
||||||
h.ptr = ptr;
|
|
||||||
h.kind = HashKey_Default;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hash_key_equal(HashKey a, HashKey b) {
|
|
||||||
if (a.key == b.key) {
|
|
||||||
// NOTE(bill): If two string's hashes collide, compare the strings themselves
|
|
||||||
if (a.kind == HashKey_String) {
|
|
||||||
if (b.kind == HashKey_String) {
|
|
||||||
return str_eq(a.string, b.string);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define _J2_IND(a, b) a##b
|
|
||||||
#define _J2(a, b) _J2_IND(a, b)
|
|
||||||
|
|
||||||
/*
|
|
||||||
MAP_TYPE - Entry type
|
|
||||||
MAP_PROC - Function prefix (e.g. entity_map_)
|
|
||||||
MAP_NAME - Name of Map (e.g. EntityMap)
|
|
||||||
*/
|
|
||||||
#define MAP_ENTRY _J2(MAP_NAME,Entry)
|
|
||||||
|
|
||||||
typedef struct MAP_ENTRY {
|
|
||||||
HashKey key;
|
|
||||||
isize next;
|
|
||||||
MAP_TYPE value;
|
|
||||||
} MAP_ENTRY;
|
|
||||||
|
|
||||||
typedef struct MAP_NAME {
|
|
||||||
Array(isize) hashes;
|
|
||||||
Array(MAP_ENTRY) entries;
|
|
||||||
} MAP_NAME;
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,init) (MAP_NAME *h, gbAllocator a);
|
|
||||||
void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity);
|
|
||||||
void _J2(MAP_PROC,destroy) (MAP_NAME *h);
|
|
||||||
MAP_TYPE *_J2(MAP_PROC,get) (MAP_NAME *h, HashKey key);
|
|
||||||
void _J2(MAP_PROC,set) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
|
||||||
void _J2(MAP_PROC,remove) (MAP_NAME *h, HashKey key);
|
|
||||||
void _J2(MAP_PROC,clear) (MAP_NAME *h);
|
|
||||||
void _J2(MAP_PROC,grow) (MAP_NAME *h);
|
|
||||||
void _J2(MAP_PROC,rehash) (MAP_NAME *h, isize new_count);
|
|
||||||
|
|
||||||
// Mutlivalued map procedure
|
|
||||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key);
|
|
||||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_next) (MAP_NAME *h, MAP_ENTRY *e);
|
|
||||||
|
|
||||||
isize _J2(MAP_PROC,multi_count) (MAP_NAME *h, HashKey key);
|
|
||||||
void _J2(MAP_PROC,multi_get_all) (MAP_NAME *h, HashKey key, MAP_TYPE *items);
|
|
||||||
void _J2(MAP_PROC,multi_insert) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
|
||||||
void _J2(MAP_PROC,multi_remove) (MAP_NAME *h, HashKey key, MAP_ENTRY *e);
|
|
||||||
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gb_inline void _J2(MAP_PROC,init)(MAP_NAME *h, gbAllocator a) {
|
|
||||||
array_init(&h->hashes, a);
|
|
||||||
array_init(&h->entries, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity) {
|
|
||||||
array_init_reserve(&h->hashes, a, capacity);
|
|
||||||
array_init_reserve(&h->entries, a, capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline void _J2(MAP_PROC,destroy)(MAP_NAME *h) {
|
|
||||||
array_free(&h->entries);
|
|
||||||
array_free(&h->hashes);
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_internal isize _J2(MAP_PROC,_add_entry)(MAP_NAME *h, HashKey key) {
|
|
||||||
MAP_ENTRY e = {0};
|
|
||||||
e.key = key;
|
|
||||||
e.next = -1;
|
|
||||||
array_add(&h->entries, e);
|
|
||||||
return h->entries.count-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_internal MapFindResult _J2(MAP_PROC,_find)(MAP_NAME *h, HashKey key) {
|
|
||||||
MapFindResult fr = {-1, -1, -1};
|
|
||||||
if (h->hashes.count > 0) {
|
|
||||||
fr.hash_index = key.key % h->hashes.count;
|
|
||||||
fr.entry_index = h->hashes.e[fr.hash_index];
|
|
||||||
while (fr.entry_index >= 0) {
|
|
||||||
if (hash_key_equal(h->entries.e[fr.entry_index].key, key)) {
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
fr.entry_prev = fr.entry_index;
|
|
||||||
fr.entry_index = h->entries.e[fr.entry_index].next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_internal MapFindResult _J2(MAP_PROC,_find_from_entry)(MAP_NAME *h, MAP_ENTRY *e) {
|
|
||||||
MapFindResult fr = {-1, -1, -1};
|
|
||||||
if (h->hashes.count > 0) {
|
|
||||||
fr.hash_index = e->key.key % h->hashes.count;
|
|
||||||
fr.entry_index = h->hashes.e[fr.hash_index];
|
|
||||||
while (fr.entry_index >= 0) {
|
|
||||||
if (&h->entries.e[fr.entry_index] == e) {
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
fr.entry_prev = fr.entry_index;
|
|
||||||
fr.entry_index = h->entries.e[fr.entry_index].next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gb_internal b32 _J2(MAP_PROC,_full)(MAP_NAME *h) {
|
|
||||||
return 0.75f * h->hashes.count <= h->entries.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline void _J2(MAP_PROC,grow)(MAP_NAME *h) {
|
|
||||||
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
|
|
||||||
_J2(MAP_PROC,rehash)(h, new_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,rehash)(MAP_NAME *h, isize new_count) {
|
|
||||||
isize i, j;
|
|
||||||
MAP_NAME nh = {0};
|
|
||||||
_J2(MAP_PROC,init)(&nh, h->hashes.allocator);
|
|
||||||
array_resize(&nh.hashes, new_count);
|
|
||||||
array_reserve(&nh.entries, h->entries.count);
|
|
||||||
for (i = 0; i < new_count; i++) {
|
|
||||||
nh.hashes.e[i] = -1;
|
|
||||||
}
|
|
||||||
for (i = 0; i < h->entries.count; i++) {
|
|
||||||
MAP_ENTRY *e = &h->entries.e[i];
|
|
||||||
MapFindResult fr;
|
|
||||||
if (nh.hashes.count == 0) {
|
|
||||||
_J2(MAP_PROC,grow)(&nh);
|
|
||||||
}
|
|
||||||
fr = _J2(MAP_PROC,_find)(&nh, e->key);
|
|
||||||
j = _J2(MAP_PROC,_add_entry)(&nh, e->key);
|
|
||||||
if (fr.entry_prev < 0) {
|
|
||||||
nh.hashes.e[fr.hash_index] = j;
|
|
||||||
} else {
|
|
||||||
nh.entries.e[fr.entry_prev].next = j;
|
|
||||||
}
|
|
||||||
nh.entries.e[j].next = fr.entry_index;
|
|
||||||
nh.entries.e[j].value = e->value;
|
|
||||||
if (_J2(MAP_PROC,_full)(&nh)) {
|
|
||||||
_J2(MAP_PROC,grow)(&nh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_J2(MAP_PROC,destroy)(h);
|
|
||||||
*h = nh;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline MAP_TYPE *_J2(MAP_PROC,get)(MAP_NAME *h, HashKey key) {
|
|
||||||
isize index = _J2(MAP_PROC,_find)(h, key).entry_index;
|
|
||||||
if (index >= 0) {
|
|
||||||
return &h->entries.e[index].value;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
|
||||||
isize index;
|
|
||||||
MapFindResult fr;
|
|
||||||
if (h->hashes.count == 0)
|
|
||||||
_J2(MAP_PROC,grow)(h);
|
|
||||||
fr = _J2(MAP_PROC,_find)(h, key);
|
|
||||||
if (fr.entry_index >= 0) {
|
|
||||||
index = fr.entry_index;
|
|
||||||
} else {
|
|
||||||
index = _J2(MAP_PROC,_add_entry)(h, key);
|
|
||||||
if (fr.entry_prev >= 0) {
|
|
||||||
h->entries.e[fr.entry_prev].next = index;
|
|
||||||
} else {
|
|
||||||
h->hashes.e[fr.hash_index] = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
h->entries.e[index].value = value;
|
|
||||||
|
|
||||||
if (_J2(MAP_PROC,_full)(h)) {
|
|
||||||
_J2(MAP_PROC,grow)(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
|
|
||||||
MapFindResult last;
|
|
||||||
if (fr.entry_prev < 0) {
|
|
||||||
h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
|
|
||||||
} else {
|
|
||||||
h->entries.e[fr.entry_prev].next = h->entries.e[fr.entry_index].next;
|
|
||||||
}
|
|
||||||
if (fr.entry_index == h->entries.count-1) {
|
|
||||||
array_pop(&h->entries);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
|
|
||||||
last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
|
|
||||||
if (last.entry_prev >= 0) {
|
|
||||||
h->entries.e[last.entry_prev].next = fr.entry_index;
|
|
||||||
} else {
|
|
||||||
h->hashes.e[last.hash_index] = fr.entry_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,remove)(MAP_NAME *h, HashKey key) {
|
|
||||||
MapFindResult fr = _J2(MAP_PROC,_find)(h, key);
|
|
||||||
if (fr.entry_index >= 0) {
|
|
||||||
_J2(MAP_PROC,_erase)(h, fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline void _J2(MAP_PROC,clear)(MAP_NAME *h) {
|
|
||||||
array_clear(&h->hashes);
|
|
||||||
array_clear(&h->entries);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key) {
|
|
||||||
isize i = _J2(MAP_PROC,_find)(h, key).entry_index;
|
|
||||||
if (i < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return &h->entries.e[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_next)(MAP_NAME *h, MAP_ENTRY *e) {
|
|
||||||
isize i = e->next;
|
|
||||||
while (i >= 0) {
|
|
||||||
if (hash_key_equal(h->entries.e[i].key, e->key)) {
|
|
||||||
return &h->entries.e[i];
|
|
||||||
}
|
|
||||||
i = h->entries.e[i].next;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
isize _J2(MAP_PROC,multi_count)(MAP_NAME *h, HashKey key) {
|
|
||||||
isize count = 0;
|
|
||||||
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
|
|
||||||
while (e != NULL) {
|
|
||||||
count++;
|
|
||||||
e = _J2(MAP_PROC,multi_find_next)(h, e);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
|
|
||||||
isize i = 0;
|
|
||||||
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
|
|
||||||
while (e != NULL) {
|
|
||||||
items[i++] = e->value;
|
|
||||||
e = _J2(MAP_PROC,multi_find_next)(h, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
|
||||||
MapFindResult fr;
|
|
||||||
isize i;
|
|
||||||
if (h->hashes.count == 0) {
|
|
||||||
_J2(MAP_PROC,grow)(h);
|
|
||||||
}
|
|
||||||
fr = _J2(MAP_PROC,_find)(h, key);
|
|
||||||
i = _J2(MAP_PROC,_add_entry)(h, key);
|
|
||||||
if (fr.entry_prev < 0) {
|
|
||||||
h->hashes.e[fr.hash_index] = i;
|
|
||||||
} else {
|
|
||||||
h->entries.e[fr.entry_prev].next = i;
|
|
||||||
}
|
|
||||||
h->entries.e[i].next = fr.entry_index;
|
|
||||||
h->entries.e[i].value = value;
|
|
||||||
if (_J2(MAP_PROC,_full)(h)) {
|
|
||||||
_J2(MAP_PROC,grow)(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,multi_remove)(MAP_NAME *h, HashKey key, MAP_ENTRY *e) {
|
|
||||||
MapFindResult fr = _J2(MAP_PROC,_find_from_entry)(h, e);
|
|
||||||
if (fr.entry_index >= 0) {
|
|
||||||
_J2(MAP_PROC,_erase)(h, fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key) {
|
|
||||||
while (_J2(MAP_PROC,get)(h, key) != NULL) {
|
|
||||||
_J2(MAP_PROC,remove)(h, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#undef _J2
|
|
||||||
#undef MAP_TYPE
|
|
||||||
#undef MAP_PROC
|
|
||||||
#undef MAP_NAME
|
|
||||||
#undef MAP_ENTRY
|
|
||||||
+381
@@ -0,0 +1,381 @@
|
|||||||
|
// A `Map` is an unordered hash table which can allow for a key to point to multiple values
|
||||||
|
// with the use of the `multi_*` procedures.
|
||||||
|
// TODO(bill): I should probably allow the `multi_map_*` stuff to be #ifdefed out
|
||||||
|
|
||||||
|
#ifndef MAP_UTIL_STUFF
|
||||||
|
#define MAP_UTIL_STUFF
|
||||||
|
// NOTE(bill): This util stuff is the same for every `Map`
|
||||||
|
struct MapFindResult {
|
||||||
|
isize hash_index;
|
||||||
|
isize entry_prev;
|
||||||
|
isize entry_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HashKeyKind {
|
||||||
|
HashKey_Default,
|
||||||
|
HashKey_String,
|
||||||
|
HashKey_Ptr,
|
||||||
|
HashKey_PtrAndId,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PtrAndId {
|
||||||
|
void *ptr;
|
||||||
|
u32 id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashKey {
|
||||||
|
HashKeyKind kind;
|
||||||
|
// u128 key;
|
||||||
|
u64 key;
|
||||||
|
union {
|
||||||
|
String string; // if String, s.len > 0
|
||||||
|
void * ptr;
|
||||||
|
PtrAndId ptr_and_id;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
||||||
|
HashKey h = {HashKey_Default};
|
||||||
|
h.kind = HashKey_Default;
|
||||||
|
// h.key = u128_from_u64(gb_fnv64a(data, len));
|
||||||
|
h.key = gb_fnv64a(data, len);
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline HashKey hash_string(String s) {
|
||||||
|
HashKey h = hashing_proc(s.text, s.len);
|
||||||
|
h.kind = HashKey_String;
|
||||||
|
h.string = s;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline HashKey hash_pointer(void *ptr) {
|
||||||
|
HashKey h = {HashKey_Ptr};
|
||||||
|
h.key = cast(u64)cast(uintptr)ptr;
|
||||||
|
h.ptr = ptr;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
gb_inline HashKey hash_ptr_and_id(void *ptr, u32 id) {
|
||||||
|
HashKey h = {HashKey_PtrAndId};
|
||||||
|
h.key = cast(u64)cast(uintptr)ptr;
|
||||||
|
h.ptr_and_id.ptr = ptr;
|
||||||
|
h.ptr_and_id.id = id;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hash_key_equal(HashKey a, HashKey b) {
|
||||||
|
if (a.key == b.key) {
|
||||||
|
// NOTE(bill): If two string's hashes collide, compare the strings themselves
|
||||||
|
if (a.kind == HashKey_String) {
|
||||||
|
if (b.kind == HashKey_String) {
|
||||||
|
return a.string == b.string;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (a.kind == HashKey_PtrAndId) {
|
||||||
|
if (b.kind == HashKey_PtrAndId) {
|
||||||
|
return a.ptr_and_id.id == b.ptr_and_id.id;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool operator==(HashKey a, HashKey b) { return hash_key_equal(a, b); }
|
||||||
|
bool operator!=(HashKey a, HashKey b) { return !hash_key_equal(a, b); }
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct MapEntry {
|
||||||
|
HashKey key;
|
||||||
|
isize next;
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Map {
|
||||||
|
Array<isize> hashes;
|
||||||
|
Array<MapEntry<T> > entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> void map_init (Map<T> *h, gbAllocator a);
|
||||||
|
template <typename T> void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity);
|
||||||
|
template <typename T> void map_destroy (Map<T> *h);
|
||||||
|
template <typename T> T * map_get (Map<T> *h, HashKey key);
|
||||||
|
template <typename T> void map_set (Map<T> *h, HashKey key, T const &value);
|
||||||
|
template <typename T> void map_remove (Map<T> *h, HashKey key);
|
||||||
|
template <typename T> void map_clear (Map<T> *h);
|
||||||
|
template <typename T> void map_grow (Map<T> *h);
|
||||||
|
template <typename T> void map_rehash (Map<T> *h, isize new_count);
|
||||||
|
|
||||||
|
// Mutlivalued map procedure
|
||||||
|
template <typename T> MapEntry<T> * multi_map_find_first(Map<T> *h, HashKey key);
|
||||||
|
template <typename T> MapEntry<T> * multi_map_find_next (Map<T> *h, MapEntry<T> *e);
|
||||||
|
|
||||||
|
template <typename T> isize multi_map_count (Map<T> *h, HashKey key);
|
||||||
|
template <typename T> void multi_map_get_all (Map<T> *h, HashKey key, T *items);
|
||||||
|
template <typename T> void multi_map_insert (Map<T> *h, HashKey key, T const &value);
|
||||||
|
template <typename T> void multi_map_remove (Map<T> *h, HashKey key, MapEntry<T> *e);
|
||||||
|
template <typename T> void multi_map_remove_all(Map<T> *h, HashKey key);
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline void map_init(Map<T> *h, gbAllocator a) {
|
||||||
|
array_init(&h->hashes, a);
|
||||||
|
array_init(&h->entries, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity) {
|
||||||
|
array_init(&h->hashes, a, capacity);
|
||||||
|
array_init(&h->entries, a, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline void map_destroy(Map<T> *h) {
|
||||||
|
array_free(&h->entries);
|
||||||
|
array_free(&h->hashes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_internal isize map__add_entry(Map<T> *h, HashKey key) {
|
||||||
|
MapEntry<T> e = {};
|
||||||
|
e.key = key;
|
||||||
|
e.next = -1;
|
||||||
|
array_add(&h->entries, e);
|
||||||
|
return h->entries.count-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
|
||||||
|
MapFindResult fr = {-1, -1, -1};
|
||||||
|
if (h->hashes.count > 0) {
|
||||||
|
// fr.hash_index = u128_to_i64(key.key % u128_from_i64(h->hashes.count));
|
||||||
|
fr.hash_index = key.key % h->hashes.count;
|
||||||
|
fr.entry_index = h->hashes[fr.hash_index];
|
||||||
|
while (fr.entry_index >= 0) {
|
||||||
|
if (hash_key_equal(h->entries[fr.entry_index].key, key)) {
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
fr.entry_prev = fr.entry_index;
|
||||||
|
fr.entry_index = h->entries[fr.entry_index].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_internal MapFindResult map__find_from_entry(Map<T> *h, MapEntry<T> *e) {
|
||||||
|
MapFindResult fr = {-1, -1, -1};
|
||||||
|
if (h->hashes.count > 0) {
|
||||||
|
fr.hash_index = e->key.key % h->hashes.count;
|
||||||
|
fr.entry_index = h->hashes[fr.hash_index];
|
||||||
|
while (fr.entry_index >= 0) {
|
||||||
|
if (&h->entries[fr.entry_index] == e) {
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
fr.entry_prev = fr.entry_index;
|
||||||
|
fr.entry_index = h->entries[fr.entry_index].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_internal b32 map__full(Map<T> *h) {
|
||||||
|
return 0.75f * h->hashes.count <= h->entries.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline void map_grow(Map<T> *h) {
|
||||||
|
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
|
||||||
|
map_rehash(h, new_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void map_rehash(Map<T> *h, isize new_count) {
|
||||||
|
isize i, j;
|
||||||
|
Map<T> nh = {};
|
||||||
|
map_init(&nh, h->hashes.allocator);
|
||||||
|
array_resize(&nh.hashes, new_count);
|
||||||
|
array_reserve(&nh.entries, h->entries.count);
|
||||||
|
for (i = 0; i < new_count; i++) {
|
||||||
|
nh.hashes[i] = -1;
|
||||||
|
}
|
||||||
|
for (i = 0; i < h->entries.count; i++) {
|
||||||
|
MapEntry<T> *e = &h->entries[i];
|
||||||
|
MapFindResult fr;
|
||||||
|
if (nh.hashes.count == 0) {
|
||||||
|
map_grow(&nh);
|
||||||
|
}
|
||||||
|
fr = map__find(&nh, e->key);
|
||||||
|
j = map__add_entry(&nh, e->key);
|
||||||
|
if (fr.entry_prev < 0) {
|
||||||
|
nh.hashes[fr.hash_index] = j;
|
||||||
|
} else {
|
||||||
|
nh.entries[fr.entry_prev].next = j;
|
||||||
|
}
|
||||||
|
nh.entries[j].next = fr.entry_index;
|
||||||
|
nh.entries[j].value = e->value;
|
||||||
|
if (map__full(&nh)) {
|
||||||
|
map_grow(&nh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map_destroy(h);
|
||||||
|
*h = nh;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline T *map_get(Map<T> *h, HashKey key) {
|
||||||
|
isize index = map__find(h, key).entry_index;
|
||||||
|
if (index >= 0) {
|
||||||
|
return &h->entries[index].value;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void map_set(Map<T> *h, HashKey key, T const &value) {
|
||||||
|
isize index;
|
||||||
|
MapFindResult fr;
|
||||||
|
if (h->hashes.count == 0)
|
||||||
|
map_grow(h);
|
||||||
|
fr = map__find(h, key);
|
||||||
|
if (fr.entry_index >= 0) {
|
||||||
|
index = fr.entry_index;
|
||||||
|
} else {
|
||||||
|
index = map__add_entry(h, key);
|
||||||
|
if (fr.entry_prev >= 0) {
|
||||||
|
h->entries[fr.entry_prev].next = index;
|
||||||
|
} else {
|
||||||
|
h->hashes[fr.hash_index] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h->entries[index].value = value;
|
||||||
|
|
||||||
|
if (map__full(h)) {
|
||||||
|
map_grow(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void map__erase(Map<T> *h, MapFindResult fr) {
|
||||||
|
MapFindResult last;
|
||||||
|
if (fr.entry_prev < 0) {
|
||||||
|
h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
|
||||||
|
} else {
|
||||||
|
h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
|
||||||
|
}
|
||||||
|
if (fr.entry_index == h->entries.count-1) {
|
||||||
|
array_pop(&h->entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
h->entries[fr.entry_index] = h->entries[h->entries.count-1];
|
||||||
|
last = map__find(h, h->entries[fr.entry_index].key);
|
||||||
|
if (last.entry_prev >= 0) {
|
||||||
|
h->entries[last.entry_prev].next = fr.entry_index;
|
||||||
|
} else {
|
||||||
|
h->hashes[last.hash_index] = fr.entry_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void map_remove(Map<T> *h, HashKey key) {
|
||||||
|
MapFindResult fr = map__find(h, key);
|
||||||
|
if (fr.entry_index >= 0) {
|
||||||
|
map__erase(h, fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
gb_inline void map_clear(Map<T> *h) {
|
||||||
|
array_clear(&h->hashes);
|
||||||
|
array_clear(&h->entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
template <typename T>
|
||||||
|
MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
|
||||||
|
isize i = map__find(h, key).entry_index;
|
||||||
|
if (i < 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &h->entries[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
MapEntry<T> *multi_map_find_next(Map<T> *h, MapEntry<T> *e) {
|
||||||
|
isize i = e->next;
|
||||||
|
while (i >= 0) {
|
||||||
|
if (hash_key_equal(h->entries[i].key, e->key)) {
|
||||||
|
return &h->entries[i];
|
||||||
|
}
|
||||||
|
i = h->entries[i].next;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
isize multi_map_count(Map<T> *h, HashKey key) {
|
||||||
|
isize count = 0;
|
||||||
|
MapEntry<T> *e = multi_map_find_first(h, key);
|
||||||
|
while (e != nullptr) {
|
||||||
|
count++;
|
||||||
|
e = multi_map_find_next(h, e);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void multi_map_get_all(Map<T> *h, HashKey key, T *items) {
|
||||||
|
isize i = 0;
|
||||||
|
MapEntry<T> *e = multi_map_find_first(h, key);
|
||||||
|
while (e != nullptr) {
|
||||||
|
items[i++] = e->value;
|
||||||
|
e = multi_map_find_next(h, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void multi_map_insert(Map<T> *h, HashKey key, T const &value) {
|
||||||
|
MapFindResult fr;
|
||||||
|
isize i;
|
||||||
|
if (h->hashes.count == 0) {
|
||||||
|
map_grow(h);
|
||||||
|
}
|
||||||
|
// Make
|
||||||
|
fr = map__find(h, key);
|
||||||
|
i = map__add_entry(h, key);
|
||||||
|
if (fr.entry_prev < 0) {
|
||||||
|
h->hashes[fr.hash_index] = i;
|
||||||
|
} else {
|
||||||
|
h->entries[fr.entry_prev].next = i;
|
||||||
|
}
|
||||||
|
h->entries[i].next = fr.entry_index;
|
||||||
|
h->entries[i].value = value;
|
||||||
|
// Grow if needed
|
||||||
|
if (map__full(h)) {
|
||||||
|
map_grow(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void multi_map_remove(Map<T> *h, HashKey key, MapEntry<T> *e) {
|
||||||
|
MapFindResult fr = map__find_from_entry(h, e);
|
||||||
|
if (fr.entry_index >= 0) {
|
||||||
|
map__erase(h, fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void multi_map_remove_all(Map<T> *h, HashKey key) {
|
||||||
|
while (map_get(h, key) != nullptr) {
|
||||||
|
map_remove(h, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||||
|
// domain. The author hereby disclaims copyright to this source code.
|
||||||
|
|
||||||
|
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||||
|
// algorithms are optimized for their respective platforms. You can still
|
||||||
|
// compile and run any of them on any platform, but your performance with the
|
||||||
|
// non-native version will be less than optimal.
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define ROTL32(x,y) _rotl(x,y)
|
||||||
|
#define ROTL64(x,y) _rotl64(x,y)
|
||||||
|
#else
|
||||||
|
|
||||||
|
gb_inline u32 rotl32(u32 x, i8 r) {
|
||||||
|
return (x << r) | (x >> (32-r));
|
||||||
|
}
|
||||||
|
gb_inline u64 rotl64(u64 x, i8 r) {
|
||||||
|
return (x << r) | (x >> (64-r));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ROTL32(x,y) rotl32(x,y)
|
||||||
|
#define ROTL64(x,y) rotl64(x,y)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gb_inline u32 fmix32(u32 h) {
|
||||||
|
h ^= h >> 16;
|
||||||
|
h *= 0x85ebca6b;
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= 0xc2b2ae35;
|
||||||
|
h ^= h >> 16;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline u64 fmix64(u64 k) {
|
||||||
|
k ^= k >> 33;
|
||||||
|
k *= 0xff51afd7ed558ccdULL;
|
||||||
|
k ^= k >> 33;
|
||||||
|
k *= 0xc4ceb9fe1a85ec53ULL;
|
||||||
|
k ^= k >> 33;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline u32 mm3_getblock32(u32 const *p, isize i) {
|
||||||
|
return p[i];
|
||||||
|
}
|
||||||
|
gb_inline u64 mm3_getblock64(u64 const *p, isize i) {
|
||||||
|
return p[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void MurmurHash3_x64_128(void const *key, isize len, u32 seed, void *out) {
|
||||||
|
u8 const * data = cast(u8 const *)key;
|
||||||
|
isize nblocks = len / 16;
|
||||||
|
|
||||||
|
u64 h1 = seed;
|
||||||
|
u64 h2 = seed;
|
||||||
|
|
||||||
|
u64 const c1 = 0x87c37b91114253d5ULL;
|
||||||
|
u64 const c2 = 0x4cf5ad432745937fULL;
|
||||||
|
|
||||||
|
u64 const * blocks = cast(u64 const *)data;
|
||||||
|
|
||||||
|
for (isize i = 0; i < nblocks; i++) {
|
||||||
|
u64 k1 = mm3_getblock64(blocks, i*2 + 0);
|
||||||
|
u64 k2 = mm3_getblock64(blocks, i*2 + 1);
|
||||||
|
|
||||||
|
k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||||
|
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
||||||
|
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||||
|
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 const * tail = cast(u8 const *)(data + nblocks*16);
|
||||||
|
|
||||||
|
u64 k1 = 0;
|
||||||
|
u64 k2 = 0;
|
||||||
|
|
||||||
|
switch(len & 15) {
|
||||||
|
case 15: k2 ^= ((u64)tail[14]) << 48;
|
||||||
|
case 14: k2 ^= ((u64)tail[13]) << 40;
|
||||||
|
case 13: k2 ^= ((u64)tail[12]) << 32;
|
||||||
|
case 12: k2 ^= ((u64)tail[11]) << 24;
|
||||||
|
case 11: k2 ^= ((u64)tail[10]) << 16;
|
||||||
|
case 10: k2 ^= ((u64)tail[ 9]) << 8;
|
||||||
|
case 9: k2 ^= ((u64)tail[ 8]) << 0;
|
||||||
|
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||||
|
|
||||||
|
case 8: k1 ^= ((u64)tail[ 7]) << 56;
|
||||||
|
case 7: k1 ^= ((u64)tail[ 6]) << 48;
|
||||||
|
case 6: k1 ^= ((u64)tail[ 5]) << 40;
|
||||||
|
case 5: k1 ^= ((u64)tail[ 4]) << 32;
|
||||||
|
case 4: k1 ^= ((u64)tail[ 3]) << 24;
|
||||||
|
case 3: k1 ^= ((u64)tail[ 2]) << 16;
|
||||||
|
case 2: k1 ^= ((u64)tail[ 1]) << 8;
|
||||||
|
case 1: k1 ^= ((u64)tail[ 0]) << 0;
|
||||||
|
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 ^= len;
|
||||||
|
h2 ^= len;
|
||||||
|
|
||||||
|
h1 += h2;
|
||||||
|
h2 += h1;
|
||||||
|
|
||||||
|
h1 = fmix64(h1);
|
||||||
|
h2 = fmix64(h2);
|
||||||
|
|
||||||
|
h1 += h2;
|
||||||
|
h2 += h1;
|
||||||
|
|
||||||
|
((u64 *)out)[0] = h1;
|
||||||
|
((u64 *)out)[1] = h2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MurmurHash3_x86_128(void const *key, isize len, u32 seed, void *out) {
|
||||||
|
u8 const * data = cast(u8 * const)key;
|
||||||
|
isize nblocks = len / 16;
|
||||||
|
|
||||||
|
u32 h1 = seed;
|
||||||
|
u32 h2 = seed;
|
||||||
|
u32 h3 = seed;
|
||||||
|
u32 h4 = seed;
|
||||||
|
|
||||||
|
u32 const c1 = 0x239b961b;
|
||||||
|
u32 const c2 = 0xab0e9789;
|
||||||
|
u32 const c3 = 0x38b34ae5;
|
||||||
|
u32 const c4 = 0xa1e38b93;
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// body
|
||||||
|
|
||||||
|
u32 const * blocks = cast(u32 const *)(data + nblocks*16);
|
||||||
|
|
||||||
|
for (isize i = -nblocks; i != 0; i++) {
|
||||||
|
u32 k1 = mm3_getblock32(blocks, i*4 + 0);
|
||||||
|
u32 k2 = mm3_getblock32(blocks, i*4 + 1);
|
||||||
|
u32 k3 = mm3_getblock32(blocks, i*4 + 2);
|
||||||
|
u32 k4 = mm3_getblock32(blocks, i*4 + 3);
|
||||||
|
|
||||||
|
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||||
|
|
||||||
|
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
||||||
|
|
||||||
|
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||||
|
|
||||||
|
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
||||||
|
|
||||||
|
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||||
|
|
||||||
|
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
||||||
|
|
||||||
|
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||||
|
|
||||||
|
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// tail
|
||||||
|
|
||||||
|
u8 const * tail = cast(u8 const *)(data + nblocks*16);
|
||||||
|
|
||||||
|
u32 k1 = 0;
|
||||||
|
u32 k2 = 0;
|
||||||
|
u32 k3 = 0;
|
||||||
|
u32 k4 = 0;
|
||||||
|
|
||||||
|
switch(len & 15) {
|
||||||
|
case 15: k4 ^= tail[14] << 16;
|
||||||
|
case 14: k4 ^= tail[13] << 8;
|
||||||
|
case 13: k4 ^= tail[12] << 0;
|
||||||
|
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||||
|
|
||||||
|
case 12: k3 ^= tail[11] << 24;
|
||||||
|
case 11: k3 ^= tail[10] << 16;
|
||||||
|
case 10: k3 ^= tail[ 9] << 8;
|
||||||
|
case 9: k3 ^= tail[ 8] << 0;
|
||||||
|
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||||
|
|
||||||
|
case 8: k2 ^= tail[ 7] << 24;
|
||||||
|
case 7: k2 ^= tail[ 6] << 16;
|
||||||
|
case 6: k2 ^= tail[ 5] << 8;
|
||||||
|
case 5: k2 ^= tail[ 4] << 0;
|
||||||
|
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||||
|
|
||||||
|
case 4: k1 ^= tail[ 3] << 24;
|
||||||
|
case 3: k1 ^= tail[ 2] << 16;
|
||||||
|
case 2: k1 ^= tail[ 1] << 8;
|
||||||
|
case 1: k1 ^= tail[ 0] << 0;
|
||||||
|
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// finalization
|
||||||
|
|
||||||
|
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||||
|
|
||||||
|
h1 += h2; h1 += h3; h1 += h4;
|
||||||
|
h2 += h1; h3 += h1; h4 += h1;
|
||||||
|
|
||||||
|
h1 = fmix32(h1);
|
||||||
|
h2 = fmix32(h2);
|
||||||
|
h3 = fmix32(h3);
|
||||||
|
h4 = fmix32(h4);
|
||||||
|
|
||||||
|
h1 += h2; h1 += h3; h1 += h4;
|
||||||
|
h2 += h1; h3 += h1; h4 += h1;
|
||||||
|
|
||||||
|
|
||||||
|
((u32 *)out)[0] = h1;
|
||||||
|
((u32 *)out)[1] = h2;
|
||||||
|
((u32 *)out)[2] = h3;
|
||||||
|
((u32 *)out)[3] = h4;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline u128 MurmurHash3_128(void const *key, isize len, u32 seed) {
|
||||||
|
u128 res;
|
||||||
|
#if defined(GB_ARCH_64_BIT)
|
||||||
|
MurmurHash3_x64_128(key, len, seed, &res);
|
||||||
|
#else
|
||||||
|
MurmurHash3_x86_128(key, len, seed, &res);
|
||||||
|
#endif
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
-1305
File diff suppressed because it is too large
Load Diff
-3207
File diff suppressed because it is too large
Load Diff
+5168
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ gb_inline void print_indent(isize indent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void print_ast(AstNode *node, isize indent) {
|
void print_ast(AstNode *node, isize indent) {
|
||||||
if (node == NULL)
|
if (node == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
+2569
File diff suppressed because it is too large
Load Diff
+275
@@ -0,0 +1,275 @@
|
|||||||
|
#define SSA_OPS \
|
||||||
|
SSA_OP(Invalid)\
|
||||||
|
\
|
||||||
|
SSA_OP(Unknown)\
|
||||||
|
\
|
||||||
|
SSA_OP(Comment) /* Does nothing */\
|
||||||
|
\
|
||||||
|
SSA_OP(SP) /* Stack Pointer */\
|
||||||
|
SSA_OP(SB) /* Stack Base */\
|
||||||
|
\
|
||||||
|
SSA_OP(Local)\
|
||||||
|
SSA_OP(Global)\
|
||||||
|
SSA_OP(Proc)\
|
||||||
|
\
|
||||||
|
SSA_OP(Load)\
|
||||||
|
SSA_OP(Store)\
|
||||||
|
SSA_OP(Move)\
|
||||||
|
SSA_OP(LoadReg)\
|
||||||
|
SSA_OP(StoreReg)\
|
||||||
|
SSA_OP(Zero) /* Zero initialize */\
|
||||||
|
\
|
||||||
|
SSA_OP(ArrayIndex) /* Index for a fixed array */\
|
||||||
|
SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
|
||||||
|
SSA_OP(PtrOffset)\
|
||||||
|
SSA_OP(ValueIndex) /* Extract for a value from a register */\
|
||||||
|
\
|
||||||
|
SSA_OP(Phi)\
|
||||||
|
SSA_OP(Copy)\
|
||||||
|
\
|
||||||
|
/* TODO(bill): calling conventions */\
|
||||||
|
SSA_OP(CallOdin)\
|
||||||
|
SSA_OP(CallC)\
|
||||||
|
SSA_OP(CallStd)\
|
||||||
|
SSA_OP(CallFast)\
|
||||||
|
\
|
||||||
|
SSA_OP(BoundsCheck)\
|
||||||
|
SSA_OP(SliceBoundsCheck)\
|
||||||
|
\
|
||||||
|
/* Built in operations/procedures */\
|
||||||
|
SSA_OP(Bswap16)\
|
||||||
|
SSA_OP(Bswap32)\
|
||||||
|
SSA_OP(Bswap64)\
|
||||||
|
\
|
||||||
|
SSA_OP(Assume)\
|
||||||
|
SSA_OP(DebugTrap)\
|
||||||
|
SSA_OP(Trap)\
|
||||||
|
SSA_OP(ReadCycleCounter)\
|
||||||
|
\
|
||||||
|
\
|
||||||
|
SSA_OP(ConstBool)\
|
||||||
|
SSA_OP(ConstString)\
|
||||||
|
SSA_OP(ConstSlice)\
|
||||||
|
SSA_OP(ConstNil)\
|
||||||
|
SSA_OP(Const8)\
|
||||||
|
SSA_OP(Const16)\
|
||||||
|
SSA_OP(Const32)\
|
||||||
|
SSA_OP(Const64)\
|
||||||
|
SSA_OP(Const32F)\
|
||||||
|
SSA_OP(Const64F)\
|
||||||
|
\
|
||||||
|
/* These should be all the operations I could possibly need for the mean time */\
|
||||||
|
SSA_OP(Add8)\
|
||||||
|
SSA_OP(Add16)\
|
||||||
|
SSA_OP(Add32)\
|
||||||
|
SSA_OP(Add64)\
|
||||||
|
SSA_OP(AddPtr)\
|
||||||
|
SSA_OP(Add32F)\
|
||||||
|
SSA_OP(Add64F)\
|
||||||
|
SSA_OP(Sub8)\
|
||||||
|
SSA_OP(Sub16)\
|
||||||
|
SSA_OP(Sub32)\
|
||||||
|
SSA_OP(Sub64)\
|
||||||
|
SSA_OP(SubPtr)\
|
||||||
|
SSA_OP(Sub32F)\
|
||||||
|
SSA_OP(Sub64F)\
|
||||||
|
SSA_OP(Mul8)\
|
||||||
|
SSA_OP(Mul16)\
|
||||||
|
SSA_OP(Mul32)\
|
||||||
|
SSA_OP(Mul64)\
|
||||||
|
SSA_OP(Mul32F)\
|
||||||
|
SSA_OP(Mul64F)\
|
||||||
|
SSA_OP(Div8)\
|
||||||
|
SSA_OP(Div8U)\
|
||||||
|
SSA_OP(Div16)\
|
||||||
|
SSA_OP(Div16U)\
|
||||||
|
SSA_OP(Div32)\
|
||||||
|
SSA_OP(Div32U)\
|
||||||
|
SSA_OP(Div64)\
|
||||||
|
SSA_OP(Div64U)\
|
||||||
|
SSA_OP(Div32F)\
|
||||||
|
SSA_OP(Div64F)\
|
||||||
|
SSA_OP(Mod8)\
|
||||||
|
SSA_OP(Mod8U)\
|
||||||
|
SSA_OP(Mod16)\
|
||||||
|
SSA_OP(Mod16U)\
|
||||||
|
SSA_OP(Mod32)\
|
||||||
|
SSA_OP(Mod32U)\
|
||||||
|
SSA_OP(Mod64)\
|
||||||
|
SSA_OP(Mod64U)\
|
||||||
|
\
|
||||||
|
SSA_OP(And8)\
|
||||||
|
SSA_OP(And16)\
|
||||||
|
SSA_OP(And32)\
|
||||||
|
SSA_OP(And64)\
|
||||||
|
SSA_OP(Or8)\
|
||||||
|
SSA_OP(Or16)\
|
||||||
|
SSA_OP(Or32)\
|
||||||
|
SSA_OP(Or64)\
|
||||||
|
SSA_OP(Xor8)\
|
||||||
|
SSA_OP(Xor16)\
|
||||||
|
SSA_OP(Xor32)\
|
||||||
|
SSA_OP(Xor64)\
|
||||||
|
SSA_OP(AndNot8)\
|
||||||
|
SSA_OP(AndNot16)\
|
||||||
|
SSA_OP(AndNot32)\
|
||||||
|
SSA_OP(AndNot64)\
|
||||||
|
\
|
||||||
|
SSA_OP(Lsh8x8)\
|
||||||
|
SSA_OP(Lsh8x16)\
|
||||||
|
SSA_OP(Lsh8x32)\
|
||||||
|
SSA_OP(Lsh8x64)\
|
||||||
|
SSA_OP(Lsh16x8)\
|
||||||
|
SSA_OP(Lsh16x16)\
|
||||||
|
SSA_OP(Lsh16x32)\
|
||||||
|
SSA_OP(Lsh16x64)\
|
||||||
|
SSA_OP(Lsh32x8)\
|
||||||
|
SSA_OP(Lsh32x16)\
|
||||||
|
SSA_OP(Lsh32x32)\
|
||||||
|
SSA_OP(Lsh32x64)\
|
||||||
|
SSA_OP(Lsh64x8)\
|
||||||
|
SSA_OP(Lsh64x16)\
|
||||||
|
SSA_OP(Lsh64x32)\
|
||||||
|
SSA_OP(Lsh64x64)\
|
||||||
|
SSA_OP(Rsh8x8)\
|
||||||
|
SSA_OP(Rsh8x16)\
|
||||||
|
SSA_OP(Rsh8x32)\
|
||||||
|
SSA_OP(Rsh8x64)\
|
||||||
|
SSA_OP(Rsh16x8)\
|
||||||
|
SSA_OP(Rsh16x16)\
|
||||||
|
SSA_OP(Rsh16x32)\
|
||||||
|
SSA_OP(Rsh16x64)\
|
||||||
|
SSA_OP(Rsh32x8)\
|
||||||
|
SSA_OP(Rsh32x16)\
|
||||||
|
SSA_OP(Rsh32x32)\
|
||||||
|
SSA_OP(Rsh32x64)\
|
||||||
|
SSA_OP(Rsh64x8)\
|
||||||
|
SSA_OP(Rsh64x16)\
|
||||||
|
SSA_OP(Rsh64x32)\
|
||||||
|
SSA_OP(Rsh64x64)\
|
||||||
|
SSA_OP(Rsh8Ux8)\
|
||||||
|
SSA_OP(Rsh8Ux16)\
|
||||||
|
SSA_OP(Rsh8Ux32)\
|
||||||
|
SSA_OP(Rsh8Ux64)\
|
||||||
|
SSA_OP(Rsh16Ux8)\
|
||||||
|
SSA_OP(Rsh16Ux16)\
|
||||||
|
SSA_OP(Rsh16Ux32)\
|
||||||
|
SSA_OP(Rsh16Ux64)\
|
||||||
|
SSA_OP(Rsh32Ux8)\
|
||||||
|
SSA_OP(Rsh32Ux16)\
|
||||||
|
SSA_OP(Rsh32Ux32)\
|
||||||
|
SSA_OP(Rsh32Ux64)\
|
||||||
|
SSA_OP(Rsh64Ux8)\
|
||||||
|
SSA_OP(Rsh64Ux16)\
|
||||||
|
SSA_OP(Rsh64Ux32)\
|
||||||
|
SSA_OP(Rsh64Ux64)\
|
||||||
|
\
|
||||||
|
SSA_OP(Eq8)\
|
||||||
|
SSA_OP(Eq16)\
|
||||||
|
SSA_OP(Eq32)\
|
||||||
|
SSA_OP(Eq64)\
|
||||||
|
SSA_OP(EqPtr)\
|
||||||
|
SSA_OP(Eq32F)\
|
||||||
|
SSA_OP(Eq64F)\
|
||||||
|
SSA_OP(Ne8)\
|
||||||
|
SSA_OP(Ne16)\
|
||||||
|
SSA_OP(Ne32)\
|
||||||
|
SSA_OP(Ne64)\
|
||||||
|
SSA_OP(NePtr)\
|
||||||
|
SSA_OP(Ne32F)\
|
||||||
|
SSA_OP(Ne64F)\
|
||||||
|
SSA_OP(Lt8)\
|
||||||
|
SSA_OP(Lt16)\
|
||||||
|
SSA_OP(Lt32)\
|
||||||
|
SSA_OP(Lt64)\
|
||||||
|
SSA_OP(LtPtr)\
|
||||||
|
SSA_OP(Lt32F)\
|
||||||
|
SSA_OP(Lt64F)\
|
||||||
|
SSA_OP(Gt8)\
|
||||||
|
SSA_OP(Gt16)\
|
||||||
|
SSA_OP(Gt32)\
|
||||||
|
SSA_OP(Gt64)\
|
||||||
|
SSA_OP(GtPtr)\
|
||||||
|
SSA_OP(Gt32F)\
|
||||||
|
SSA_OP(Gt64F)\
|
||||||
|
SSA_OP(Le8)\
|
||||||
|
SSA_OP(Le16)\
|
||||||
|
SSA_OP(Le32)\
|
||||||
|
SSA_OP(Le64)\
|
||||||
|
SSA_OP(LePtr)\
|
||||||
|
SSA_OP(Le32F)\
|
||||||
|
SSA_OP(Le64F)\
|
||||||
|
SSA_OP(Ge8)\
|
||||||
|
SSA_OP(Ge16)\
|
||||||
|
SSA_OP(Ge32)\
|
||||||
|
SSA_OP(Ge64)\
|
||||||
|
SSA_OP(GePtr)\
|
||||||
|
SSA_OP(Ge32F)\
|
||||||
|
SSA_OP(Ge64F)\
|
||||||
|
\
|
||||||
|
SSA_OP(NotB)\
|
||||||
|
SSA_OP(EqB)\
|
||||||
|
SSA_OP(NeB)\
|
||||||
|
\
|
||||||
|
SSA_OP(Neg8)\
|
||||||
|
SSA_OP(Neg16)\
|
||||||
|
SSA_OP(Neg32)\
|
||||||
|
SSA_OP(Neg64)\
|
||||||
|
SSA_OP(Neg32F)\
|
||||||
|
SSA_OP(Neg64F)\
|
||||||
|
\
|
||||||
|
SSA_OP(Not8)\
|
||||||
|
SSA_OP(Not16)\
|
||||||
|
SSA_OP(Not32)\
|
||||||
|
SSA_OP(Not64)\
|
||||||
|
\
|
||||||
|
SSA_OP(SignExt8to16)\
|
||||||
|
SSA_OP(SignExt8to32)\
|
||||||
|
SSA_OP(SignExt8to64)\
|
||||||
|
SSA_OP(SignExt16to32)\
|
||||||
|
SSA_OP(SignExt16to64)\
|
||||||
|
SSA_OP(SignExt32to64)\
|
||||||
|
SSA_OP(ZeroExt8to16)\
|
||||||
|
SSA_OP(ZeroExt8to32)\
|
||||||
|
SSA_OP(ZeroExt8to64)\
|
||||||
|
SSA_OP(ZeroExt16to32)\
|
||||||
|
SSA_OP(ZeroExt16to64)\
|
||||||
|
SSA_OP(ZeroExt32to64)\
|
||||||
|
SSA_OP(Trunc16to8)\
|
||||||
|
SSA_OP(Trunc32to8)\
|
||||||
|
SSA_OP(Trunc32to16)\
|
||||||
|
SSA_OP(Trunc64to8)\
|
||||||
|
SSA_OP(Trunc64to16)\
|
||||||
|
SSA_OP(Trunc64to32)\
|
||||||
|
\
|
||||||
|
SSA_OP(Cvt32to32F)\
|
||||||
|
SSA_OP(Cvt32to64F)\
|
||||||
|
SSA_OP(Cvt64to32F)\
|
||||||
|
SSA_OP(Cvt64to64F)\
|
||||||
|
SSA_OP(Cvt32Fto32)\
|
||||||
|
SSA_OP(Cvt32Fto64)\
|
||||||
|
SSA_OP(Cvt64Fto32)\
|
||||||
|
SSA_OP(Cvt64Fto64)\
|
||||||
|
SSA_OP(Cvt32Fto64F)\
|
||||||
|
SSA_OP(Cvt64Fto32F)\
|
||||||
|
SSA_OP(Cvt32Uto32F)\
|
||||||
|
SSA_OP(Cvt32Uto64F)\
|
||||||
|
SSA_OP(Cvt32Fto32U)\
|
||||||
|
SSA_OP(Cvt64Fto32U)\
|
||||||
|
SSA_OP(Cvt64Uto32F)\
|
||||||
|
SSA_OP(Cvt64Uto64F)\
|
||||||
|
SSA_OP(Cvt32Fto64U)\
|
||||||
|
SSA_OP(Cvt64Fto64U)\
|
||||||
|
|
||||||
|
|
||||||
|
enum ssaOp {
|
||||||
|
#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
|
||||||
|
SSA_OPS
|
||||||
|
#undef SSA_OP
|
||||||
|
};
|
||||||
|
|
||||||
|
String const ssa_op_strings[] = {
|
||||||
|
#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
||||||
|
SSA_OPS
|
||||||
|
#undef SSA_OP
|
||||||
|
};
|
||||||
-1439
File diff suppressed because it is too large
Load Diff
-443
@@ -1,443 +0,0 @@
|
|||||||
gb_global gbArena string_buffer_arena = {0};
|
|
||||||
gb_global gbAllocator string_buffer_allocator = {0};
|
|
||||||
|
|
||||||
void init_string_buffer_memory(void) {
|
|
||||||
// NOTE(bill): This should be enough memory for file systems
|
|
||||||
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
|
|
||||||
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE(bill): Used for UTF-8 strings
|
|
||||||
typedef struct String {
|
|
||||||
u8 * text;
|
|
||||||
isize len;
|
|
||||||
} String;
|
|
||||||
// NOTE(bill): used for printf style arguments
|
|
||||||
#define LIT(x) ((int)(x).len), (x).text
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct String16 {
|
|
||||||
wchar_t *text;
|
|
||||||
isize len;
|
|
||||||
} String16;
|
|
||||||
|
|
||||||
|
|
||||||
gb_inline String make_string(u8 *text, isize len) {
|
|
||||||
String s;
|
|
||||||
s.text = text;
|
|
||||||
if (len < 0) {
|
|
||||||
len = gb_strlen(cast(char *)text);
|
|
||||||
}
|
|
||||||
s.len = len;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gb_inline String16 make_string16(wchar_t *text, isize len) {
|
|
||||||
String16 s;
|
|
||||||
s.text = text;
|
|
||||||
s.len = len;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gb_inline String make_string_c(char *text) {
|
|
||||||
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gb_inline bool str_eq_ignore_case(String a, String b) {
|
|
||||||
if (a.len == b.len) {
|
|
||||||
isize i;
|
|
||||||
for (i = 0; i < a.len; i++) {
|
|
||||||
char x = cast(char)a.text[i];
|
|
||||||
char y = cast(char)b.text[i];
|
|
||||||
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int string_compare(String x, String y) {
|
|
||||||
if (!(x.len == y.len &&
|
|
||||||
x.text == y.text)) {
|
|
||||||
isize n, fast, offset, curr_block;
|
|
||||||
isize *la, *lb;
|
|
||||||
isize pos;
|
|
||||||
|
|
||||||
n = gb_min(x.len, y.len);
|
|
||||||
|
|
||||||
fast = n/gb_size_of(isize) + 1;
|
|
||||||
offset = (fast-1)*gb_size_of(isize);
|
|
||||||
curr_block = 0;
|
|
||||||
if (n <= gb_size_of(isize)) {
|
|
||||||
fast = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
la = cast(isize *)x.text;
|
|
||||||
lb = cast(isize *)y.text;
|
|
||||||
|
|
||||||
for (; curr_block < fast; curr_block++) {
|
|
||||||
if (la[curr_block] ^ lb[curr_block]) {
|
|
||||||
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
|
||||||
if (x.text[pos] ^ y.text[pos]) {
|
|
||||||
return cast(int)x.text[pos] - cast(int)y.text[pos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; offset < n; offset++) {
|
|
||||||
if (x.text[offset] ^ y.text[offset]) {
|
|
||||||
return cast(int)x.text[offset] - cast(int)y.text[offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
GB_COMPARE_PROC(string_cmp_proc) {
|
|
||||||
String x = *(String *)a;
|
|
||||||
String y = *(String *)b;
|
|
||||||
return string_compare(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; }
|
|
||||||
// gb_inline bool operator !=(String a, String b) { return !operator==(a, b); }
|
|
||||||
// gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; }
|
|
||||||
// gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; }
|
|
||||||
// gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
|
|
||||||
// gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
|
|
||||||
|
|
||||||
// template <size_t N> gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); }
|
|
||||||
// template <size_t N> gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); }
|
|
||||||
// template <size_t N> gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; }
|
|
||||||
// template <size_t N> gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; }
|
|
||||||
|
|
||||||
gb_inline bool str_eq(String a, String b) { return a.len == b.len ? gb_memcompare(a.text, b.text, a.len) == 0 : false; }
|
|
||||||
gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); }
|
|
||||||
gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; }
|
|
||||||
gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; }
|
|
||||||
gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; }
|
|
||||||
gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; }
|
|
||||||
|
|
||||||
gb_inline bool str_has_prefix(String s, String prefix) {
|
|
||||||
isize i;
|
|
||||||
if (prefix.len < s.len) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < prefix.len; i++) {
|
|
||||||
if (s.text[i] != prefix.text[i]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline isize string_extension_position(String str) {
|
|
||||||
isize dot_pos = -1;
|
|
||||||
isize i = str.len;
|
|
||||||
bool seen_dot = false;
|
|
||||||
while (i --> 0) {
|
|
||||||
if (str.text[i] == GB_PATH_SEPARATOR)
|
|
||||||
break;
|
|
||||||
if (str.text[i] == '.') {
|
|
||||||
dot_pos = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dot_pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
gb_inline bool string_has_extension(String str, String ext) {
|
|
||||||
if (str.len > ext.len+1) {
|
|
||||||
u8 *s = str.text+str.len - ext.len-1;
|
|
||||||
if (s[0] == '.') {
|
|
||||||
s++;
|
|
||||||
return gb_memcompare(s, ext.text, ext.len) == 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_contains_char(String s, u8 c) {
|
|
||||||
isize i;
|
|
||||||
for (i = 0; i < s.len; i++) {
|
|
||||||
if (s.text[i] == c)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): Make this non-windows specific
|
|
||||||
String16 string_to_string16(gbAllocator a, String s) {
|
|
||||||
int len, len1;
|
|
||||||
wchar_t *text;
|
|
||||||
|
|
||||||
if (s.len < 1) {
|
|
||||||
return make_string16(NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
||||||
cast(char *)s.text, s.len, NULL, 0);
|
|
||||||
if (len == 0) {
|
|
||||||
return make_string16(NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = gb_alloc_array(a, wchar_t, len+1);
|
|
||||||
|
|
||||||
len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
||||||
cast(char *)s.text, s.len, text, len);
|
|
||||||
if (len1 == 0) {
|
|
||||||
gb_free(a, text);
|
|
||||||
return make_string16(NULL, 0);
|
|
||||||
}
|
|
||||||
text[len] = 0;
|
|
||||||
|
|
||||||
return make_string16(text, len-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
String string16_to_string(gbAllocator a, String16 s) {
|
|
||||||
int len, len1;
|
|
||||||
u8 *text;
|
|
||||||
|
|
||||||
if (s.len < 1) {
|
|
||||||
return make_string(NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
|
||||||
s.text, s.len, NULL, 0, NULL, NULL);
|
|
||||||
if (len == 0) {
|
|
||||||
return make_string(NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = gb_alloc_array(a, u8, len+1);
|
|
||||||
|
|
||||||
len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
|
||||||
s.text, s.len, cast(char *)text, len, NULL, NULL);
|
|
||||||
if (len1 == 0) {
|
|
||||||
gb_free(a, text);
|
|
||||||
return make_string(NULL, 0);
|
|
||||||
}
|
|
||||||
text[len] = 0;
|
|
||||||
|
|
||||||
return make_string(text, len-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
|
|
||||||
u8 c;
|
|
||||||
|
|
||||||
if (s.text[0] == quote &&
|
|
||||||
(quote == '\'' || quote == '"')) {
|
|
||||||
return false;
|
|
||||||
} else if (s.text[0] >= 0x80) {
|
|
||||||
Rune r = -1;
|
|
||||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
|
||||||
*rune = r;
|
|
||||||
*multiple_bytes = true;
|
|
||||||
*tail_string = make_string(s.text+size, s.len-size);
|
|
||||||
return true;
|
|
||||||
} else if (s.text[0] != '\\') {
|
|
||||||
*rune = s.text[0];
|
|
||||||
*tail_string = make_string(s.text+1, s.len-1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.len <= 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
c = s.text[1];
|
|
||||||
s = make_string(s.text+2, s.len-2);
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
default: return false;
|
|
||||||
|
|
||||||
case 'a': *rune = '\a'; break;
|
|
||||||
case 'b': *rune = '\b'; break;
|
|
||||||
case 'f': *rune = '\f'; break;
|
|
||||||
case 'n': *rune = '\n'; break;
|
|
||||||
case 'r': *rune = '\r'; break;
|
|
||||||
case 't': *rune = '\t'; break;
|
|
||||||
case 'v': *rune = '\v'; break;
|
|
||||||
case '\\': *rune = '\\'; break;
|
|
||||||
|
|
||||||
|
|
||||||
case '\'':
|
|
||||||
case '"':
|
|
||||||
if (c != quote) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*rune = c;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7': {
|
|
||||||
isize i;
|
|
||||||
i32 r = gb_digit_to_int(c);
|
|
||||||
if (s.len < 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < 2; i++) {
|
|
||||||
i32 d = gb_digit_to_int(s.text[i]);
|
|
||||||
if (d < 0 || d > 7) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
r = (r<<3) | d;
|
|
||||||
}
|
|
||||||
s = make_string(s.text+2, s.len-2);
|
|
||||||
if (r > 0xff) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*rune = r;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'x':
|
|
||||||
case 'u':
|
|
||||||
case 'U': {
|
|
||||||
Rune r = 0;
|
|
||||||
isize i, count = 0;
|
|
||||||
switch (c) {
|
|
||||||
case 'x': count = 2; break;
|
|
||||||
case 'u': count = 4; break;
|
|
||||||
case 'U': count = 8; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.len < count) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
i32 d = gb_hex_digit_to_int(s.text[i]);
|
|
||||||
if (d < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
r = (r<<4) | d;
|
|
||||||
}
|
|
||||||
s = make_string(s.text+count, s.len-count);
|
|
||||||
if (c == 'x') {
|
|
||||||
*rune = r;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (r > GB_RUNE_MAX) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*rune = r;
|
|
||||||
*multiple_bytes = true;
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
*tail_string = s;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 0 == failure
|
|
||||||
// 1 == original memory
|
|
||||||
// 2 == new allocation
|
|
||||||
i32 unquote_string(gbAllocator a, String *s_) {
|
|
||||||
String s = *s_;
|
|
||||||
isize n = s.len;
|
|
||||||
u8 quote;
|
|
||||||
if (n < 2) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
quote = s.text[0];
|
|
||||||
if (quote != s.text[n-1]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.text += 1;
|
|
||||||
s.len -= 2;
|
|
||||||
|
|
||||||
if (quote == '`') {
|
|
||||||
if (string_contains_char(s, '`')) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
*s_ = s;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (quote != '"' && quote != '\'') {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string_contains_char(s, '\n')) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
|
||||||
if (quote == '"') {
|
|
||||||
*s_ = s;
|
|
||||||
return 1;
|
|
||||||
} else if (quote == '\'') {
|
|
||||||
Rune r = GB_RUNE_INVALID;
|
|
||||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
|
||||||
if ((size == s.len) && (r != -1 || size != 1)) {
|
|
||||||
*s_ = s;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
u8 rune_temp[4] = {0};
|
|
||||||
isize buf_len = 3*s.len / 2;
|
|
||||||
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
|
||||||
isize offset = 0;
|
|
||||||
while (s.len > 0) {
|
|
||||||
String tail_string = {0};
|
|
||||||
Rune r = 0;
|
|
||||||
bool multiple_bytes = false;
|
|
||||||
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
|
||||||
if (!success) {
|
|
||||||
gb_free(a, buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s = tail_string;
|
|
||||||
|
|
||||||
if (r < 0x80 || !multiple_bytes) {
|
|
||||||
buf[offset++] = cast(u8)r;
|
|
||||||
} else {
|
|
||||||
isize size = gb_utf8_encode_rune(rune_temp, r);
|
|
||||||
gb_memmove(buf+offset, rune_temp, size);
|
|
||||||
offset += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quote == '\'' && s.len != 0) {
|
|
||||||
gb_free(a, buf);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*s_ = make_string(buf, offset);
|
|
||||||
}
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
+569
@@ -0,0 +1,569 @@
|
|||||||
|
gb_global gbArena string_buffer_arena = {};
|
||||||
|
gb_global gbAllocator string_buffer_allocator = {};
|
||||||
|
gb_global gbMutex string_buffer_mutex = {};
|
||||||
|
|
||||||
|
void init_string_buffer_memory(void) {
|
||||||
|
// NOTE(bill): This should be enough memory for file systems
|
||||||
|
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
|
||||||
|
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
|
||||||
|
gb_mutex_init(&string_buffer_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE(bill): Used for UTF-8 strings
|
||||||
|
struct String {
|
||||||
|
u8 * text;
|
||||||
|
isize len;
|
||||||
|
|
||||||
|
u8 &operator[](isize i) {
|
||||||
|
GB_ASSERT(0 <= i && i < len);
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
u8 const &operator[](isize i) const {
|
||||||
|
GB_ASSERT(0 <= i && i < len);
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// NOTE(bill): used for printf style arguments
|
||||||
|
#define LIT(x) ((int)(x).len), (x).text
|
||||||
|
#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1}
|
||||||
|
#if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
|
||||||
|
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
|
||||||
|
#else
|
||||||
|
#define str_lit(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOTE(bill): String16 is only used for Windows due to its file directories
|
||||||
|
struct String16 {
|
||||||
|
wchar_t *text;
|
||||||
|
isize len;
|
||||||
|
wchar_t &operator[](isize i) {
|
||||||
|
GB_ASSERT(0 <= i && i < len);
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
wchar_t const &operator[](isize i) const {
|
||||||
|
GB_ASSERT(0 <= i && i < len);
|
||||||
|
return text[i];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
gb_inline String make_string(u8 *text, isize len) {
|
||||||
|
String s;
|
||||||
|
s.text = text;
|
||||||
|
if (len < 0) {
|
||||||
|
len = gb_strlen(cast(char *)text);
|
||||||
|
}
|
||||||
|
s.len = len;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gb_inline String16 make_string16(wchar_t *text, isize len) {
|
||||||
|
String16 s;
|
||||||
|
s.text = text;
|
||||||
|
s.len = len;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
isize string16_len(wchar_t *s) {
|
||||||
|
if (s == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
wchar_t *p = s;
|
||||||
|
while (*p) {
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return p - s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gb_inline String make_string_c(char *text) {
|
||||||
|
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
String substring(String s, isize lo, isize hi) {
|
||||||
|
isize max = s.len;
|
||||||
|
GB_ASSERT(lo <= hi && hi <= max);
|
||||||
|
|
||||||
|
return make_string(s.text+lo, hi-lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gb_inline bool str_eq_ignore_case(String a, String b) {
|
||||||
|
if (a.len == b.len) {
|
||||||
|
isize i;
|
||||||
|
for (i = 0; i < a.len; i++) {
|
||||||
|
char x = cast(char)a[i];
|
||||||
|
char y = cast(char)b[i];
|
||||||
|
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int string_compare(String const &x, String const &y) {
|
||||||
|
if (x.len != y.len || x.text != y.text) {
|
||||||
|
isize n, fast, offset, curr_block;
|
||||||
|
isize *la, *lb;
|
||||||
|
isize pos;
|
||||||
|
|
||||||
|
n = gb_min(x.len, y.len);
|
||||||
|
|
||||||
|
fast = n/gb_size_of(isize) + 1;
|
||||||
|
offset = (fast-1)*gb_size_of(isize);
|
||||||
|
curr_block = 0;
|
||||||
|
if (n <= gb_size_of(isize)) {
|
||||||
|
fast = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
la = cast(isize *)x.text;
|
||||||
|
lb = cast(isize *)y.text;
|
||||||
|
|
||||||
|
for (; curr_block < fast; curr_block++) {
|
||||||
|
if (la[curr_block] ^ lb[curr_block]) {
|
||||||
|
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
||||||
|
if (x[pos] ^ y[pos]) {
|
||||||
|
return cast(int)x[pos] - cast(int)y[pos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; offset < n; offset++) {
|
||||||
|
if (x[offset] ^ y[offset]) {
|
||||||
|
return cast(int)x[offset] - cast(int)y[offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GB_COMPARE_PROC(string_cmp_proc) {
|
||||||
|
String x = *(String *)a;
|
||||||
|
String y = *(String *)b;
|
||||||
|
return string_compare(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline bool str_eq(String const &a, String const &b) {
|
||||||
|
if (a.len != b.len) return false;
|
||||||
|
for (isize i = 0; i < a.len; i++) {
|
||||||
|
if (a.text[i] != b.text[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); }
|
||||||
|
gb_inline bool str_lt(String const &a, String const &b) { return string_compare(a, b) < 0; }
|
||||||
|
gb_inline bool str_gt(String const &a, String const &b) { return string_compare(a, b) > 0; }
|
||||||
|
gb_inline bool str_le(String const &a, String const &b) { return string_compare(a, b) <= 0; }
|
||||||
|
gb_inline bool str_ge(String const &a, String const &b) { return string_compare(a, b) >= 0; }
|
||||||
|
|
||||||
|
gb_inline bool operator == (String const &a, String const &b) { return str_eq(a, b); }
|
||||||
|
gb_inline bool operator != (String const &a, String const &b) { return str_ne(a, b); }
|
||||||
|
gb_inline bool operator < (String const &a, String const &b) { return str_lt(a, b); }
|
||||||
|
gb_inline bool operator > (String const &a, String const &b) { return str_gt(a, b); }
|
||||||
|
gb_inline bool operator <= (String const &a, String const &b) { return str_le(a, b); }
|
||||||
|
gb_inline bool operator >= (String const &a, String const &b) { return str_ge(a, b); }
|
||||||
|
|
||||||
|
template <isize N> bool operator == (String const &a, char const (&b)[N]) { return str_eq(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
template <isize N> bool operator != (String const &a, char const (&b)[N]) { return str_ne(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
template <isize N> bool operator < (String const &a, char const (&b)[N]) { return str_lt(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
template <isize N> bool operator > (String const &a, char const (&b)[N]) { return str_gt(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
gb_inline bool str_has_prefix(String s, String prefix) {
|
||||||
|
isize i;
|
||||||
|
if (prefix.len < s.len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < prefix.len; i++) {
|
||||||
|
if (s[i] != prefix[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline isize string_extension_position(String str) {
|
||||||
|
isize dot_pos = -1;
|
||||||
|
isize i = str.len;
|
||||||
|
while (i --> 0) {
|
||||||
|
if (str[i] == GB_PATH_SEPARATOR)
|
||||||
|
break;
|
||||||
|
if (str[i] == '.') {
|
||||||
|
dot_pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dot_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
String string_trim_whitespace(String str) {
|
||||||
|
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
|
||||||
|
str.len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (str.len > 0 && rune_is_whitespace(str[0])) {
|
||||||
|
str.text++;
|
||||||
|
str.len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_inline bool string_has_extension(String str, String ext) {
|
||||||
|
str = string_trim_whitespace(str);
|
||||||
|
if (str.len <= ext.len+1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
isize len = str.len;
|
||||||
|
for (isize i = len-1; i >= 0; i--) {
|
||||||
|
if (str[i] == '.') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
if (len == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 *s = str.text + len;
|
||||||
|
return gb_memcompare(s, ext.text, ext.len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool string_contains_char(String s, u8 c) {
|
||||||
|
isize i;
|
||||||
|
for (i = 0; i < s.len; i++) {
|
||||||
|
if (s[i] == c)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String filename_from_path(String s) {
|
||||||
|
isize i = string_extension_position(s);
|
||||||
|
if (i > 0) {
|
||||||
|
isize j = 0;
|
||||||
|
s.len = i;
|
||||||
|
for (j = i-1; j >= 0; j--) {
|
||||||
|
if (s[j] == '/' ||
|
||||||
|
s[j] == '\\') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.text += j+1;
|
||||||
|
s.len = i-j-1;
|
||||||
|
}
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
|
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
|
||||||
|
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, multibyte_input, input_length, output, output_size);
|
||||||
|
}
|
||||||
|
int convert_widechar_to_multibyte(wchar_t *widechar_input, int input_length, char *output, int output_size) {
|
||||||
|
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
|
||||||
|
|
||||||
|
#include <iconv.h>
|
||||||
|
|
||||||
|
int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
|
||||||
|
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
|
||||||
|
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
|
||||||
|
iconv_close(conv);
|
||||||
|
|
||||||
|
return (int) result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
|
||||||
|
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
|
||||||
|
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
|
||||||
|
iconv_close(conv);
|
||||||
|
|
||||||
|
return (int) result;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error Implement system
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO(bill): Make this non-windows specific
|
||||||
|
String16 string_to_string16(gbAllocator a, String s) {
|
||||||
|
int len, len1;
|
||||||
|
wchar_t *text;
|
||||||
|
|
||||||
|
if (s.len < 1) {
|
||||||
|
return make_string16(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, nullptr, 0);
|
||||||
|
if (len == 0) {
|
||||||
|
return make_string16(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
text = gb_alloc_array(a, wchar_t, len+1);
|
||||||
|
|
||||||
|
len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len);
|
||||||
|
if (len1 == 0) {
|
||||||
|
gb_free(a, text);
|
||||||
|
return make_string16(nullptr, 0);
|
||||||
|
}
|
||||||
|
text[len] = 0;
|
||||||
|
|
||||||
|
return make_string16(text, len-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String string16_to_string(gbAllocator a, String16 s) {
|
||||||
|
int len, len1;
|
||||||
|
u8 *text;
|
||||||
|
|
||||||
|
if (s.len < 1) {
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0);
|
||||||
|
if (len == 0) {
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
len += 1; // NOTE(bill): It needs an extra 1 for some reason
|
||||||
|
|
||||||
|
text = gb_alloc_array(a, u8, len+1);
|
||||||
|
|
||||||
|
len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len);
|
||||||
|
if (len1 == 0) {
|
||||||
|
gb_free(a, text);
|
||||||
|
return make_string(nullptr, 0);
|
||||||
|
}
|
||||||
|
text[len] = 0;
|
||||||
|
|
||||||
|
return make_string(text, len-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
|
||||||
|
u8 c;
|
||||||
|
|
||||||
|
if (s[0] == quote &&
|
||||||
|
(quote == '\'' || quote == '"')) {
|
||||||
|
return false;
|
||||||
|
} else if (s[0] >= 0x80) {
|
||||||
|
Rune r = -1;
|
||||||
|
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||||
|
*rune = r;
|
||||||
|
*multiple_bytes = true;
|
||||||
|
*tail_string = make_string(s.text+size, s.len-size);
|
||||||
|
return true;
|
||||||
|
} else if (s[0] != '\\') {
|
||||||
|
*rune = s[0];
|
||||||
|
*tail_string = make_string(s.text+1, s.len-1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.len <= 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
c = s[1];
|
||||||
|
s = make_string(s.text+2, s.len-2);
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
default: return false;
|
||||||
|
|
||||||
|
case 'a': *rune = '\a'; break;
|
||||||
|
case 'b': *rune = '\b'; break;
|
||||||
|
case 'f': *rune = '\f'; break;
|
||||||
|
case 'n': *rune = '\n'; break;
|
||||||
|
case 'r': *rune = '\r'; break;
|
||||||
|
case 't': *rune = '\t'; break;
|
||||||
|
case 'v': *rune = '\v'; break;
|
||||||
|
case '\\': *rune = '\\'; break;
|
||||||
|
|
||||||
|
|
||||||
|
case '\'':
|
||||||
|
case '"':
|
||||||
|
if (c != quote) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rune = c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7': {
|
||||||
|
isize i;
|
||||||
|
i32 r = gb_digit_to_int(c);
|
||||||
|
if (s.len < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
i32 d = gb_digit_to_int(s[i]);
|
||||||
|
if (d < 0 || d > 7) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
r = (r<<3) | d;
|
||||||
|
}
|
||||||
|
s = make_string(s.text+2, s.len-2);
|
||||||
|
if (r > 0xff) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rune = r;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
case 'u':
|
||||||
|
case 'U': {
|
||||||
|
Rune r = 0;
|
||||||
|
isize i, count = 0;
|
||||||
|
switch (c) {
|
||||||
|
case 'x': count = 2; break;
|
||||||
|
case 'u': count = 4; break;
|
||||||
|
case 'U': count = 8; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.len < count) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
i32 d = gb_hex_digit_to_int(s[i]);
|
||||||
|
if (d < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
r = (r<<4) | d;
|
||||||
|
}
|
||||||
|
s = make_string(s.text+count, s.len-count);
|
||||||
|
if (c == 'x') {
|
||||||
|
*rune = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r > GB_RUNE_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*rune = r;
|
||||||
|
*multiple_bytes = true;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
*tail_string = s;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 0 == failure
|
||||||
|
// 1 == original memory
|
||||||
|
// 2 == new allocation
|
||||||
|
i32 unquote_string(gbAllocator a, String *s_) {
|
||||||
|
String s = *s_;
|
||||||
|
isize n = s.len;
|
||||||
|
u8 quote;
|
||||||
|
if (n < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
quote = s[0];
|
||||||
|
if (quote != s[n-1]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s.text += 1;
|
||||||
|
s.len -= 2;
|
||||||
|
|
||||||
|
if (quote == '`') {
|
||||||
|
if (string_contains_char(s, '`')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*s_ = s;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (quote != '"' && quote != '\'') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string_contains_char(s, '\n')) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
||||||
|
if (quote == '"') {
|
||||||
|
*s_ = s;
|
||||||
|
return 1;
|
||||||
|
} else if (quote == '\'') {
|
||||||
|
Rune r = GB_RUNE_INVALID;
|
||||||
|
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||||
|
if ((size == s.len) && (r != -1 || size != 1)) {
|
||||||
|
*s_ = s;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
u8 rune_temp[4] = {};
|
||||||
|
isize buf_len = 3*s.len / 2;
|
||||||
|
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
||||||
|
isize offset = 0;
|
||||||
|
while (s.len > 0) {
|
||||||
|
String tail_string = {};
|
||||||
|
Rune r = 0;
|
||||||
|
bool multiple_bytes = false;
|
||||||
|
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
||||||
|
if (!success) {
|
||||||
|
gb_free(a, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
s = tail_string;
|
||||||
|
|
||||||
|
if (r < 0x80 || !multiple_bytes) {
|
||||||
|
buf[offset++] = cast(u8)r;
|
||||||
|
} else {
|
||||||
|
isize size = gb_utf8_encode_rune(rune_temp, r);
|
||||||
|
gb_memmove(buf+offset, rune_temp, size);
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote == '\'' && s.len != 0) {
|
||||||
|
gb_free(a, buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*s_ = make_string(buf, offset);
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user