mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 19:21:37 -07:00
Compare commits
2205 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31989c93ac | |||
| 7b0ba76915 | |||
| bd8ca64a30 | |||
| 32fda798f3 | |||
| 12895de9ac | |||
| 0216ade2f9 | |||
| 14e6cdb1a0 | |||
| 4daf098a3a | |||
| 914c99a15e | |||
| d31c63c0ae | |||
| c783840eab | |||
| d3eca21e40 | |||
| df3690c32a | |||
| 999d1a022d | |||
| 1f2f3cb315 | |||
| 8de70ce73d | |||
| 3820f27c7c | |||
| 7309cfdbb2 | |||
| ea19f3e77f | |||
| ae2fc5830e | |||
| 3946f357e8 | |||
| b9b2b90f0c | |||
| a6a7395be7 | |||
| 16b50a2f57 | |||
| e7f54d25d6 | |||
| 033b46def8 | |||
| 1f571f48e5 | |||
| ec178825ec | |||
| 6158a49618 | |||
| 674aeffee4 | |||
| 546759bdef | |||
| fa903fb4df | |||
| 5164d1d866 | |||
| b6e33a1e64 | |||
| f5248a8d9d | |||
| 74ed779616 | |||
| 9f24188ec8 | |||
| 5551404be4 | |||
| 9fd35776fd | |||
| 3385fcecb0 | |||
| 804b96a985 | |||
| 9cc20954a3 | |||
| 91ff3e5bca | |||
| 6d032e6f1a | |||
| 16abfd56e8 | |||
| d1d5f61230 | |||
| 5b7e1cd6f6 | |||
| cbfe3571ab | |||
| b04bc21ec6 | |||
| 2945feecde | |||
| 36cac87387 | |||
| a7e38dc063 | |||
| 0aaab84938 | |||
| 3a1492fc99 | |||
| ca818fb857 | |||
| 13e5cb8cc4 | |||
| 7ae54ae3b4 | |||
| b2beb9512f | |||
| 96ad6d2084 | |||
| 86f1574f78 | |||
| 6565a49e34 | |||
| ce85b73eca | |||
| fc65aee307 | |||
| ede135a08f | |||
| c18fc2da9f | |||
| c8a3937ea0 | |||
| f9a6777e3a | |||
| 83eabe2140 | |||
| c4067372dd | |||
| b4e976364a | |||
| 642afa4f88 | |||
| f65fa0e4a6 | |||
| 65b9dbe13f | |||
| cb52f6986a | |||
| 94ba182691 | |||
| 5b7c83d871 | |||
| 4059542afb | |||
| 9da1347c21 | |||
| a8c10c58b4 | |||
| 6d9b2ec5b4 | |||
| d749f5c704 | |||
| 83f553cd89 | |||
| 730f9ee0b3 | |||
| 51e50d3e31 | |||
| 1b2cc739a9 | |||
| 7bdd8094d6 | |||
| 323fc7a6a9 | |||
| 92363da58e | |||
| 0ea64182f1 | |||
| 8478b887a5 | |||
| 86448ee044 | |||
| 56a52a1d06 | |||
| 858c5f8fd8 | |||
| f92b4c7849 | |||
| 9fdebebd28 | |||
| 2b18f43b65 | |||
| 53e1512978 | |||
| b5f9c95ce7 | |||
| 231f91304a | |||
| d7b3f3a0e7 | |||
| bb81d4869f | |||
| b633a42bc2 | |||
| 6bd05ef5d7 | |||
| 251a3a690e | |||
| f22b014db0 | |||
| a6edcf4f18 | |||
| 2a598aa061 | |||
| 910ab7b3d4 | |||
| 2b27300387 | |||
| 0db1ebb4e5 | |||
| 0013033f9a | |||
| f00123742c | |||
| 509e8b512f | |||
| 2562df5387 | |||
| 0ab356aa4e | |||
| b3c51a8b44 | |||
| fb3aeccd36 | |||
| 9495e3d10c | |||
| 0f711b8719 | |||
| 2a6130b7e1 | |||
| 34384cc2f1 | |||
| aeafed0218 | |||
| 3a4bbfcfae | |||
| 01c84b32a6 | |||
| 240fc65d4d | |||
| c9d3b95b0d | |||
| 9f596d6f34 | |||
| 2a684830f9 | |||
| c4ba3f1c83 | |||
| 724c776dbe | |||
| ad8048b615 | |||
| 781395ada1 | |||
| 9635ea8706 | |||
| 413188bab2 | |||
| 5fa54fd2cc | |||
| b79e1b6b5c | |||
| 8dff0f7ef5 | |||
| f85f3dce12 | |||
| 5edb1e8a28 | |||
| f70939ab4f | |||
| 08e271720a | |||
| c3ebc49ad2 | |||
| 9cccb20f49 | |||
| 6985d72fda | |||
| 0056cdffa7 | |||
| a229f9825b | |||
| a6ff48a5de | |||
| 6648dc9ed1 | |||
| bbbf7168f1 | |||
| c15ed44f82 | |||
| 58466a6f3b | |||
| be76c860a5 | |||
| 4533c02cc7 | |||
| 01d12770fa | |||
| 82b559c32b | |||
| 474d79dcf1 | |||
| 61db6c1234 | |||
| e641d714a0 | |||
| f305726015 | |||
| 57b09b2ffb | |||
| e86fde3cb1 | |||
| 99944f3b02 | |||
| a9295d33ab | |||
| 4acae2af44 | |||
| 036429bf2f | |||
| d0920804c3 | |||
| 97d3d4ff6f | |||
| 6ea0910213 | |||
| b8d33165c9 | |||
| 6b3ee447f0 | |||
| 9b1cc6e94f | |||
| 0ffb718a91 | |||
| a7dd686859 | |||
| 87a6d695d6 | |||
| a89633e3ed | |||
| 59a0bbb385 | |||
| a3fa647bfd | |||
| 1a4e2196bd | |||
| d8f9daac95 | |||
| f992e36f9a | |||
| 11dd971e13 | |||
| 5ed4bac16f | |||
| 04ceb5d20c | |||
| 0b67de47d6 | |||
| 15c4077806 | |||
| 37a3abdaaa | |||
| ac709b8afb | |||
| d80049bfd2 | |||
| 239f3c0418 | |||
| 5b11a842a8 | |||
| 626b4740b1 | |||
| 437d5e28cd | |||
| 6c7fc4212a | |||
| 8589af1458 | |||
| bf5ce04b24 | |||
| 8057af9e09 | |||
| d8bc2030e6 | |||
| 5eaef091e2 | |||
| 84fd40de77 | |||
| 1d7f99cbdf | |||
| 3d4a3730b0 | |||
| 1f31d573e4 | |||
| 237962182b | |||
| e84406a895 | |||
| 1a0614b0d7 | |||
| 876820789e | |||
| 4e21a4d46a | |||
| 6ac0fb80a6 | |||
| 098699103d | |||
| d6bcc25b69 | |||
| a2c50d3666 | |||
| f06efffe22 | |||
| e42f7008fc | |||
| 26c9d17040 | |||
| 2be87169ef | |||
| 732c745bb3 | |||
| c035fc337f | |||
| cbfbff7240 | |||
| 99e6eba20f | |||
| 7d11ee605c | |||
| 4671207c61 | |||
| aa029fe8d9 | |||
| ef539696b9 | |||
| 26fe9b0212 | |||
| fd6e2ed5de | |||
| 7bd1039a49 | |||
| 76a230372f | |||
| 86b613fb15 | |||
| aacf524a47 | |||
| 8ad3a1f41f | |||
| d09ac8943a | |||
| 8e63c94393 | |||
| 0b16ed7c85 | |||
| 89d824216a | |||
| 3f23a0b3b0 | |||
| 7819fec0a1 | |||
| 96ed948590 | |||
| 3bd01d3a02 | |||
| c4b492fb64 | |||
| e1bdaa981a | |||
| 95e8668b77 | |||
| 90adf214d7 | |||
| ff92eb9112 | |||
| 5f08c77d8b | |||
| dc236d6830 | |||
| 8b066b2456 | |||
| 218c1599b1 | |||
| 9b2eecb3df | |||
| e0a242e9a1 | |||
| 48946fe46f | |||
| 0b0e661b62 | |||
| 10c3acf37f | |||
| e377f5a329 | |||
| cd4403be0c | |||
| f661d34049 | |||
| af1d4d6e72 | |||
| f6c7a0c9b8 | |||
| c2bfb221f5 | |||
| d59fced21b | |||
| 7c42d4ba75 | |||
| 14ce6d8ed8 | |||
| 2630e9ced1 | |||
| 482c687462 | |||
| de8c1165c2 | |||
| d51b98a8d2 | |||
| 6861ff47bc | |||
| 0ba3b5c0bd | |||
| fcdfcfce19 | |||
| d49ecd9009 | |||
| 72a5030f3d | |||
| 197a72adde | |||
| f043e92650 | |||
| e3f3e715e2 | |||
| e8f2fb58d9 | |||
| 8d2430e54d | |||
| dc1b3cc563 | |||
| d52695b077 | |||
| 6adbdb1e0e | |||
| 7d4f9545a7 | |||
| 4fc60601d3 | |||
| 4f4c3bb03b | |||
| 8434cb8951 | |||
| 99ebb5af3b | |||
| 45274868c3 | |||
| 3ef9566817 | |||
| 3299d6a204 | |||
| e27f5796d6 | |||
| ba4363d678 | |||
| eb2b3572bb | |||
| 1a9e75267b | |||
| dd0fb744fe | |||
| 70a66cd559 | |||
| 832a586b8d | |||
| 117ade0700 | |||
| 25f77e32ee | |||
| 704ee9f851 | |||
| 190932935c | |||
| a10d180d81 | |||
| c704de8442 | |||
| f63b9806d2 | |||
| 9409f53a9b | |||
| 9faf292218 | |||
| 92f5f86193 | |||
| 1e711fa5a5 | |||
| 51b346753a | |||
| b63aa0520a | |||
| 04e106e06a | |||
| 5d42a6de6e | |||
| bb3e0fa03f | |||
| e7e936f480 | |||
| 026bb8ed6f | |||
| cbc3800797 | |||
| 4236e870d7 | |||
| b725ae5ae0 | |||
| 3afa2736b7 | |||
| 8dd1b61aa2 | |||
| f5a1d8f2b5 | |||
| d3f2f94800 | |||
| f141e2868d | |||
| 400d6014d0 | |||
| ab6947b2c7 | |||
| 52bbdefec4 | |||
| 8ee67e41f4 | |||
| 0af2b38225 | |||
| 97f7a558fa | |||
| ab7c75860e | |||
| 680d723c77 | |||
| 8eda24f2d1 | |||
| 3dac1c34fa | |||
| 7fddac2c36 | |||
| a55975bd5a | |||
| 2c91c21021 | |||
| 3a1bee19a9 | |||
| 872e97dba6 | |||
| 705984f828 | |||
| aa620e8ea1 | |||
| 1addee32b5 | |||
| 92402603b9 | |||
| 4438b3e7af | |||
| 602a651613 | |||
| d0cee15317 | |||
| df5626cc1f | |||
| 2dcc986c4c | |||
| 550df8711f | |||
| 1e321cd48c | |||
| 020856d91a | |||
| 1bd0e09ae1 | |||
| dbaf8568d6 | |||
| 802a776330 | |||
| 9170c875e1 | |||
| 5002b71670 | |||
| f229084baa | |||
| f09b6a4c90 | |||
| 65a2125dba | |||
| 9e698b720f | |||
| 5157619eb7 | |||
| 90593fe6ae | |||
| 16b4178b8a | |||
| 8f2b848698 | |||
| 9655b61c11 | |||
| 9b9a4fcf22 | |||
| a736d0e83f | |||
| 7ba339e6bd | |||
| baf5b9edc3 | |||
| a615402d7c | |||
| c9bec10a8e | |||
| df80e8752b | |||
| 2df0532b17 | |||
| 62dc99dbef | |||
| b925ad5927 | |||
| 090579d6b5 | |||
| 29a3cb25d3 | |||
| 7ff690500a | |||
| d0b913dad1 | |||
| d659e679fd | |||
| ae97c1111a | |||
| f38d7b02f3 | |||
| 5e706bab56 | |||
| b362ce9a22 | |||
| 9961ad8e48 | |||
| b54b5aabac | |||
| d214c45fe5 | |||
| 3f638f92e2 | |||
| b0d668d254 | |||
| 488282409f | |||
| d3c2191cf7 | |||
| dd13cf637e | |||
| 0804be5d81 | |||
| 3c189d2cf6 | |||
| c83592629d | |||
| 2e3706e447 | |||
| 1524852ffc | |||
| 957e6f7f08 | |||
| 1b3ee7153c | |||
| bda5e8cc66 | |||
| 6d6f8f8da9 | |||
| dca6c451da | |||
| a6c8dcdd21 | |||
| b98a4c6d69 | |||
| 4be385d648 | |||
| 6bbecbe895 | |||
| b21993a1c4 | |||
| dd69fcba07 | |||
| 7909872877 | |||
| 0a920b5439 | |||
| 921ee82c97 | |||
| b7893082ce | |||
| 6bfe9b6656 | |||
| da703edbf4 | |||
| 796331fea6 | |||
| 820095ddac | |||
| d57fbf48f0 | |||
| 53c842e9ba | |||
| 1e375ba8de | |||
| 5cbb266ef5 | |||
| 0730e01b24 | |||
| dfc63dcb60 | |||
| 8093062e3b | |||
| 9524739dfc | |||
| 054e018e23 | |||
| 3d81ad46d2 | |||
| ed4d21045b | |||
| 93955a0fd8 | |||
| fc0002ab67 | |||
| 04fe23a3c8 | |||
| 1707e004ec | |||
| 5169dc07c7 | |||
| 18fb6a4be4 | |||
| 8dba0e332c | |||
| 3951b93d0a | |||
| 10bac2445b | |||
| 06e364b9bd | |||
| ce90509a07 | |||
| a0d0e93475 | |||
| 2ce1f4ba9f | |||
| a985449c31 | |||
| 6abc93ad84 | |||
| a9bc07dbff | |||
| da283d5a7f | |||
| 1181d7cf90 | |||
| 2a2d3273ea | |||
| 775e6caf31 | |||
| 4468ddf8f8 | |||
| bf0c6f5a30 | |||
| d1e670335f | |||
| c6c6c56ba9 | |||
| d4e95282c2 | |||
| 5a02ebe2c8 | |||
| dae817e5ab | |||
| 28502ba53b | |||
| 8dc74a004c | |||
| c584456a21 | |||
| c74d8405ec | |||
| e0a370f8f1 | |||
| 4cf70f360b | |||
| a83d9f59f6 | |||
| 5d14189a18 | |||
| fb686bdebd | |||
| 19b9cb7524 | |||
| f92334a769 | |||
| 4ee936ab8d | |||
| 0c21939600 | |||
| f6f2ab2f25 | |||
| 9d163fede8 | |||
| bb026c99a9 | |||
| 8d2ad0da0e | |||
| db7a3ffd2a | |||
| c213d72ec6 | |||
| 01b1385672 | |||
| e92fdb4a99 | |||
| 2817bab494 | |||
| 7d93dd6024 | |||
| e1da631d26 | |||
| 6151fdb324 | |||
| 2fe0eaf2ad | |||
| 85f2f4aa88 | |||
| 3f63e12198 | |||
| 10cde925ca | |||
| 56240240f6 | |||
| f83e1b8b0a | |||
| a27c68f526 | |||
| 8ec5987ae1 | |||
| 408fa027af | |||
| 5e903ed2ff | |||
| ce20604e3c | |||
| 92e1c71dd6 | |||
| 0190f90979 | |||
| 3d74c2f6c0 | |||
| 1596bca92d | |||
| 470508adbc | |||
| 8f42958ba3 | |||
| 4d7270cec9 | |||
| b13423d7f7 | |||
| 703404a54d | |||
| 15f5c85379 | |||
| e197af766d | |||
| 10fe5e97b3 | |||
| 5073fcd39e | |||
| a72ac6f841 | |||
| 8a67775149 | |||
| 85e331d5e2 | |||
| 81b00c7a3e | |||
| 49ecd73406 | |||
| 2180f4a475 | |||
| 1f0c1943da | |||
| 0b7711684b | |||
| 9821b2be25 | |||
| 49b42f585c | |||
| 9d5692ae68 | |||
| bfda101686 | |||
| 6d67567453 | |||
| 35711a400c | |||
| 66da96284a | |||
| 7d9600b740 | |||
| 0c09cb9c12 | |||
| 09e1cf0737 | |||
| 992858b687 | |||
| b555b0083a | |||
| 25feb507a4 | |||
| 0b299cb8b4 | |||
| fbe2366af3 | |||
| 391e2a5120 | |||
| 0103cedad7 | |||
| d56807095a | |||
| 5dc82c2720 | |||
| 5f1b397a05 | |||
| 6ed6a91a64 | |||
| 0f399a7294 | |||
| 4bcb667e97 | |||
| 7e6454b2f7 | |||
| 357e72654d | |||
| e9f3ebba13 | |||
| 3a30f9fd71 | |||
| 7a4ec48389 | |||
| a101e0d7ba | |||
| 64f5c9ab58 | |||
| 0ee5a07d01 | |||
| d658a8fca5 | |||
| abe8789890 | |||
| a4b60b78c8 | |||
| 66d1656367 | |||
| 14c4fed94c | |||
| c3205316ed | |||
| b542ef273d | |||
| 404132de17 | |||
| cd43f4c94c | |||
| 23ff98dea0 | |||
| c3a8e232a5 | |||
| 7f89f6b582 | |||
| 159150c6d9 | |||
| 527b39ce2b | |||
| 5af3c7b0e8 | |||
| 5db4bd9944 | |||
| 20b410f149 | |||
| ae7cbd5171 | |||
| 04f7225ea5 | |||
| f0c6f29f82 | |||
| 7d9a9a2283 | |||
| ba85e432e7 | |||
| cfba29002a | |||
| 11c7b6a2e4 | |||
| 47f9876b36 | |||
| ff31f9a900 | |||
| ebc4867514 | |||
| e1ccba3de5 | |||
| 28570f8fa4 | |||
| 24bd370e1b | |||
| 3e67ae7339 | |||
| 0e52c37865 | |||
| d520b9a1ba | |||
| 5c7d6fcfd0 | |||
| 5ae924f988 | |||
| cae1e02593 | |||
| b09297da81 | |||
| 9abdfaaf6c | |||
| b32ef9e47b | |||
| b8324b0776 | |||
| d0ca045586 | |||
| 673879d1d2 | |||
| 5d1c9583cb | |||
| d017b5de9d | |||
| 93ead4bcb3 | |||
| bbe9b4dee0 | |||
| 3bd00fd6b7 | |||
| 83fec387d4 | |||
| f6f10d10e8 | |||
| 16a7c55334 | |||
| e9e2ab240d | |||
| 842281ddd3 | |||
| 978d7fcb99 | |||
| b267a5964d | |||
| b288613307 | |||
| 4591353724 | |||
| 13107628f8 | |||
| c407687a4c | |||
| 5a50ab7a99 | |||
| 4578544007 | |||
| bdfef08214 | |||
| 42678848b2 | |||
| ab52f8d795 | |||
| d79ee7d530 | |||
| 7e271310ff | |||
| f24de51c65 | |||
| 2252d992d7 | |||
| 2d70a784d1 | |||
| a8a4dc1eb1 | |||
| 9e9e905431 | |||
| 8ee41c20af | |||
| 11c705508d | |||
| 267ae0b4a2 | |||
| 1bc6e6a7cc | |||
| 33a458c520 | |||
| 6a7ccd8c0a | |||
| 9ba2926e7e | |||
| a50b2d5d04 | |||
| 7f9626e5c7 | |||
| 7140c95c55 | |||
| ceef5db547 | |||
| 5ec8dd166a | |||
| 80a32a8182 | |||
| c2b3056094 | |||
| f99f351e01 | |||
| 880c7f01a8 | |||
| 10f0961184 | |||
| eea403d0ab | |||
| 2cc5c4eed3 | |||
| f308d8d73e | |||
| ff0bc3ccad | |||
| 072979c6d2 | |||
| a3d2c40da0 | |||
| 5b1312342e | |||
| 85e31e1b69 | |||
| fb0fb4767b | |||
| 38a9a2b7fc | |||
| 005c6af302 | |||
| 1d14b3059e | |||
| cc2fa8f756 | |||
| d1c9fd4e01 | |||
| 4593730632 | |||
| f62a0891bd | |||
| 4f2d4716ad | |||
| 022b793a7d | |||
| 7267004a55 | |||
| 786c9dfe07 | |||
| 81b24594ce | |||
| 995ba0df9a | |||
| 08392d885e | |||
| d462dbb5be | |||
| 19c32ecb81 | |||
| 494b1e7eaa | |||
| c43d17bfec | |||
| c9723e2dc0 | |||
| 4ba579bc25 | |||
| 58d4d424c6 | |||
| 89ccb5b99f | |||
| 7f5021c8e9 | |||
| 8bec324779 | |||
| e6f26b9931 | |||
| b8c534eba9 | |||
| 95d3f43e15 | |||
| 2d97e1dee3 | |||
| be2dfd42fd | |||
| 851118faf4 | |||
| 53cd7a3d0c | |||
| f170648629 | |||
| 42def957d5 | |||
| 6433a0d31e | |||
| 359e5d9e15 | |||
| e229885b2b | |||
| ee78374281 | |||
| ebe152a155 | |||
| 46582a45bd | |||
| 6b5ea011e7 | |||
| 9503440eb0 | |||
| 3fa4c5043a | |||
| 9db81498d8 | |||
| 7fbe0a6f23 | |||
| 3fd5c3cd85 | |||
| 99121d6ff2 | |||
| 0c0c83ee29 | |||
| b75d59d6e0 | |||
| 71c8a3456e | |||
| 37e3e081c6 | |||
| 5ea9fc3fb0 | |||
| 53f65224f6 | |||
| 902d313c6a | |||
| 45d844f9d2 | |||
| 9b58781122 | |||
| b74f8f2047 | |||
| 88c90cf99a | |||
| 321dcc60e3 | |||
| 400f12f31f | |||
| 2c5a84bb78 | |||
| e01d8a04a9 | |||
| 69afa33fa5 | |||
| 44e0e96612 | |||
| 0839dccfdc | |||
| 2484f4d04b | |||
| d22e5b697d | |||
| 301ee664e9 | |||
| 9b4d4a2c61 | |||
| d9647174a3 | |||
| 4d29b64196 | |||
| 8be1b2e1b1 | |||
| b85258a9fc | |||
| 07897ed78e | |||
| 8115386217 | |||
| f8dd4816ff | |||
| 928a445a14 | |||
| 42cd78497a | |||
| 813f5d211f | |||
| 71d129a709 | |||
| c35762528c | |||
| aa796a1032 | |||
| 5bf71ba75c | |||
| 967981aacd | |||
| 33d05a07de | |||
| 8da8301b09 | |||
| 536cceeef9 | |||
| 098684a6fe | |||
| 694ee02247 | |||
| 0451c88ab6 | |||
| 2ef7bfc06e | |||
| 7bfdb4f9f4 | |||
| 0a35b13411 | |||
| 803f6a6651 | |||
| 672cfd51c3 | |||
| 80cdf8b6a8 | |||
| dc2d5239c5 | |||
| dacfc9de15 | |||
| 689aa4d734 | |||
| 8a46b493fd | |||
| 86abdc0603 | |||
| a634444f99 | |||
| 04a25b11ad | |||
| 40546fbde2 | |||
| c1176c2bcb | |||
| 57853fe1b1 | |||
| 013b3b9fb3 | |||
| dc0f04e53b | |||
| 40606d9272 | |||
| ebf7926fa4 | |||
| dfb3101ecf | |||
| e3d3a81617 | |||
| dbdbbcd60f | |||
| f9aaff99c6 | |||
| 4b718aae75 | |||
| 20db0e7f09 | |||
| 614ea5c168 | |||
| 57565b78a6 | |||
| b5b085914a | |||
| 044e64beb0 | |||
| adca5b57cd | |||
| 3b898e5224 | |||
| 153e7525b5 | |||
| 44a303e577 | |||
| d63878d0dd | |||
| a20c31d6b5 | |||
| 560bdc339b | |||
| 01dfb1dac8 | |||
| ee8d3e03f8 | |||
| 4aad45e3e7 | |||
| fe5c642d9f | |||
| b5fdb3f855 | |||
| 416ff149bd | |||
| 233c6e2d3a | |||
| a29a6d9285 | |||
| a7a31e4c23 | |||
| 72cad591bd | |||
| 684bf7aa61 | |||
| d760b95e4f | |||
| 5e81fc72b9 | |||
| 0977ac111a | |||
| 2db16d6a32 | |||
| 5aa46d31b2 | |||
| 14e8b299b7 | |||
| c7cb754514 | |||
| a5e42a0465 | |||
| d808f9eccf | |||
| 1734eb949f | |||
| 7fae890ef9 | |||
| 94879ed149 | |||
| 2c75fe2314 | |||
| 1da0668653 | |||
| c60fb10a6a | |||
| 7140f42915 | |||
| ad92fbfd4e | |||
| 1416946757 | |||
| 6a8c4ee04c | |||
| 516df9123d | |||
| 818d6dbbea | |||
| 286c5b7b24 | |||
| c0e8113f6f | |||
| e15dfa8eb6 | |||
| f12ded54f2 | |||
| b4951c9b39 | |||
| 9f0a28017d | |||
| d7c8a3a9dd | |||
| 10b109e25f | |||
| 2afe4bea67 | |||
| 12ae5ed09e | |||
| 0bdc3b4f21 | |||
| eaabb888d4 | |||
| b53fe14c22 | |||
| 45683703ea | |||
| 03053a18ce | |||
| 2a6d9e8927 | |||
| fa81061db0 | |||
| 39b3c8c80f | |||
| 3139151935 | |||
| 672a8f5dbd | |||
| 8672ff1c55 | |||
| abfa894566 | |||
| 5b52fed268 | |||
| 94a638b436 | |||
| 1b8c3ca22a | |||
| 71b32ae117 | |||
| 939459b635 | |||
| 562b518394 | |||
| d62503d031 | |||
| 4e8a801b35 | |||
| e1b711b3b3 | |||
| 6c69e8c043 | |||
| 7fa2d25eea | |||
| dae514a2c9 | |||
| 068993a819 | |||
| 66ae4e5afc | |||
| 218d1131e8 | |||
| f4f6e9ad49 | |||
| 48ab7f876c | |||
| 1e53a6fa96 | |||
| 4cef160c87 | |||
| 68582c5ad1 | |||
| a9a2dafca5 | |||
| da3467c25f | |||
| 5fc42bf9c9 | |||
| 42bbd31df1 | |||
| c7cc38b7d8 | |||
| 4afc78efc6 | |||
| bc34083c9c | |||
| 08dd8414c1 | |||
| d54255505a | |||
| d4914c3546 | |||
| 772c8779fa | |||
| c92b2e9612 | |||
| 1af143b749 | |||
| 1370c10d1b | |||
| 1348d8a8cd | |||
| 495aaacb81 | |||
| 22e982c8fb | |||
| 6d614ef07c | |||
| 723f351a6d | |||
| c93872cc13 | |||
| 97dece15d7 | |||
| 657103c4cf | |||
| b9d3129fb3 | |||
| b311540b16 | |||
| 07ced1cf0e | |||
| a1d4ea7718 | |||
| f921a91fc8 | |||
| 4dade34603 | |||
| d76249d90b | |||
| d118fc569a | |||
| 2dc39a5cbd | |||
| c89fc35e94 | |||
| 614d209824 | |||
| f1a7b31209 | |||
| 6a8b3fee38 | |||
| 4551521b2c | |||
| 97dfcffa76 | |||
| 6d3feb4531 | |||
| c44d25d14f | |||
| 25dd00cd0b | |||
| 01c10aa944 | |||
| 4908d1ebdd | |||
| 7bc146e6fd | |||
| 59ab51acec | |||
| cf23954297 | |||
| d1cc6534cd | |||
| 1b454c7ded | |||
| 150d4e343d | |||
| 28ada801a0 | |||
| a7676bff6e | |||
| 4369298e96 | |||
| a58c29582e | |||
| 3ad20a2d2d | |||
| b86dfa7af7 | |||
| 980890ee8a | |||
| 0a63690b39 | |||
| 0076a4df62 | |||
| 4c065a7e99 | |||
| 04036aba9c | |||
| b08aa857b3 | |||
| 2d26278a65 | |||
| 27a3c5449a | |||
| 9c63212824 | |||
| 65d41d4248 | |||
| b04231dd95 | |||
| 37633c1d2a | |||
| 5877017d30 | |||
| 132fdf14b8 | |||
| e7d3001dd1 | |||
| f163181204 | |||
| 2c5c8192f8 | |||
| 162c87b1b8 | |||
| 77734ea967 | |||
| 912fc2890b | |||
| 14059583cd | |||
| f3bffb9810 | |||
| 540730c0be | |||
| 40f0e74b8c | |||
| 5ca0cd60d0 | |||
| 96f0a08725 | |||
| d85893954d | |||
| d26033eb23 | |||
| c7a70be824 | |||
| 08c490d9ac | |||
| 8ee7ee7120 | |||
| d471a59041 | |||
| f25818e923 | |||
| 3d531be711 | |||
| 56d365a4e7 | |||
| 308300c1fc | |||
| 927d6814f2 | |||
| 7c99f52187 | |||
| 4ab9edeb53 | |||
| c5b3d7a736 | |||
| d7172e168e | |||
| d99ffe604f | |||
| b77c79294c | |||
| 8e722274f0 | |||
| ebe7fc23a5 | |||
| 4d40f564ef | |||
| fd62959bf4 | |||
| 8b8cada33e | |||
| aaa24894b6 | |||
| 2af19c496e | |||
| fea34b32ea | |||
| b891c0feab | |||
| 9f039c323e | |||
| b0dd3ac599 | |||
| b38a8cfb12 | |||
| 4c79b52867 | |||
| 6dba05b00d | |||
| 32a29d627a | |||
| caf9bc6be9 | |||
| 654740d5b1 | |||
| b894e2b378 | |||
| c40acd008e | |||
| 3d2279fba0 | |||
| 2b080dbbc2 | |||
| 9cadd58465 | |||
| 65e9b4d5f0 | |||
| fb3d73cb20 | |||
| 222941727f | |||
| 5697d6df74 | |||
| 426c1ed6f4 | |||
| 458ec5922e | |||
| f5fdd031f9 | |||
| ceb58ae04f | |||
| 868603f617 | |||
| d2faa9bef1 | |||
| b1663a14e9 | |||
| 3fc60930e6 | |||
| 665734f04f | |||
| 16f3bc2c0b | |||
| 71a733e3b5 | |||
| a66612e8ae | |||
| 00c0ce45b3 | |||
| ab0afa548b | |||
| ea1690b7a1 | |||
| a5ff983266 | |||
| a46a1f5f34 | |||
| 40135cbc66 | |||
| c61fd3a70a | |||
| 45fbc4e8c5 | |||
| 9ce8f124bb | |||
| 63bbb9b62f | |||
| 56c4039e72 | |||
| 0755dfbd5d | |||
| 2780f82f30 | |||
| 5dcb5c2ba3 | |||
| 9d552d04ea | |||
| 88e1b93786 | |||
| 62f5eb5bca | |||
| 6ab471ff87 | |||
| 155b138aa4 | |||
| a730b04bf1 | |||
| 957e1e1f07 | |||
| 133f88406f | |||
| ecd2eacd75 | |||
| 2614830c69 | |||
| 381fbd3daf | |||
| dd9113786c | |||
| 1354f53d02 | |||
| 564e85ee29 | |||
| ef04d13337 | |||
| 68d4bde82f | |||
| a019059975 | |||
| 7580ec494b | |||
| a9b20c29b1 | |||
| 76a2807b56 | |||
| 14ff561f6c | |||
| 1fd677b42e | |||
| 6b18b90222 | |||
| 9e6d488063 | |||
| 4a15689776 | |||
| c785c3569f | |||
| e6f9b4fb11 | |||
| b978959fae | |||
| 8b09ab6fe7 | |||
| 2347dca9d9 | |||
| 2ada90e094 | |||
| a137a06b00 | |||
| b1684fe455 | |||
| 886054f0f8 | |||
| 0e1cfa5a0a | |||
| 400558abcd | |||
| d75634ff5e | |||
| 0c04b9398a | |||
| e3d1d1d85c | |||
| b6ea7b7418 | |||
| 7426f3b092 | |||
| 299c299dff | |||
| 290c111206 | |||
| 314d5a778e | |||
| dc706d8a6b | |||
| a6fb2dd587 | |||
| 4e93b70f8a | |||
| 1eaa47ebae | |||
| 3a31444656 | |||
| f7efaf2ba2 | |||
| 14c6f2f258 | |||
| 231f3cc15a | |||
| 332e598357 | |||
| 716373836c | |||
| fdb60b2d51 | |||
| 885c5dc8b7 | |||
| 394baa9ddd | |||
| 3d86fc2f2f | |||
| 61b07335d8 | |||
| 712744ef36 | |||
| dbcd49acfc | |||
| 291bf0c143 | |||
| e398c074db | |||
| eadb66c9ef | |||
| 9d7e1c17cc | |||
| 775b544326 | |||
| da26e14959 | |||
| bdab5e00da | |||
| e781056df1 | |||
| b08d944c33 | |||
| 4f24f1172e | |||
| 4446a1431a | |||
| 090937f8af | |||
| d852b0c948 | |||
| 007a7989b8 | |||
| 5c04800831 | |||
| c634d4a96d | |||
| c67ea97845 | |||
| 15d3f4c190 | |||
| 1b3ec66fa2 | |||
| ad3b6ab718 | |||
| 8c618225bf | |||
| 1f5ab0b5f1 | |||
| 1652d5033b | |||
| 9b4b20e8b1 | |||
| 8fb8b5ed7e | |||
| 7bd86bb3ec | |||
| b6d6eb6ae2 | |||
| a126d2ba16 | |||
| 6faab8e47a | |||
| 76a6757ee9 | |||
| 0c8746ada6 | |||
| a0c81c79ad | |||
| cdfaa643cc | |||
| 989cc893ef | |||
| 2878cd8241 | |||
| a9ab90bd24 | |||
| e551d2b25e | |||
| 38ae2e9efa | |||
| 684945ea57 | |||
| 4c51384ad6 | |||
| 64bd884d94 | |||
| a07232ea63 | |||
| 79b585ada8 | |||
| f917935f9d | |||
| 222e5c8ae9 | |||
| dbd0638853 | |||
| 772dc47f55 | |||
| 0777351482 | |||
| 53d8216311 | |||
| 6a0c3d5599 | |||
| 9647fb2a4b | |||
| 42f936742e | |||
| e2d4667639 | |||
| b74d828af7 | |||
| e16409f88a | |||
| 6571f07c7e | |||
| 4b7a09b92e | |||
| 26fb1fa18c | |||
| 24c43d33bb | |||
| 7343f0c7ff | |||
| 3c8cda514b | |||
| bc954df80e | |||
| e1ae359a77 | |||
| c9602953aa | |||
| 0185b43c2f | |||
| bc5c37ebb1 | |||
| 0133dd9034 | |||
| a194aa5a9e | |||
| 766f3e259f | |||
| 6092cc0497 | |||
| fa5d00521b | |||
| d1e29400d3 | |||
| 2dc7aaec82 | |||
| b7242f9d4b | |||
| 7ea7fc10db | |||
| 141da818ba | |||
| dc3d62d437 | |||
| dee28d998f | |||
| 96ef6aa7f3 | |||
| 238a40321a | |||
| 1aea59a0fc | |||
| c6dee52abe | |||
| 1e180d611d | |||
| e452765d28 | |||
| 2b80683fc7 | |||
| 5f840ea2fc | |||
| c72427fd1e | |||
| 44b959648c | |||
| a96bf08266 | |||
| ebaf48c07d | |||
| c197a27185 | |||
| 5ccccf8816 | |||
| 345e790f52 | |||
| fd529b97be | |||
| be1a3488a4 | |||
| 46c610d6e5 | |||
| db2eff6847 | |||
| e047d9eb5e | |||
| 3113e8c892 | |||
| 19e37c852e | |||
| 8fc24fd6f2 | |||
| 493f11521d | |||
| 3363e2c199 | |||
| cf94d1735d | |||
| d9245a6af3 | |||
| d453b9a5b1 | |||
| 5af20aa467 | |||
| cd2c4c02e1 | |||
| 6c21e99832 | |||
| 08598b9425 | |||
| 6295f6747f | |||
| 64f84ef9a3 | |||
| d1b9f3ac74 | |||
| d732a51587 | |||
| 9487f8c92e | |||
| c5def60224 | |||
| ca2220214e | |||
| 6e6a053823 | |||
| 686e0ef3d1 | |||
| 594238a86c | |||
| c60766f8e6 | |||
| 5acea1bceb | |||
| aac643f476 | |||
| 37edbfeb74 | |||
| 51da3e469b | |||
| 9156af2bab | |||
| 3a18ae3978 | |||
| f294c1adee | |||
| bb93a8b131 | |||
| 5bfe5ad82e | |||
| dd28fe6e82 | |||
| cda0f4d8f3 | |||
| 0546b5c218 | |||
| 61a3e50d1b | |||
| 75aeb02c39 | |||
| a32f024d94 | |||
| 37d993c417 | |||
| bcbb59dc11 | |||
| c1ec45dc0a | |||
| 0778d18bc7 | |||
| d7e9b8d374 | |||
| f647187e53 | |||
| 9dabbc2c95 | |||
| 4167168c63 | |||
| aa156e4bfc | |||
| 1c9656aedb | |||
| 8b2f902f3d | |||
| bbece7e910 | |||
| e5f188241c | |||
| 6d3203c11b | |||
| 5ba3d90893 | |||
| 894f267bbf | |||
| e084799b31 | |||
| 3ba3421f5f | |||
| 2bbad5903f | |||
| a240a3d146 | |||
| 775f1e2c95 | |||
| 7c982b6e10 | |||
| cc14180e9d | |||
| b2d40680c8 | |||
| 8662df2b7f | |||
| 6abbc9f1b5 | |||
| 66a9fde12c | |||
| eb5af2876a | |||
| 1f2fdddc6d | |||
| 0bcf53b513 | |||
| 956dd26aa0 | |||
| b504d6e12a | |||
| b4e83a430a | |||
| e3d7e6f76a | |||
| 5c3dc30dc0 | |||
| c508e46ed9 | |||
| 9d85f236b8 | |||
| 3a05a2e562 | |||
| 68384a452f | |||
| 34b6486361 | |||
| 1ce90b2166 | |||
| 9d6666f333 | |||
| d29335ecec | |||
| 95873e66ab | |||
| b7eebe5d00 | |||
| 57d4333ed3 | |||
| 26f11f12ab | |||
| 0b6fc19fb0 | |||
| f2dae7023f | |||
| f36775ffd8 | |||
| 8702a8a477 | |||
| 47e31c3de8 | |||
| b1d0d82254 | |||
| 542e524a87 | |||
| b54c35639b | |||
| cfcb0514bf | |||
| 1a6b7f9945 | |||
| 03957cee64 | |||
| 1584260886 | |||
| a565d842da | |||
| 411d1450b0 | |||
| 984fa1c672 | |||
| 12c810f85d | |||
| 3bf01c8498 | |||
| d05837ab6d | |||
| 4369a1714e | |||
| 13f084a219 | |||
| 4205f0f0b1 | |||
| bd62bceca6 | |||
| ff6ec860b3 | |||
| 2bf60d3337 | |||
| f288614eaf | |||
| 9761d54c24 | |||
| 3794914478 | |||
| 3e11b4fe1e | |||
| 50c3f4d74e | |||
| 304c7594cd | |||
| d02b050850 | |||
| 17b0e3a1a1 | |||
| 28583bfff8 | |||
| b2df48dadb | |||
| 04a853c6fe | |||
| 84f0c975b5 | |||
| 00161023cd | |||
| 7f063eb5e7 | |||
| 784c48c9e3 | |||
| 008d8f25c8 | |||
| 7ffcf34dca | |||
| f3a4904f21 | |||
| 3aec78b1d4 | |||
| a3e6e8d304 | |||
| a747c03f29 | |||
| 2301ae157c | |||
| d3c7d6d485 | |||
| 9b063ad9a3 | |||
| c2f9bf489e | |||
| e496b95881 | |||
| 444f4f446a | |||
| 41ad896f3f | |||
| 0a4b88f9a6 | |||
| 4c2f03b1f2 | |||
| 52dcaeb1e9 | |||
| f96fbc94c8 | |||
| bb62bed981 | |||
| bc6b8c5332 | |||
| 6ab6447791 | |||
| f61c4715c1 | |||
| 3061bc8478 | |||
| d035d48c8e | |||
| b55b1ffe14 | |||
| 620d5d34f7 | |||
| f9654b6c36 | |||
| 6659ceb551 | |||
| 5aa591d884 | |||
| efe91b1f91 | |||
| 7c99884afb | |||
| dfd7a194ed | |||
| 2ddb27869b | |||
| 5c608b01ba | |||
| 2bd85e764e | |||
| 822e4894f2 | |||
| ce2e23849e | |||
| 099995e7dd | |||
| 72f4186b21 | |||
| 3742d9e7e9 | |||
| 4ac1218bf8 | |||
| b171cc41e6 | |||
| efc3a9e69d | |||
| 307c58d908 | |||
| ae02e9c34a | |||
| 139fa55c27 | |||
| 562bb6e4c4 | |||
| ef2931d4a5 | |||
| 2d4aa2be6d | |||
| 42b42db675 | |||
| 73e9dbbf8c | |||
| 0971a59493 | |||
| 627c91124a | |||
| 4eba717281 | |||
| 9623e5e032 | |||
| 805cc48f03 | |||
| d894fb3708 | |||
| b6ca913cff | |||
| 39db428603 | |||
| 992502f03b | |||
| edc3a9392a | |||
| f881ebd007 | |||
| 11ea03d2e8 | |||
| 99b4d59f44 | |||
| dfeefc5179 | |||
| ab46406f4d | |||
| 48ad147818 | |||
| 79d49f1955 | |||
| 1ccc8700e4 | |||
| f38d70a235 | |||
| b37b7a0f72 | |||
| f8d7f42208 | |||
| db0756a119 | |||
| 1a4e25f141 | |||
| 79ade6ac7b | |||
| ecce1d9974 | |||
| 834308d8ce | |||
| 9e73189d63 | |||
| 11bddf270c | |||
| 0818a272e2 | |||
| 9750b1162a | |||
| 3106aaaa3d | |||
| d31d4c9bd6 | |||
| 6993777d36 | |||
| 54c044ee09 | |||
| 2e5cecf9e6 | |||
| 7acb49eefb | |||
| 0f6c1f3482 | |||
| 1ee0fe7457 | |||
| 1a18481d8b | |||
| 28c61c0f5d | |||
| 597fb452b1 | |||
| 5961a63880 | |||
| cce5e595e5 | |||
| e7d72f6848 | |||
| 7dcad45e0d | |||
| 3772ea6ae1 | |||
| 2cc2eb1ec0 | |||
| 8a789e33b0 | |||
| 2f86f8f8e0 | |||
| 02f9a27f46 | |||
| 6cb605a025 | |||
| 9f3e42e4ef | |||
| 71d987bd2e | |||
| 637899467c | |||
| 5bdb424c6b | |||
| c62cfddb9c | |||
| 14a4c28f8f | |||
| f1e1814ff9 | |||
| b468cf141b | |||
| 787ea1feba | |||
| 91477e9e69 | |||
| cfd0dfd2bf | |||
| 46b1868185 | |||
| 4c4de1d6c4 | |||
| c8b30de771 | |||
| 4f3837f0e6 | |||
| 76848e8807 | |||
| 12902821d6 | |||
| f5549f6bde | |||
| 3825eab989 | |||
| 3cd6ae311d | |||
| 26cfc0257d | |||
| 1d31eabb6e | |||
| 8cd2797b2e | |||
| 11f5236434 | |||
| 220485a2d2 | |||
| eb274cf316 | |||
| aa542980ce | |||
| e0240c186f | |||
| ae58502a21 | |||
| 6a3697279c | |||
| c19ec5d65d | |||
| 15dca449c9 | |||
| dda985f49d | |||
| 12256beeb2 | |||
| 0858ae2024 | |||
| 6c18864291 | |||
| ae57284912 | |||
| 001837e6bb | |||
| 28523f17e2 | |||
| ae2af8315e | |||
| adbb3bb75f | |||
| 6181c4edb3 | |||
| 830c194da5 | |||
| 1830c1e57c | |||
| e5735af6d6 | |||
| a6b0ae71b2 | |||
| 3365baee8f | |||
| cc88dd0b71 | |||
| f050bfe872 | |||
| ab71acc3a5 | |||
| 0a85d1af6b | |||
| 68adadb01a | |||
| d56f458d11 | |||
| a65eadee63 | |||
| 16dfae62bc | |||
| fe680a8b1f | |||
| 54fe9f3eb1 | |||
| cbc6c2666b | |||
| a4d0ac1802 | |||
| 0dc29a7208 | |||
| a9321bc73f | |||
| e3f0ab7c3d | |||
| 5643ea1ba2 | |||
| 3b6523fbd9 | |||
| ffc4f01470 | |||
| e326f41d16 | |||
| 1d0ac72e4a | |||
| b216e44870 | |||
| 7d39b26cf4 | |||
| 884d5fed9f | |||
| ec84188597 | |||
| 85ac95f81b | |||
| 042550cf87 | |||
| b3ebff715a | |||
| 1ee60663bb | |||
| 59da98d3f0 | |||
| 2d41a42f61 | |||
| e1e4a916a5 | |||
| 71f94bff76 | |||
| c7d6467cfa | |||
| 79a3c0b36c | |||
| 966249c10a | |||
| acc010cba5 | |||
| 89f4e7a8db | |||
| 55f4eabecd | |||
| d0fc9aa069 | |||
| 8be9b5082c | |||
| 708907df31 | |||
| 70586b1cf8 | |||
| 877a78d6ba | |||
| 3928614326 | |||
| 5e5f5bfa8d | |||
| 3a1a7b40f9 | |||
| 835d7dcab2 | |||
| 28816dc491 | |||
| ccdc3438be | |||
| 60711dd355 | |||
| fad3947e26 | |||
| d8e5b2d1a4 | |||
| 2d26ad0226 | |||
| 45d3c6c0d3 | |||
| c6bffd7c35 | |||
| 462d81430c | |||
| d3cada5bd6 | |||
| cdbf831a7a | |||
| 0718f14774 | |||
| a6fe656f21 | |||
| dc5da7933a | |||
| 96fc9138d4 | |||
| 6512a3e5f2 | |||
| 49f2124df0 | |||
| a11d6e696a | |||
| 1705ba8069 | |||
| 8d2c4a78a1 | |||
| 8504ff920b | |||
| e34a9e6185 | |||
| c3c7834246 | |||
| 1ab40d8600 | |||
| 92ce02dab0 | |||
| 8abe9ef507 | |||
| d0e04bf569 | |||
| b92599879a | |||
| 0e91298fd1 | |||
| e515220694 | |||
| a55683d287 | |||
| fa4e95105f | |||
| 1e01085ef7 | |||
| 04a1f869b5 | |||
| e04ba7530d | |||
| ea055f1465 | |||
| 3b2c867817 | |||
| 3de23eb0bf | |||
| 5de3b07e2b | |||
| c0ca4d4635 | |||
| efe4b71bae | |||
| bc37bd5429 | |||
| 5f20e04259 | |||
| 9bef5ec01a | |||
| cdf873542b | |||
| 4742690dec | |||
| 3a16f1e854 | |||
| 877400dd12 | |||
| a4e3201113 | |||
| a99cc2fd70 | |||
| 5fe4c33d0e | |||
| 4d9d38cc28 | |||
| 5b71ffd4f9 | |||
| c2ca24a486 | |||
| e5aff6fd6d | |||
| 3eb8aa8268 | |||
| 6d1c32eb77 | |||
| ba776a3c9f | |||
| cd7e260f4e | |||
| ba67e474d3 | |||
| b92a8c513e | |||
| 13572aeef0 | |||
| 5081ea1a0c | |||
| e9e7ce2606 | |||
| 915dcb0c28 | |||
| 8236c6d4b7 | |||
| 555fe37ad8 | |||
| 881f667558 | |||
| 0a99595efe | |||
| 268491b224 | |||
| 49ea9ed722 | |||
| d7108416c9 | |||
| b136630856 | |||
| fa6f31186a | |||
| b027b1d60f | |||
| 7ed1d931cb | |||
| 2570296b01 | |||
| f0a4526250 | |||
| c39332c7e7 | |||
| 3f4b6b22dc | |||
| e0549df03e | |||
| e46662a546 | |||
| 360a74e2fe | |||
| 597c4591bc | |||
| 80833ed703 | |||
| 106302189c | |||
| 05c5f98e8e | |||
| d556fa2cd8 | |||
| 9bd7f023b2 | |||
| 398109ac84 | |||
| 12b870ba66 | |||
| 6202fb8373 | |||
| ced818ad54 | |||
| ccbb6df749 | |||
| 6eb505a677 | |||
| 619783ca1b | |||
| 642aa0bc4b | |||
| 45b3067068 | |||
| b7858a66b9 | |||
| 4e203feaf4 | |||
| a513b47780 | |||
| 547a2831c7 | |||
| 5c52ffe24e | |||
| a5763d6fee | |||
| 95482c554d | |||
| 10758710d4 | |||
| 86cf9383ea | |||
| 307977d4cf | |||
| 1beff539d7 | |||
| df578d6ec5 | |||
| 6aae381e83 | |||
| 7ee9051a56 | |||
| eb11edabe0 | |||
| c067b90403 | |||
| 5b6770f3d2 | |||
| 718b80ba39 | |||
| 4d052d5119 | |||
| 7e4c643401 | |||
| e920338f21 | |||
| af2048570c | |||
| 1ee4f849cb | |||
| 703393fc63 | |||
| 81420ab246 | |||
| c94d19718b | |||
| e25c72ecdd | |||
| 780b81a59f | |||
| 9f1dda701d | |||
| e597a8d72e | |||
| de9a4b5164 | |||
| 319aca3101 | |||
| 9dc2c01aaa | |||
| 6164672421 | |||
| 61906613b0 | |||
| 3b48fa8e7d | |||
| 324b7d65e7 | |||
| 373a60b9ef | |||
| 2ef22e86e0 | |||
| 830f4f540f | |||
| 56ff5496bc | |||
| 20fbece14c | |||
| 9fbfd86cde | |||
| 7547bc66cf | |||
| 18a9fa7355 | |||
| b32af841c5 | |||
| 66b4252931 | |||
| 2c95eaa418 | |||
| 7382f52dc9 | |||
| 49dd299999 | |||
| e391b05513 | |||
| 2de62910fc | |||
| fc77b5b4ac | |||
| a83d916fad | |||
| e71a641379 | |||
| e2eca45188 | |||
| 4d78540658 | |||
| b83c3f265b | |||
| 30f5a3bb93 | |||
| 2e1e1e6034 | |||
| 991479fbf9 | |||
| 5660f98cc3 | |||
| 5bf0f9d630 | |||
| 15b72119eb | |||
| dc30e7a200 | |||
| db2293144a | |||
| 5016f45429 | |||
| 9fa4aa40b7 | |||
| 52f60c706a | |||
| fff4ead96a | |||
| 3574341b6b | |||
| cbabc80d92 | |||
| f4cf88c2ca | |||
| 6db95b554f | |||
| 105de7705a | |||
| 584dffea14 | |||
| 41b6d215bb | |||
| 9274f29ca9 | |||
| 08c87e57f8 | |||
| b21cdd5037 | |||
| 63ab8b2418 | |||
| cb7a343caf | |||
| 40542e6e26 | |||
| 9da05dd4cb | |||
| ae9da0abfb | |||
| d3ea334e7a | |||
| d76132a3fb | |||
| 223c473cf6 | |||
| fd57cfa1ae | |||
| f23bd2dc27 | |||
| 69062ba3ab | |||
| e75563cb32 | |||
| d63885a495 | |||
| f28a34fa99 | |||
| a1e8de4e00 | |||
| d247ba4751 | |||
| 27b7dc336a | |||
| 60a7c68aa6 | |||
| 78c103e62c | |||
| ffec1c77f2 | |||
| 5357181484 | |||
| 33ddb3ad4d | |||
| 1cd453db14 | |||
| 3b5932699c | |||
| bada81159d | |||
| 652da98c70 | |||
| e14e2c3b4d | |||
| f96a897821 | |||
| b74ae77745 | |||
| 564226be02 | |||
| f6c45fc68a | |||
| 35ba5771a5 | |||
| b2461f7192 | |||
| 60a54f404b | |||
| 921f261377 | |||
| d70a555c1c | |||
| 4c339360e9 | |||
| 731dad480d | |||
| a0f2357cb3 | |||
| e86ac75e9c | |||
| f51de2e488 | |||
| 5efefdcf16 | |||
| cabb2bb992 | |||
| d560f6c920 | |||
| 21432ba96e | |||
| c341597657 | |||
| 2a1420d4e7 | |||
| 28d88f6af4 | |||
| c4d2d287fc | |||
| 6a85546b76 | |||
| 2e92d0c821 | |||
| a499a3aa5e | |||
| 23ab3c4713 | |||
| da300aa9c3 | |||
| e225158a6f | |||
| 2ce55783d2 | |||
| 14eeee40b2 | |||
| 038dea9202 | |||
| 0ae3484171 | |||
| 54976c3249 | |||
| 4c06b44315 | |||
| 678b58e0b1 | |||
| 8f913c656c | |||
| 001b48a5c6 | |||
| 54929a1b92 | |||
| 92780e2683 | |||
| 2891988d3b | |||
| c1728914c6 | |||
| ed2f49e8d2 | |||
| 8a76a370a9 | |||
| 1160fd4331 | |||
| 0134c38759 | |||
| d079095517 | |||
| 028d628e9f | |||
| 5e4b62acfe | |||
| 9366fa8e95 | |||
| 369db3a8e3 | |||
| 8c360b2a3c | |||
| b66e7bed45 | |||
| e919482aa8 | |||
| dce45e7d58 | |||
| 1a0877e965 | |||
| 0361a18551 | |||
| 83d90f1463 | |||
| f661ae9d09 | |||
| bee4cb57f2 | |||
| 53b670b889 | |||
| e2600a3e44 | |||
| 25101b2ae0 | |||
| 4e7867fcc1 | |||
| 101ee64165 | |||
| 4c3e65791e | |||
| a9c8031b61 | |||
| afb3033913 | |||
| 2ad26640a2 | |||
| 2c0b08145f | |||
| aa9c9eda9e | |||
| 1353d61894 | |||
| 88ba6d8015 | |||
| 8b288a2072 | |||
| 4e90644527 | |||
| 6651b65373 | |||
| 705352099f | |||
| 2e28c9d793 | |||
| 2fe660a1d7 | |||
| b03ce0e9b4 | |||
| 386f5f596d | |||
| add53228b2 | |||
| d90008cc52 | |||
| dbf8f9ab38 | |||
| 81a99cf67b | |||
| 876af6fb02 | |||
| b3734a5f77 | |||
| 419ab6f00c | |||
| 5558b55e9f | |||
| 4b14d608f4 | |||
| 9428d86f2b | |||
| ddebf0daf2 | |||
| 3a44c62ecf | |||
| 184efd4f49 | |||
| 6b3c4cc379 | |||
| 0b137e087c | |||
| 37790c13a0 | |||
| 82057f08ce | |||
| 1553421e1a | |||
| f3ea109e6f | |||
| 90dbfe7660 | |||
| 125bad3154 | |||
| 30c83d6c81 | |||
| 4f12c118a5 | |||
| 423775d50e | |||
| 860a5c3e86 | |||
| 649e02f209 | |||
| b449305cc1 | |||
| 49bee6bad0 | |||
| ac277a1cce | |||
| a17310a83c | |||
| b509946b13 | |||
| a69ea58388 | |||
| 30530d058c | |||
| 436928d06a | |||
| 32a502d14e | |||
| 0d665c637f | |||
| 1b6a14ac39 | |||
| 367013f589 | |||
| c980a30bad | |||
| 78b459590c | |||
| 054e241033 | |||
| f7e9649be4 | |||
| fd1f6ec75c | |||
| 6b0d7cb26c | |||
| 3aea08df78 | |||
| 3c6f90e552 | |||
| 3703ca4df4 | |||
| 41b8281c73 | |||
| acd1f83bd0 | |||
| ba8371104d | |||
| 991682e9fd | |||
| f0de994059 | |||
| ebb2a9812c | |||
| 265c05927f | |||
| 05ad38ae2d | |||
| 596a2c8355 | |||
| 9f52b2c283 | |||
| 8035a407a6 | |||
| 97760c3fa4 | |||
| d75291097e | |||
| db632b7e22 | |||
| 1a75dfe075 | |||
| e00d88d82e | |||
| 04cce1826b | |||
| cc28cda053 | |||
| cfabc0e61f | |||
| 91b534d128 | |||
| 3268f43340 | |||
| 05e374934d | |||
| 3e1ff0ec67 | |||
| 65945dac09 | |||
| 1608da2dc8 | |||
| c340827381 | |||
| 74fa7ca25d | |||
| 5a9223afda | |||
| febcd73323 | |||
| df06236076 | |||
| b0d3fbba47 | |||
| adb6c7637e | |||
| 425f83b17d | |||
| 976415ff9d | |||
| 4d7fb3e8d6 | |||
| bcca3bf322 | |||
| 74aaa3408f | |||
| 2a5beee88c | |||
| cec9f7abfe | |||
| 284a9cd4c3 | |||
| 5955c101d4 | |||
| f80b910ba3 | |||
| 2b0521347b | |||
| 0c06a8d154 | |||
| b0e3a4e276 | |||
| b651466630 | |||
| 24c09c9201 | |||
| e48346a9ee | |||
| 9bd8bdaa5a | |||
| a137699d95 | |||
| f6a56c2f82 | |||
| dffa791607 | |||
| 5ce6555721 | |||
| 53b3ad186f | |||
| 82c1c5b3fe | |||
| 6d880bc3bb | |||
| 40281d595d | |||
| 85fab55e57 | |||
| 1d2eb8055e | |||
| 9e0b69312b | |||
| bbddbba340 | |||
| 0d01a6f552 | |||
| ae3672608d | |||
| e5c39fb2a9 | |||
| eb4b3f5976 | |||
| dbb070524f | |||
| 36b0b50ba4 | |||
| ac46b2053d | |||
| 0ffcccdae5 | |||
| 4777bd607e | |||
| 39e9b50482 | |||
| 30adb9c770 | |||
| b1d1497f4b | |||
| 9df3a94d33 | |||
| d4f335d068 | |||
| 74341b9b74 | |||
| 66ee2cb6ed | |||
| 1d4881cbbe | |||
| 04b917a60a | |||
| e6c99cd289 | |||
| 6bc5584add | |||
| 121f0185d6 | |||
| e7999f8450 | |||
| 0b29e42adb | |||
| fcc8b89e6b | |||
| 529d1c78c7 | |||
| 414486829a | |||
| 3e05be8eb8 | |||
| ae24a8e5ae | |||
| d2588f9d1d | |||
| 1eb9994d88 | |||
| a43b89f36e | |||
| 0ed34af19d | |||
| 71729c2855 | |||
| 6c8c430c2a | |||
| 57b97ad0bd | |||
| 56f7a859df | |||
| e5e14b9947 | |||
| 3d8bf36a30 | |||
| 85f7c2d040 | |||
| 26ea8f6dcb | |||
| e05fe1837d | |||
| 94762b56f6 | |||
| b3b688fa50 | |||
| 5eaa8de8f9 | |||
| 26d3c54aff | |||
| 349a62121c | |||
| bbb0e14633 | |||
| 42312d9def | |||
| 065d0e4ee3 | |||
| b772ad7094 | |||
| 444d366c39 | |||
| 8e4233b86a | |||
| 6424966b7a | |||
| 4e42d7df43 | |||
| 580ee5cc4a | |||
| 56a98a483f | |||
| df7a4eda8a | |||
| 91cc0b282a | |||
| 01d8aea4df | |||
| ee904060c5 | |||
| afb5538e83 | |||
| 1f24f105cc | |||
| 8f39ebbe5a | |||
| c1e720a49b | |||
| f38c8875b2 | |||
| e7e51f53ce | |||
| 5259de5872 | |||
| e2b9c87aa8 | |||
| 8c7cf0dbb0 | |||
| e6e9375b09 | |||
| c6096f9205 | |||
| 11614c2649 | |||
| 793bc8c585 | |||
| 335e88b738 | |||
| b77ea94976 | |||
| ae17a51c0d | |||
| ee7a83f124 | |||
| 67ac551a2f | |||
| 572ac616c1 | |||
| 96bf6a5bcb | |||
| c43d66c286 | |||
| 95fb5fa46c | |||
| d614913c11 | |||
| 3bfaac0844 | |||
| 14d0cbf6d7 | |||
| 61a163d773 | |||
| 3a644dad78 | |||
| d2c1c719bd | |||
| 333db4dc94 | |||
| cbcf4b6071 | |||
| e6e0aba8c3 | |||
| 85097a9958 | |||
| 7791c343c4 | |||
| 3bd762591a | |||
| 8e3b77aba8 | |||
| 36e3a02f67 | |||
| 566a242ba3 | |||
| 1e3b3c107c | |||
| 2ac33285c1 | |||
| 7cb8016df3 | |||
| cf3c5a878a | |||
| 2d20bde495 | |||
| b9e347ef50 | |||
| 6707c8750e | |||
| e5502c13ee | |||
| f30d2e43ea | |||
| 6c73f9d3fd | |||
| 1161aa829d | |||
| 01519f2fd5 | |||
| 33aad3a8ce | |||
| 4262c125c5 | |||
| a09d5959ef | |||
| d7bd3f8402 | |||
| 0fff6a2b74 | |||
| f4c0405221 | |||
| 49d337c830 | |||
| 294092979e | |||
| c454ede184 | |||
| d854c5003c | |||
| 66d8776b83 | |||
| ba6ecf35cf | |||
| 10cc9cf661 | |||
| 2db971eedd | |||
| 1775e80b41 | |||
| e4a93619db | |||
| 4d14b3bcb4 | |||
| 9f4f5f9346 | |||
| 0fae31fb54 | |||
| 8987a6630c | |||
| 10ff8e0426 | |||
| a0ae02168a | |||
| a3c1ac2030 | |||
| 629b248f53 | |||
| 62a72f0163 | |||
| 655931f0ea | |||
| ca36fabfc0 | |||
| 7bd62481ad | |||
| fbd27d7c45 | |||
| 3546391311 | |||
| 24c812115e | |||
| 28be0ad69b | |||
| f0980c0a98 | |||
| 1df4aa90ce | |||
| 6b3cf051f8 | |||
| 4ecd6e592b | |||
| dbddec33c8 | |||
| 401a5955a4 | |||
| 9a3b4167bb | |||
| 13bc6eeea4 | |||
| 2da18b6d33 | |||
| 6d37ed12d2 | |||
| eab23cd5b7 | |||
| d233706a2d | |||
| f1ab17ed4e | |||
| 6113164211 | |||
| 4db462a703 | |||
| a22c6d6c0c | |||
| 59fb7b020a | |||
| 65f079ebc4 | |||
| d16aa79492 | |||
| 5af0acc4af | |||
| a459364de3 | |||
| 277ef1a68f | |||
| 193c7c82c8 | |||
| f7d8ba408c | |||
| 9a8759efef | |||
| 054948e701 | |||
| 1c5ddd65b4 | |||
| b8697fb4ed | |||
| 03570275c1 | |||
| b5587f1937 | |||
| c4c6975f1b | |||
| 0be0fb2a57 | |||
| 115e6e7f9e | |||
| 3868a9a0f0 | |||
| ba5050ac7c | |||
| d936ca1ea0 | |||
| fd8c4d58bb | |||
| ce4b7b8b7d | |||
| 069a47220e | |||
| 66e4aaffc5 | |||
| 81336b58cb | |||
| b201670f7a | |||
| 4b051a0d3b | |||
| 45353465a6 | |||
| c63cb98019 | |||
| 773cf5ca08 | |||
| 2db03cb4a5 | |||
| eed873c6ec | |||
| 3d2d461867 | |||
| 36392d658e | |||
| 82696179e8 | |||
| 188bc28f6a | |||
| 240da5c8e0 | |||
| 689a0c0b49 | |||
| bc16b290ba | |||
| 96d32680fe | |||
| d782b3d21d | |||
| ed089b44b9 | |||
| 33f4af2e19 | |||
| 69f7382eec | |||
| 7e3293fc20 | |||
| e4a8283327 | |||
| 001baf4419 | |||
| d167290b28 | |||
| f4879d4723 | |||
| fd81c06c35 | |||
| 94afcec757 | |||
| 4f28e9e1fb | |||
| 0622509807 | |||
| 9ca2246bac | |||
| 647e2cafd7 | |||
| 5df854fcef | |||
| 260089431e | |||
| d0d8da8c08 | |||
| d1365b3466 | |||
| c949ca2a5c | |||
| d974b29f67 | |||
| cc7316bb35 | |||
| a0d8dcd974 | |||
| c642e326ce | |||
| 362a118782 | |||
| 3ab481df17 | |||
| 4e7150b470 | |||
| 1ced92be47 | |||
| 15dbea6899 | |||
| c4081393c1 | |||
| 1d81b73df9 | |||
| 18f885efab | |||
| bba088bee7 | |||
| 6cbb6bef0b | |||
| 8744c60563 | |||
| 8197c02dcf | |||
| 9faf0020cc | |||
| 53075e2570 | |||
| 264ca00db7 | |||
| 6b65ef6d88 | |||
| 5957d7f7be | |||
| 35c102137f | |||
| 5427d14416 | |||
| 178236d1ff | |||
| 736c880ba9 | |||
| 126f7aa892 | |||
| 2957f007e3 | |||
| 04501c93fe | |||
| 4236519b84 | |||
| e4944b4f2e | |||
| 2deb2f8eeb | |||
| 3fa398ec43 | |||
| 1851674b50 | |||
| c5ef5279d4 | |||
| d3c24d159f | |||
| 23f9f9064e | |||
| a134307dcd | |||
| c3b510c2d9 | |||
| e7fc24e48c | |||
| 6a88dc322a | |||
| 6b464e3558 | |||
| 76b0c7b765 | |||
| 91857e8f16 | |||
| ccda456c0a | |||
| 83bad13e9e | |||
| e6a206a430 | |||
| f52a1e4ded | |||
| a8e458339b | |||
| 6b5e9aec8e | |||
| 2ab0d97573 | |||
| 0c05fc1432 | |||
| 33eeb58521 | |||
| 8fafdb185c | |||
| c2c935ba81 | |||
| 2d73c8868b | |||
| b95bb1286b | |||
| 4237c8ec30 | |||
| 49b4b39055 | |||
| bf15fea135 | |||
| 47c03e376d | |||
| 1cabfac36c | |||
| 8e32276283 | |||
| 366b306df0 | |||
| 4bf1f798f5 | |||
| b2fdb69b4d | |||
| af2736daec | |||
| 5cad7d44a6 | |||
| 2b96be0ae8 | |||
| 2a89d8021c | |||
| 13deb4706c | |||
| 9b61adb97d | |||
| 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 | |||
| d05ec5e484 |
@@ -0,0 +1 @@
|
||||
*.odin linguist-language=Odin
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: gingerbill
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
|
||||
|
||||
* Operating System:
|
||||
* Please paste `odin version` output:
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Please describe the behavior you are expecting
|
||||
|
||||
## Current Behavior
|
||||
|
||||
What is the current behavior?
|
||||
|
||||
## Failure Information (for bugs)
|
||||
|
||||
Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Please provide detailed steps for reproducing the issue.
|
||||
|
||||
1. step 1
|
||||
2. step 2
|
||||
3. you get it...
|
||||
|
||||
### Failure Logs
|
||||
|
||||
Please include any relevant log snippets or files here.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,59 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
build_macOS:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: powershell
|
||||
run: |
|
||||
cd bin
|
||||
$ProgressPreference = "SilentlyContinue";
|
||||
Invoke-WebRequest -Uri https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip -OutFile llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > $null
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build.bat 1
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: 0 20 * * *
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install cURL
|
||||
run: choco install curl
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin
|
||||
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > nul
|
||||
rm -f llvm-binaries.zip
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build.bat 1 1
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
rm bin/llvm/windows/LLVM-C.lib
|
||||
mkdir dist
|
||||
cp odin.exe dist
|
||||
cp LLVM-C.dll dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r bin dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ubuntu_artifacts
|
||||
path: dist
|
||||
build_macos:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: macos_artifacts
|
||||
path: dist
|
||||
upload_b2:
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [build_windows, build_macos, build_ubuntu]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Install B2 CLI
|
||||
shell: bash
|
||||
run: sudo pip install --upgrade b2
|
||||
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: windows_artifacts
|
||||
|
||||
- name: Download Ubuntu artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: ubuntu_artifacts
|
||||
|
||||
- name: Download macOS artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: macos_artifacts
|
||||
|
||||
- name: Create archives and upload
|
||||
shell: bash
|
||||
env:
|
||||
APPID: ${{ secrets.B2_APPID }}
|
||||
APPKEY: ${{ secrets.B2_APPKEY }}
|
||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||
run: |
|
||||
b2 authorize-account "$APPID" "$APPKEY"
|
||||
|
||||
chmod +x ./ci/upload_create_nightly.sh
|
||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
||||
|
||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||
|
||||
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
|
||||
b2 upload-file "$BUCKET" nightly.json nightly.json
|
||||
|
||||
b2 clear-account
|
||||
+14
-1
@@ -18,11 +18,12 @@ bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
![Cc]ore/[Ll]og/
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
demo
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
@@ -257,7 +258,19 @@ paket-files/
|
||||
builds/
|
||||
bin/
|
||||
*.exe
|
||||
*.obj
|
||||
*.pdb
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
# temp files
|
||||
* .ll
|
||||
*.bc
|
||||
*.ll
|
||||
|
||||
*.sublime-workspace
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2020 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,28 @@
|
||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++11 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
release:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
nightly:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# The Proposal Process
|
||||
|
||||
## Introduction
|
||||
|
||||
The Odin project's development process is driven by design and pragmatism. Significant changes to the language, libraries, or tools _must_ be first discussed, and maybe formally documented, before they can be implemented.
|
||||
|
||||
This document describes the process for proposing, documenting, and implementing changes to the Odin project.
|
||||
|
||||
## The Proposal Process
|
||||
|
||||
The proposal process is the process for reviewing a proposal and reaching a decision about whether to accept or decline the proposal.
|
||||
|
||||
1. [Ginger Bill](https://github.com/gingerBill) is [BDFL](https://wikipedia.org/wiki/Benevolent_dictator_for_life) and significant changes _must_ be passed by him.
|
||||
|
||||
2. The proposal author creates a brief issue describing the proposal.
|
||||
|
||||
Note: There is no need for a design document at this point.<br>
|
||||
Note: A non-proposal issue can be turned into a proposal by simply adding the _proposal_ label.
|
||||
|
||||
3. A discussion on the issue tracker will classify the proposal into one of three outcomes:
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
* Ask for a design document.
|
||||
|
||||
If the proposal is accepted or declined, the process is done. Otherwise the discussion around the process is expected to identify issues that ought to be addressed in a more detailed design.
|
||||
|
||||
4. The proposal author writes a design document to work out details of the proposed design and address the concerns raised in the initial discussion.
|
||||
|
||||
5. Once comments and revisions on the design document calm, there is a final discussion on the issue, to reach one of two outcomes:
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
|
||||
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
|
||||
|
||||
## Design Documents
|
||||
|
||||
The design document should follow this template:
|
||||
|
||||
|
||||
```
|
||||
# Proposal: [Title]
|
||||
|
||||
Author(s): [Author Name, Co-Author Name]
|
||||
Last updated: [Date ISO-8601]
|
||||
Discussion at https://github.com/odin-lang/Odin/issues/######
|
||||
|
||||
## Abstract
|
||||
|
||||
## Background
|
||||
|
||||
## Proposal
|
||||
|
||||
## Rationale
|
||||
|
||||
## Compatibility
|
||||
|
||||
## Implementation
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
If you need help with this process, please contact an Odin contributor by posting an issue to the [issue tracker](https://github.com/odin-lang/Odin/issues).
|
||||
@@ -1,35 +1,134 @@
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
|
||||
<p align="center">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="120">
|
||||
<br/>
|
||||
A fast, concise, readable, pragmatic and open sourced programming language.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/odin-lang/odin.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://discord.gg/hnwN2Rj">
|
||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/actions">
|
||||
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
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:
|
||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of creating an alternative to C with the following goals:
|
||||
* simplicity
|
||||
* high performance
|
||||
* built for modern systems
|
||||
* joy of programming
|
||||
* metaprogramming
|
||||
* designed for good programmers
|
||||
|
||||
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
Website: [https://odin-lang.org/](https://odin-lang.org/)
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
|
||||
- [Demo](https://youtu.be/TMCkT-uASaE?t=1800)
|
||||
- [Q&A](https://youtu.be/TMCkT-uASaE?t=5749)
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
```odin
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
program := "+ + * 😃 - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
switch token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case '😃': accumulator *= accumulator;
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
|
||||
Instructions for downloading and install the Odin compiler and libraries.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
||||
|
||||
An overview of the Odin programming language.
|
||||
|
||||
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
|
||||
|
||||
Answers to common questions about Odin.
|
||||
|
||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||
|
||||
A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### References
|
||||
|
||||
#### [Language Specification](https://odin-lang.org/ref/spec)
|
||||
|
||||
The official Odin Language specification.
|
||||
|
||||
### Articles
|
||||
|
||||
#### [The Odin Blog](https://odin-lang.org/blog)
|
||||
|
||||
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
|
||||
- MacOS
|
||||
* Have LLVM explicitly installed (`brew install llvm`)
|
||||
* Have XCode installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C99 support)
|
||||
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
@@ -42,30 +141,26 @@ Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
* x86-64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is calling the linker for now)
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
|
||||
## Roadmap
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
|
||||
- [Demo](https://youtu.be/TMCkT-uASaE?t=1800)
|
||||
- [Q&A](https://youtu.be/TMCkT-uASaE?t=5749)
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
|
||||
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
|
||||
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
|
||||
* [Packages, Bit Sets, cstring](https://youtu.be/b8bJbjiXZrQ)
|
||||
- [Q&A](https://youtu.be/5jmxyIfyyTk)
|
||||
|
||||
Not in any particular order and not be implemented
|
||||
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Debug Information
|
||||
- pdb format too
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
- Big numbers library
|
||||
- Multithreading for performance increase
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# The Odin Programming Language
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
|
||||
- MacOS
|
||||
* Have LLVM explicitly installed (`brew install llvm`)
|
||||
* Have XCode installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
Binary file not shown.
@@ -4,28 +4,46 @@
|
||||
set exe_name=odin.exe
|
||||
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=0
|
||||
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
if "%1" == "1" (
|
||||
set release_mode=1
|
||||
) else if "%1" == "release" (
|
||||
set release_mode=1
|
||||
) else (
|
||||
set release_mode=0
|
||||
)
|
||||
|
||||
:: Normal = 0, CI Nightly = 1
|
||||
if "%2" == "1" (
|
||||
set nightly=1
|
||||
) else (
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DLLVM_BACKEND_SUPPORT
|
||||
|
||||
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
|
||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
rem -DDISPLAY_TIMING
|
||||
) else ( rem Release
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||
)
|
||||
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 -wd4244 ^
|
||||
-wd4306 ^
|
||||
-wd4201 -wd4204 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4505 -wd4512 -wd4550
|
||||
-wd4512
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
kernel32.lib
|
||||
rem "src\dyncall\lib\*.lib"
|
||||
kernel32.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
@@ -35,21 +53,17 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set linker_flags=%linker_flags% -debug
|
||||
)
|
||||
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "src\main.c" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run code/demo.odin
|
||||
rem && odin build code/metagen.odin ^
|
||||
rem && call "code\metagen.exe" "src\ast_nodes.metagen"
|
||||
rem && odin run code/Jaze/src/main.odin
|
||||
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=0
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm"
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g -fno-inline-functions"
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
@@ -19,6 +25,4 @@ if [[ "$(uname)" == "Darwin" ]]; then
|
||||
other_args="${other_args} -liconv"
|
||||
fi
|
||||
|
||||
${compiler} src/main.c ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
|
||||
./odin run code/demo.odin
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import urllib.parse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
files_by_date = {}
|
||||
bucket = sys.argv[1]
|
||||
|
||||
files_lines = execute_cli(f"b2 ls --long {bucket} nightly").split("\n")
|
||||
for x in files_lines:
|
||||
parts = x.split(" ", 1)
|
||||
if parts[0]:
|
||||
json_str = execute_cli(f"b2 get-file-info {parts[0]}")
|
||||
data = json.loads(json_str)
|
||||
name = remove_prefix(data['fileName'], "nightly/")
|
||||
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
|
||||
sha1 = data['contentSha1']
|
||||
size = int(data['contentLength'])
|
||||
ts = int(data['fileInfo']['src_last_modified_millis'])
|
||||
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
|
||||
|
||||
if date not in files_by_date.keys():
|
||||
files_by_date[date] = []
|
||||
|
||||
files_by_date[date].append({
|
||||
'name': name,
|
||||
'url': url,
|
||||
'sha1': sha1,
|
||||
'sizeInBytes': size,
|
||||
})
|
||||
|
||||
now = datetime.datetime.utcnow().isoformat()
|
||||
|
||||
print(json.dumps({
|
||||
'last_updated' : now,
|
||||
'files': files_by_date
|
||||
}, sort_keys=True, indent=4))
|
||||
|
||||
def remove_prefix(text, prefix):
|
||||
return text[text.startswith(prefix) and len(prefix):]
|
||||
|
||||
def execute_cli(command):
|
||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
||||
return sb.stdout.read().decode("utf-8");
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import urllib.parse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
files_by_date = {}
|
||||
bucket = sys.argv[1]
|
||||
days_to_keep = int(sys.argv[2])
|
||||
print(f"Looking for binaries to delete older than {days_to_keep} days")
|
||||
|
||||
files_lines = execute_cli(f"b2 ls --long --versions {bucket} nightly").split("\n")
|
||||
for x in files_lines:
|
||||
parts = [y for y in x.split(' ') if y]
|
||||
|
||||
if parts and parts[0]:
|
||||
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
delta = now - date
|
||||
|
||||
if delta.days > days_to_keep:
|
||||
print(f'Deleting {parts[5]}')
|
||||
execute_cli(f'b2 delete-file-version {parts[0]}')
|
||||
|
||||
|
||||
def execute_cli(command):
|
||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
||||
return sb.stdout.read().decode("utf-8");
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
bucket=$1
|
||||
platform=$2
|
||||
artifact=$3
|
||||
|
||||
now=$(date +'%Y-%m-%d')
|
||||
filename="odin-$platform-nightly+$now.zip"
|
||||
|
||||
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
||||
|
||||
7z a -bd "output/$filename" -r "$artifact"
|
||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
||||
@@ -1,18 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
immutable program := "+ + * - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
match token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
default: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
|
||||
}
|
||||
-222
@@ -1,222 +0,0 @@
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import wgl "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
#import gl "opengl.odin";
|
||||
|
||||
TWO_HEARTS :: '💕';
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := cast(int)win32.GetLastError();
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
}
|
||||
}
|
||||
|
||||
// Yuk!
|
||||
to_c_string :: proc(s: string) -> []u8 {
|
||||
c_str := make([]u8, len(s)+1);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[len(s)] = 0;
|
||||
return c_str;
|
||||
}
|
||||
|
||||
|
||||
Window :: struct {
|
||||
width, height: int,
|
||||
wc: win32.WndClassExA,
|
||||
dc: win32.Hdc,
|
||||
hwnd: win32.Hwnd,
|
||||
opengl_context, rc: wgl.Hglrc,
|
||||
c_title: []u8,
|
||||
}
|
||||
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
|
||||
using win32;
|
||||
|
||||
w: Window;
|
||||
w.width, w.height = msg, height;
|
||||
|
||||
class_name := "Win32-Odin-Window\x00";
|
||||
c_class_name := ^class_name[0];
|
||||
if title[len(title)-1] != 0 {
|
||||
w.c_title = to_c_string(title);
|
||||
} else {
|
||||
w.c_title = cast([]u8)title;
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil);
|
||||
|
||||
w.wc = WndClassExA{
|
||||
size = size_of(WndClassExA),
|
||||
style = CS_VREDRAW | CS_HREDRAW,
|
||||
instance = cast(Hinstance)instance,
|
||||
class_name = c_class_name,
|
||||
wnd_proc = window_proc,
|
||||
};
|
||||
|
||||
if RegisterClassExA(^w.wc) == 0 {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
c_class_name, ^w.c_title[0],
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
cast(i32)w.width, cast(i32)w.height,
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.dc = GetDC(w.hwnd);
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
size = size_of(PIXELFORMATDESCRIPTOR),
|
||||
version = 1,
|
||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
pixel_type = PFD_TYPE_RGBA,
|
||||
color_bits = 32,
|
||||
alpha_bits = 8,
|
||||
depth_bits = 24,
|
||||
stencil_bits = 8,
|
||||
layer_type = PFD_MAIN_PLANE,
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wgl.CreateContext(w.dc);
|
||||
wgl.MakeCurrent(w.dc, w.opengl_context);
|
||||
|
||||
attribs := [8]i32{
|
||||
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||
};
|
||||
|
||||
wgl_str := "wglCreateContextAttribsARB\x00";
|
||||
wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.GetProcAddress(^wgl_str[0]);
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, nil, ^attribs[0]);
|
||||
wgl.MakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
return w, true;
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
win32.SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
|
||||
run :: proc() {
|
||||
using win32;
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, cast(Wnd_Proc)win32_proc);
|
||||
if !window_success {
|
||||
return;
|
||||
}
|
||||
defer destroy_window(^window);
|
||||
|
||||
gl.init();
|
||||
|
||||
|
||||
prev_time := time_now();
|
||||
running := true;
|
||||
|
||||
pos := Vec2{100, 100};
|
||||
|
||||
for running {
|
||||
curr_time := time_now();
|
||||
dt := cast(f32)(curr_time - prev_time);
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: Msg;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false;
|
||||
}
|
||||
TranslateMessage(^msg);
|
||||
DispatchMessageA(^msg);
|
||||
}
|
||||
|
||||
if is_key_down(Key_Code.ESCAPE) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
{
|
||||
SPEED :: 500;
|
||||
v: Vec2;
|
||||
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1; }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
|
||||
|
||||
v = norm(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
|
||||
|
||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.LoadIdentity();
|
||||
gl.Ortho(0, cast(f64)window.width,
|
||||
0, cast(f64)window.height, 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
defer gl.End();
|
||||
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0);
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0);
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
}
|
||||
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window);
|
||||
ms_to_sleep := cast(i32)(16 - 1000*dt);
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
run();
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
|
||||
#foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
||||
|
||||
|
||||
SOCKET :: #type uint;
|
||||
INVALID_SOCKET :: ~(cast(SOCKET)0);
|
||||
|
||||
AF :: enum i32 {
|
||||
UNSPEC = 0, // unspecified
|
||||
UNIX = 1, // local to host (pipes, portals)
|
||||
INET = 2, // internetwork: UDP, TCP, etc.
|
||||
IMPLINK = 3, // arpanet imp addresses
|
||||
PUP = 4, // pup protocols: e.g. BSP
|
||||
CHAOS = 5, // mit CHAOS protocols
|
||||
NS = 6, // XEROX NS protocols
|
||||
ISO = 7, // ISO protocols
|
||||
OSI = ISO, // OSI is ISO
|
||||
ECMA = 8, // european computer manufacturers
|
||||
DATAKIT = 9, // datakit protocols
|
||||
CCITT = 10, // CCITT protocols, X.25 etc
|
||||
SNA = 11, // IBM SNA
|
||||
DECnet = 12, // DECnet
|
||||
DLI = 13, // Direct data link interface
|
||||
LAT = 14, // LAT
|
||||
HYLINK = 15, // NSC Hyperchannel
|
||||
APPLETALK = 16, // AppleTalk
|
||||
ROUTE = 17, // Internal Routing Protocol
|
||||
LINK = 18, // Link layer interface
|
||||
XTP = 19, // eXpress Transfer Protocol (no AF)
|
||||
COIP = 20, // connection-oriented IP, aka ST II
|
||||
CNT = 21, // Computer Network Technology
|
||||
RTIP = 22, // Help Identify RTIP packets
|
||||
IPX = 23, // Novell Internet Protocol
|
||||
SIP = 24, // Simple Internet Protocol
|
||||
PIP = 25, // Help Identify PIP packets
|
||||
MAX = 26,
|
||||
};
|
||||
|
||||
SOCK_STREAM :: 1;
|
||||
SOCKET_ERROR :: -1;
|
||||
IPPROTO_TCP :: 6;
|
||||
AI_PASSIVE :: 0x0020;
|
||||
SOMAXCONN :: 128;
|
||||
|
||||
SD_RECEIVE :: 0;
|
||||
SD_SEND :: 1;
|
||||
SD_BOTH :: 2;
|
||||
|
||||
WSADESCRIPTION_LEN :: 256;
|
||||
WSASYS_STATUS_LEN :: 128;
|
||||
WSADATA :: struct #ordered {
|
||||
version: i16,
|
||||
high_version: i16,
|
||||
|
||||
|
||||
// NOTE(bill): This is x64 ordering
|
||||
max_sockets: u16,
|
||||
max_udp_dg: u16,
|
||||
vendor_info: ^byte,
|
||||
description: [WSADESCRIPTION_LEN+1]byte,
|
||||
system_status: [WSASYS_STATUS_LEN+1]byte,
|
||||
}
|
||||
|
||||
addrinfo :: struct #ordered {
|
||||
flags: i32,
|
||||
family: i32,
|
||||
socktype: i32,
|
||||
protocol: i32,
|
||||
addrlen: uint,
|
||||
canonname: ^u8,
|
||||
addr: ^sockaddr,
|
||||
next: ^addrinfo,
|
||||
}
|
||||
|
||||
sockaddr :: struct #ordered {
|
||||
family: u16,
|
||||
data: [14]byte,
|
||||
}
|
||||
|
||||
|
||||
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign ws2;
|
||||
WSACleanup :: proc() -> i32 #foreign ws2;
|
||||
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign ws2;
|
||||
freeaddrinfo :: proc(ai: ^addrinfo) #foreign ws2;
|
||||
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign ws2;
|
||||
closesocket :: proc(s: SOCKET) -> i32 #foreign ws2;
|
||||
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign ws2;
|
||||
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign ws2;
|
||||
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign ws2;
|
||||
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign ws2;
|
||||
WSAGetLastError :: proc() -> i32 #foreign ws2;
|
||||
|
||||
to_c_string :: proc(s: string) -> ^byte {
|
||||
c_str := new_slice(byte, s.count+1);
|
||||
assert(c_str.data != nil);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[s.count] = 0;
|
||||
return c_str.data;
|
||||
}
|
||||
|
||||
run :: proc() {
|
||||
wsa: WSADATA;
|
||||
res: ^addrinfo = nil;
|
||||
hints: addrinfo;
|
||||
s, client: SOCKET;
|
||||
|
||||
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
|
||||
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer WSACleanup();
|
||||
|
||||
hints.family = cast(i32)AF.INET;
|
||||
hints.socktype = SOCK_STREAM;
|
||||
hints.protocol = IPPROTO_TCP;
|
||||
hints.flags = AI_PASSIVE;
|
||||
|
||||
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer freeaddrinfo(res);
|
||||
|
||||
s = socket(res.family, res.socktype, res.protocol);
|
||||
if s == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(s);
|
||||
|
||||
bind(s, res.addr, cast(i32)res.addrlen);
|
||||
listen(s, SOMAXCONN);
|
||||
|
||||
client = accept(s, nil, 0);
|
||||
if client == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(client);
|
||||
|
||||
html :=
|
||||
`HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Content-type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
buf: [1024]byte;
|
||||
for {
|
||||
bytes := recv(client, ^buf[0], cast(i32)buf.count, 0);
|
||||
if bytes > 0 {
|
||||
// fmt.println(buf[:bytes] as string)
|
||||
bytes_sent := send(client, html.data, cast(i32)(html.count-1), 0);
|
||||
if bytes_sent == SOCKET_ERROR {
|
||||
fmt.println("send failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} else if bytes == 0 {
|
||||
fmt.println("Connection closing...");
|
||||
break;
|
||||
} else {
|
||||
fmt.println("recv failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(client, SD_SEND);
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
|
||||
CANVAS_WIDTH :: 128;
|
||||
CANVAS_HEIGHT :: 128;
|
||||
CANVAS_SCALE :: 3;
|
||||
FRAME_TIME :: 1.0/30.0;
|
||||
WINDOW_TITLE :: "Punity\x00";
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||
|
||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
|
||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
|
||||
|
||||
|
||||
STACK_CAPACITY :: 1<<20;
|
||||
STORAGE_CAPACITY :: 1<<20;
|
||||
|
||||
DRAW_LIST_RESERVE :: 128;
|
||||
|
||||
MAX_KEYS :: 256;
|
||||
|
||||
Core :: struct {
|
||||
stack: ^Bank,
|
||||
storage: ^Bank,
|
||||
|
||||
running: bool,
|
||||
key_modifiers: u32,
|
||||
key_states: [MAX_KEYS]byte,
|
||||
key_deltas: [MAX_KEYS]byte,
|
||||
|
||||
perf_frame,
|
||||
perf_frame_inner,
|
||||
perf_step,
|
||||
perf_audio,
|
||||
perf_blit,
|
||||
perf_blit_cvt,
|
||||
perf_blit_gdi: Perf_Span,
|
||||
|
||||
frame: i64,
|
||||
|
||||
canvas: Canvas,
|
||||
draw_list: ^Draw_List,
|
||||
}
|
||||
|
||||
Perf_Span :: struct {
|
||||
stamp: f64,
|
||||
delta: f32,
|
||||
}
|
||||
|
||||
Bank :: struct {
|
||||
memory: []byte,
|
||||
cursor: int,
|
||||
}
|
||||
|
||||
Bank_State :: struct {
|
||||
state: Bank,
|
||||
bank: ^Bank,
|
||||
}
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{a, b, g, r: byte},
|
||||
rgba: u32,
|
||||
}
|
||||
|
||||
Palette :: struct {
|
||||
colors: [256]Color,
|
||||
colors_count: byte,
|
||||
}
|
||||
|
||||
|
||||
Rect :: raw_union {
|
||||
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
||||
using pos: struct {left, top, right, bottom: int},
|
||||
e: [4]int,
|
||||
}
|
||||
|
||||
Bitmap :: struct {
|
||||
pixels: []byte,
|
||||
width: int,
|
||||
height: int,
|
||||
}
|
||||
|
||||
Font :: struct {
|
||||
using bitmap: Bitmap,
|
||||
char_width: int,
|
||||
char_height: int,
|
||||
}
|
||||
|
||||
Canvas :: struct {
|
||||
using bitmap: ^Bitmap,
|
||||
palette: Palette,
|
||||
translate_x: int,
|
||||
translate_y: int,
|
||||
clip: Rect,
|
||||
font: ^Font,
|
||||
}
|
||||
|
||||
DrawFlag :: enum {
|
||||
NONE = 0,
|
||||
FLIP_H = 1<<0,
|
||||
FLIP_V = 1<<1,
|
||||
MASK = 1<<2,
|
||||
}
|
||||
|
||||
Draw_Item :: struct {}
|
||||
Draw_List :: struct {
|
||||
items: []Draw_Item,
|
||||
}
|
||||
|
||||
Key :: enum {
|
||||
MOD_SHIFT = 0x0001,
|
||||
MOD_CONTROL = 0x0002,
|
||||
MOD_ALT = 0x0004,
|
||||
MOD_SUPER = 0x0008,
|
||||
|
||||
UNKNOWN =-1,
|
||||
INVALID =-2,
|
||||
|
||||
LBUTTON = 1,
|
||||
RBUTTON = 2,
|
||||
CANCEL = 3,
|
||||
MBUTTON = 4,
|
||||
|
||||
BACK = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
SHIFT = 16,
|
||||
CONTROL = 17,
|
||||
MENU = 18,
|
||||
PAUSE = 19,
|
||||
CAPITAL = 20,
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 32,
|
||||
PRIOR = 33,
|
||||
NEXT = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT = 37,
|
||||
UP = 38,
|
||||
RIGHT = 39,
|
||||
DOWN = 40,
|
||||
SELECT = 41,
|
||||
PRINT = 42,
|
||||
EXEC = 43,
|
||||
SNAPSHOT = 44,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
HELP = 47,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
|
||||
APOSTROPHE = 39, /* ' */
|
||||
COMMA = 44, /* , */
|
||||
MINUS = 45, /* - */
|
||||
PERIOD = 46, /* . */
|
||||
SLASH = 47, /* / */
|
||||
NUM0 = 48,
|
||||
NUM1 = 49,
|
||||
NUM2 = 50,
|
||||
NUM3 = 51,
|
||||
NUM4 = 52,
|
||||
NUM5 = 53,
|
||||
NUM6 = 54,
|
||||
NUM7 = 55,
|
||||
NUM8 = 56,
|
||||
NUM9 = 57,
|
||||
SEMICOLON = 59, /* ; */
|
||||
EQUAL = 61, /* = */
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LEFT_BRACKET = 91, /* [ */
|
||||
BACKSLASH = 92, /* \ */
|
||||
RIGHT_BRACKET = 93, /* ] */
|
||||
GRAVE_ACCENT = 96, /* ` */
|
||||
};
|
||||
|
||||
|
||||
key_down :: proc(k: Key) -> bool {
|
||||
return _core.key_states[k] != 0;
|
||||
}
|
||||
|
||||
key_pressed :: proc(k: Key) -> bool {
|
||||
return (_core.key_deltas[k] != 0) && key_down(k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
|
||||
_core: Core;
|
||||
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32;
|
||||
|
||||
_core.running = true;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
|
||||
win32_app_key_mods :: proc() -> u32 {
|
||||
mods: u32 = 0;
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= cast(u32)Key.MOD_SHIFT;
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= cast(u32)Key.MOD_CONTROL;
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= cast(u32)Key.MOD_ALT;
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= cast(u32)Key.MOD_SUPER;
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
match msg {
|
||||
case WM_KEYDOWN:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 1;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_KEYUP:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 0;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
_core.running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
window_class := WNDCLASSEXA{
|
||||
class_name = (cast(string)"Punity\x00").data, // C-style string
|
||||
size = size_of(WNDCLASSEXA),
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = cast(HINSTANCE)GetModuleHandleA(nil),
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
|
||||
};
|
||||
|
||||
if RegisterClassExA(^window_class) == 0 {
|
||||
fmt.fprintln(os.stderr, "RegisterClassExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
screen_width := GetSystemMetrics(SM_CXSCREEN);
|
||||
screen_height := GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
rc: RECT;
|
||||
rc.left = (screen_width - WINDOW_WIDTH) / 2;
|
||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
|
||||
rc.right = rc.left + WINDOW_WIDTH;
|
||||
rc.bottom = rc.top + WINDOW_HEIGHT;
|
||||
|
||||
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
assert(AdjustWindowRect(^rc, style, 0) != 0);
|
||||
|
||||
wt := WINDOW_TITLE;
|
||||
|
||||
win32_window := CreateWindowExA(0,
|
||||
window_class.class_name,
|
||||
wt.data,
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
nil, nil, window_class.instance,
|
||||
nil);
|
||||
|
||||
if win32_window == nil {
|
||||
fmt.fprintln(os.stderr, "CreateWindowExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi.size = size_of(BITMAPINFOHEADER);
|
||||
window_bmi.width = CANVAS_WIDTH;
|
||||
window_bmi.height = CANVAS_HEIGHT;
|
||||
window_bmi.planes = 1;
|
||||
window_bmi.bit_count = 32;
|
||||
window_bmi.compression = BI_RGB;
|
||||
|
||||
|
||||
user_init(^_core);
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW);
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
defer free(window_buffer);
|
||||
|
||||
|
||||
for i := 0; i < window_buffer.count; i += 1 {
|
||||
window_buffer[i] = 0xff00ff;
|
||||
}
|
||||
|
||||
|
||||
dt: f64;
|
||||
prev_time := time_now();
|
||||
curr_time := time_now();
|
||||
total_time : f64 = 0;
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
|
||||
message: MSG;
|
||||
for _core.running {
|
||||
curr_time = time_now();
|
||||
dt = curr_time - prev_time;
|
||||
prev_time = curr_time;
|
||||
total_time += dt;
|
||||
|
||||
offset_x += 1;
|
||||
offset_y += 2;
|
||||
|
||||
{
|
||||
data: [128]byte;
|
||||
buf: fmt.Buffer;
|
||||
buf.data = data[:];
|
||||
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
|
||||
win32.SetWindowTextA(win32_window, ^buf[0]);
|
||||
}
|
||||
|
||||
|
||||
for y := 0; y < CANVAS_HEIGHT; y += 1 {
|
||||
for x := 0; x < CANVAS_WIDTH; x += 1 {
|
||||
g := (x % 32) * 8;
|
||||
b := (y % 32) * 8;
|
||||
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
|
||||
}
|
||||
}
|
||||
|
||||
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
|
||||
|
||||
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false;
|
||||
}
|
||||
TranslateMessage(^message);
|
||||
DispatchMessageA(^message);
|
||||
}
|
||||
|
||||
user_step(^_core);
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
window_buffer.data,
|
||||
^window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
ReleaseDC(win32_window, dc);
|
||||
|
||||
|
||||
{
|
||||
delta := time_now() - prev_time;
|
||||
ms := cast(i32)((FRAME_TIME - delta) * 1000);
|
||||
if ms > 0 {
|
||||
win32.Sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
_core.frame += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
user_init :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
user_step :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
run(user_init, user_step);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#import "fmt.odin" as fmt
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Sub Hello!")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*#import "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Hello1!")
|
||||
}*/
|
||||
|
||||
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("hello, world!");
|
||||
}
|
||||
|
||||
|
||||
/*#import "fmt.odin" as .
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello3!")
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*#import "fmt.odin" as _
|
||||
|
||||
thing :: proc() {
|
||||
// println("Hello4!")
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello5!")
|
||||
}*/
|
||||
@@ -1,763 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
#import "os.odin";
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
#import "raw.odin";
|
||||
|
||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
// This could change at a later date if the all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Do not change the order of any of this data
|
||||
// The compiler relies upon this _exact_ order
|
||||
Type_Info_Enum_Value :: raw_union {
|
||||
f: f64,
|
||||
i: i64,
|
||||
}
|
||||
// NOTE(bill): This must match the compiler's
|
||||
Calling_Convention :: enum {
|
||||
ODIN = 0,
|
||||
C = 1,
|
||||
STD = 2,
|
||||
FAST = 3,
|
||||
}
|
||||
|
||||
Type_Info_Record :: struct #ordered {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
usings: []bool, // usings may not be used in tuples
|
||||
packed: bool,
|
||||
ordered: bool,
|
||||
custom_align: bool,
|
||||
}
|
||||
|
||||
Type_Info :: union {
|
||||
size: int,
|
||||
align: int,
|
||||
|
||||
Named{name: string, base: ^Type_Info},
|
||||
Integer{signed: bool},
|
||||
Float{},
|
||||
Complex{},
|
||||
Quaternion{},
|
||||
String{},
|
||||
Boolean{},
|
||||
Any{},
|
||||
Pointer{
|
||||
elem: ^Type_Info, // nil -> rawptr
|
||||
},
|
||||
Atomic{elem: ^Type_Info},
|
||||
Procedure{
|
||||
params: ^Type_Info, // Type_Info.Tuple
|
||||
results: ^Type_Info, // Type_Info.Tuple
|
||||
variadic: bool,
|
||||
convention: Calling_Convention,
|
||||
},
|
||||
Array{
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
count: int,
|
||||
},
|
||||
Dynamic_Array{elem: ^Type_Info, elem_size: int},
|
||||
Slice {elem: ^Type_Info, elem_size: int},
|
||||
Vector {elem: ^Type_Info, elem_size, count: int},
|
||||
Tuple {using record: Type_Info_Record}, // Only really used for procedures
|
||||
Struct {using record: Type_Info_Record},
|
||||
Raw_Union {using record: Type_Info_Record},
|
||||
Union{
|
||||
common_fields: struct {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
},
|
||||
variant_names: []string,
|
||||
variant_types: []^Type_Info,
|
||||
},
|
||||
Enum{
|
||||
base: ^Type_Info,
|
||||
names: []string,
|
||||
values: []Type_Info_Enum_Value,
|
||||
},
|
||||
Map{
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
generated_struct: ^Type_Info,
|
||||
count: int, // == 0 if dynamic
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): only the ones that are needed (not all types)
|
||||
// This will be set by the compiler
|
||||
__type_table: []Type_Info;
|
||||
|
||||
__argv__: ^^byte;
|
||||
__argc__: i32;
|
||||
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case Type_Info.Named:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
type_info_base_without_enum :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case Type_Info.Named:
|
||||
base = i.base;
|
||||
case Type_Info.Enum:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
|
||||
assume :: proc(cond: bool) #foreign __llvm_core "llvm.assume";
|
||||
|
||||
__debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
|
||||
__trap :: proc() #foreign __llvm_core "llvm.trap";
|
||||
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
Allocator_Mode :: enum u8 {
|
||||
ALLOC,
|
||||
FREE,
|
||||
FREE_ALL,
|
||||
RESIZE,
|
||||
}
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
||||
Allocator :: struct #ordered {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
Context :: struct #ordered {
|
||||
thread_id: int,
|
||||
|
||||
allocator: Allocator,
|
||||
|
||||
user_data: rawptr,
|
||||
user_index: int,
|
||||
}
|
||||
|
||||
#thread_local __context: Context;
|
||||
|
||||
|
||||
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
||||
|
||||
|
||||
__check_context :: proc() {
|
||||
c := &__context;
|
||||
|
||||
if c.allocator.procedure == nil {
|
||||
c.allocator = default_allocator();
|
||||
}
|
||||
if c.thread_id == 0 {
|
||||
c.thread_id = os.current_thread_id();
|
||||
}
|
||||
}
|
||||
|
||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT); }
|
||||
|
||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
if a.procedure == nil {
|
||||
return;
|
||||
}
|
||||
a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr :: proc(ptr: rawptr) #inline {
|
||||
__check_context();
|
||||
free_ptr_with_allocator(context.allocator, ptr);
|
||||
}
|
||||
|
||||
free_all :: proc() #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
a.procedure(a.data, Allocator_Mode.FREE_ALL, 0, 0, nil, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT); }
|
||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.RESIZE, new_size, alignment, ptr, old_size, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
|
||||
if old_memory == nil {
|
||||
return alloc_align(new_size, alignment);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc_align(new_size, alignment);
|
||||
if new_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
__mem_copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
|
||||
match mode {
|
||||
case ALLOC:
|
||||
return os.heap_alloc(size);
|
||||
|
||||
case FREE:
|
||||
os.heap_free(old_memory);
|
||||
return nil;
|
||||
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case RESIZE:
|
||||
ptr := os.heap_resize(old_memory, size);
|
||||
assert(ptr != nil);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__string_eq :: proc(a, b: string) -> bool {
|
||||
if len(a) != len(b) {
|
||||
return false;
|
||||
}
|
||||
if len(a) == 0 {
|
||||
return true;
|
||||
}
|
||||
if &a[0] == &b[0] {
|
||||
return true;
|
||||
}
|
||||
return __string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b: string) -> int {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
|
||||
__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; }
|
||||
__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; }
|
||||
__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; }
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
|
||||
|
||||
|
||||
__complex64_eq :: proc(a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex64_ne :: proc(a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
__quaternion128_eq :: proc(a, b: quaternion128) -> bool #inline {
|
||||
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
|
||||
}
|
||||
__quaternion128_ne :: proc(a, b: quaternion128) -> bool #inline {
|
||||
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
|
||||
}
|
||||
__quaternion256_eq :: proc(a, b: quaternion256) -> bool #inline {
|
||||
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
|
||||
}
|
||||
__quaternion256_ne :: proc(a, b: quaternion256) -> bool #inline {
|
||||
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
|
||||
}
|
||||
|
||||
__assert :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__panic :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__bounds_check_error :: proc(file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
|
||||
file, line, column, index, count);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__slice_expr_error :: proc(file: string, line, column: int, low, high, max: int) {
|
||||
if 0 <= low && low <= high && high <= max {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..<%d..<%d]\n",
|
||||
file, line, column, low, high, max);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..<%d]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__type_assertion_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
|
||||
if !ok {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
|
||||
file, line, column, from, to);
|
||||
__debug_trap();
|
||||
}
|
||||
}
|
||||
|
||||
__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||
return utf8.decode_rune(s);
|
||||
}
|
||||
|
||||
|
||||
__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
|
||||
llvm_memset_64bit(data, byte(value), len, 1, false);
|
||||
return data;
|
||||
}
|
||||
__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_set(data, 0, len);
|
||||
}
|
||||
__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
__mem_compare :: proc(a, b: ^byte, n: int) -> int {
|
||||
for i in 0..<n {
|
||||
match {
|
||||
case (a+i)^ < (b+i)^:
|
||||
return -1;
|
||||
case (a+i)^ > (b+i)^:
|
||||
return +1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
__sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
__abs_complex64 :: proc(x: complex64) -> f32 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f32(r*r + i*i);
|
||||
}
|
||||
__abs_complex128 :: proc(x: complex128) -> f64 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f64(r*r + i*i);
|
||||
}
|
||||
__abs_quaternion128 :: proc(x: quaternion128) -> f32 #inline {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return __sqrt_f32(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
__abs_quaternion256 :: proc(x: quaternion256) -> f64 #inline {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return __sqrt_f64(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
__check_context();
|
||||
array.allocator = context.allocator;
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
if cap > 0 {
|
||||
__dynamic_array_reserve(array_, elem_size, elem_align, cap);
|
||||
array.len = len;
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int) -> bool {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
if cap <= array.cap {
|
||||
return true;
|
||||
}
|
||||
|
||||
__check_context();
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator;
|
||||
}
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
old_size := array.cap * elem_size;
|
||||
new_size := cap * elem_size;
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
|
||||
if new_data == nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
array.data = new_data;
|
||||
array.cap = cap;
|
||||
return true;
|
||||
}
|
||||
|
||||
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int) -> bool {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len);
|
||||
if ok {
|
||||
array.len = len;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return array.len;
|
||||
}
|
||||
|
||||
|
||||
ok := true;
|
||||
if array.cap <= array.len+item_count {
|
||||
cap := 2 * array.cap + max(8, item_count);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.len;
|
||||
}
|
||||
data := ^byte(array.data);
|
||||
assert(data != nil);
|
||||
__mem_copy(data + (elem_size*array.len), items, elem_size * item_count);
|
||||
array.len += item_count;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
ok := true;
|
||||
if array.cap <= array.len+1 {
|
||||
cap := 2 * array.cap + max(8, 1);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.len;
|
||||
}
|
||||
data := ^byte(array.data);
|
||||
assert(data != nil);
|
||||
__mem_zero(data + (elem_size*array.len), elem_size);
|
||||
array.len++;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
slice := ^raw.Slice(slice_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
item_count = min(slice.cap-slice.len, item_count);
|
||||
if item_count > 0 {
|
||||
data := ^byte(slice.data);
|
||||
assert(data != nil);
|
||||
__mem_copy(data + (elem_size*slice.len), items, elem_size * item_count);
|
||||
slice.len += item_count;
|
||||
}
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
|
||||
// Map stuff
|
||||
|
||||
__default_hash :: proc(data: []byte) -> u64 {
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
return fnv64a(data);
|
||||
}
|
||||
__default_hash_string :: proc(s: string) -> u64 {
|
||||
return __default_hash([]byte(s));
|
||||
}
|
||||
|
||||
__INITIAL_MAP_CAP :: 16;
|
||||
|
||||
__Map_Key :: struct #ordered {
|
||||
hash: u64,
|
||||
str: string,
|
||||
}
|
||||
|
||||
__Map_Find_Result :: struct #ordered {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
__Map_Entry_Header :: struct #ordered {
|
||||
key: __Map_Key,
|
||||
next: int,
|
||||
/*
|
||||
value: Value_Type,
|
||||
*/
|
||||
}
|
||||
|
||||
__Map_Header :: struct #ordered {
|
||||
m: ^raw.Dynamic_Map,
|
||||
is_key_string: bool,
|
||||
entry_size: int,
|
||||
entry_align: int,
|
||||
value_offset: int,
|
||||
value_size: int,
|
||||
}
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: __Map_Header, cap: int) {
|
||||
__dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap);
|
||||
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap);
|
||||
}
|
||||
|
||||
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
|
||||
new_header: __Map_Header = header;
|
||||
nm: raw.Dynamic_Map;
|
||||
new_header.m = &nm;
|
||||
|
||||
header_hashes := ^raw.Dynamic_Array(&header.m.hashes);
|
||||
nm_hashes := ^raw.Dynamic_Array(&nm.hashes);
|
||||
|
||||
__dynamic_array_resize(nm_hashes, size_of(int), align_of(int), new_count);
|
||||
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len);
|
||||
for i in 0..<new_count {
|
||||
nm.hashes[i] = -1;
|
||||
}
|
||||
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
if len(nm.hashes) == 0 {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
|
||||
entry_header := __dynamic_map_get_entry(header, i);
|
||||
data := ^byte(entry_header);
|
||||
|
||||
fr := __dynamic_map_find(new_header, entry_header.key);
|
||||
j := __dynamic_map_add_entry(new_header, entry_header.key);
|
||||
if fr.entry_prev < 0 {
|
||||
nm.hashes[fr.hash_index] = j;
|
||||
} else {
|
||||
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
|
||||
e.next = j;
|
||||
}
|
||||
|
||||
e := __dynamic_map_get_entry(new_header, j);
|
||||
e.next = fr.entry_index;
|
||||
ndata := ^byte(e);
|
||||
__mem_copy(ndata+value_offset, data+value_offset, value_size);
|
||||
|
||||
if __dynamic_map_full(new_header) {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
}
|
||||
free_ptr_with_allocator(header_hashes.allocator, header_hashes.data);
|
||||
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
|
||||
header.m^ = nm;
|
||||
}
|
||||
|
||||
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
|
||||
index := __dynamic_map_find(h, key).entry_index;
|
||||
if index >= 0 {
|
||||
data := ^byte(__dynamic_map_get_entry(h, index));
|
||||
val := data + h.value_offset;
|
||||
return val;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
|
||||
index: int;
|
||||
assert(value != nil);
|
||||
|
||||
|
||||
if len(m.hashes) == 0 {
|
||||
__dynamic_map_reserve(h, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = __dynamic_map_add_entry(h, key);
|
||||
if fr.entry_prev >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_prev);
|
||||
entry.next = index;
|
||||
} else {
|
||||
m.hashes[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
{
|
||||
e := __dynamic_map_get_entry(h, index);
|
||||
e.key = key;
|
||||
val := ^byte(e) + value_offset;
|
||||
__mem_copy(val, value, value_size);
|
||||
}
|
||||
|
||||
if __dynamic_map_full(h) {
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_grow :: proc(using h: __Map_Header) {
|
||||
new_count := max(2*m.entries.cap + 8, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_rehash(h, new_count);
|
||||
}
|
||||
|
||||
__dynamic_map_full :: proc(using h: __Map_Header) -> bool {
|
||||
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
|
||||
if a.hash == b.hash {
|
||||
if h.is_key_string {
|
||||
return a.str == b.str;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
|
||||
fr := __Map_Find_Result{-1, -1, -1};
|
||||
if len(m.hashes) > 0 {
|
||||
fr.hash_index = int(key.hash % u64(len(m.hashes)));
|
||||
fr.entry_index = m.hashes[fr.hash_index];
|
||||
for fr.entry_index >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
if __dynamic_map_hash_equal(h, entry.key, key) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry.next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
|
||||
prev := m.entries.len;
|
||||
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align);
|
||||
if c != prev {
|
||||
end := __dynamic_map_get_entry(h, c-1);
|
||||
end.key = key;
|
||||
end.next = -1;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) {
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
__dynamic_map_erase(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
|
||||
data := ^byte(m.entries.data) + index*entry_size;
|
||||
return ^__Map_Entry_Header(data);
|
||||
}
|
||||
|
||||
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
} else {
|
||||
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == m.entries.len-1 {
|
||||
m.entries.len--;
|
||||
}
|
||||
__mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.len-1), entry_size);
|
||||
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
|
||||
if last.entry_prev >= 0 {
|
||||
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
m.hashes[last.hash_index] = fr.entry_index;
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
|
||||
// import "fmt.odin";
|
||||
|
||||
// proc __u128_mod(a, b: u128) -> u128 #link_name "__umodti3" {
|
||||
// var _, r := __u128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __u128_quo(a, b: u128) -> u128 #link_name "__udivti3" {
|
||||
// var n, _ := __u128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_mod(a, b: i128) -> i128 #link_name "__modti3" {
|
||||
// var _, r := __i128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __i128_quo(a, b: i128) -> i128 #link_name "__divti3" {
|
||||
// var n, _ := __i128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_quo_mod(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
||||
// var s := b >> 127
|
||||
// b = (b ~ s) - s
|
||||
// s = a >> 127
|
||||
// a = (a ~ s) - s
|
||||
|
||||
// var n, r := __u128_quo_mod(a as u128, b as u128)
|
||||
// return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
||||
// }
|
||||
|
||||
|
||||
// proc __u128_quo_mod(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
||||
// proc clz(x: u64) -> u64 {
|
||||
// proc clz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
||||
// return clz_u64(x, false)
|
||||
// }
|
||||
// proc ctz(x: u64) -> u64 {
|
||||
// proc ctz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
||||
// return ctz_u64(x, false)
|
||||
// }
|
||||
|
||||
|
||||
// u128_lo_hi :: raw_union {
|
||||
// all: u128
|
||||
// using _lohi: struct {lo, hi: u64;}
|
||||
// }
|
||||
|
||||
// n, d, q, r: u128_lo_hi
|
||||
// sr: u64
|
||||
|
||||
// n.all = a
|
||||
// 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
|
||||
// }
|
||||
// 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 sr > 64 - 2 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// if d.hi == 0 {
|
||||
// if (d.lo & (d.lo - 1)) == 0 {
|
||||
// var 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
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!==0;
|
||||
true :: 0==0;
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
|
||||
byte :: u8; // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
|
||||
rune :: rune;
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
|
||||
typeid :: typeid;
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
|
||||
// Procedures
|
||||
len :: proc(array: Array_Type) -> int ---
|
||||
cap :: proc(array: Array_Type) -> int ---
|
||||
|
||||
size_of :: proc($T: typeid) -> int ---
|
||||
align_of :: proc($T: typeid) -> int ---
|
||||
offset_of :: proc($T: typeid) -> uintptr ---
|
||||
type_of :: proc(x: expr) -> type ---
|
||||
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
|
||||
typeid_of :: proc($T: typeid) -> typeid ---
|
||||
|
||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||
|
||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
|
||||
min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
abs :: proc(value: T) -> T ---
|
||||
clamp :: proc(value, minimum, maximum: T) -> T ---
|
||||
@@ -0,0 +1,35 @@
|
||||
package c
|
||||
|
||||
import b "core:builtin"
|
||||
|
||||
CHAR_BIT :: 8;
|
||||
|
||||
bool :: b.bool;
|
||||
char :: b.u8;
|
||||
byte :: b.byte;
|
||||
schar :: b.i8;
|
||||
uchar :: b.u8;
|
||||
short :: b.i16;
|
||||
ushort :: b.u16;
|
||||
int :: b.i32;
|
||||
uint :: b.u32;
|
||||
|
||||
long :: b.i32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.i64;
|
||||
ulong :: b.u32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.u64;
|
||||
|
||||
longlong :: b.i64;
|
||||
ulonglong :: b.u64;
|
||||
float :: b.f32;
|
||||
double :: b.f64;
|
||||
complex_float :: b.complex64;
|
||||
complex_double :: b.complex128;
|
||||
|
||||
#assert(size_of(b.uintptr) == size_of(b.int));
|
||||
|
||||
size_t :: b.uint;
|
||||
ssize_t :: b.int;
|
||||
ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
|
||||
wchar_t :: b.u16 when (ODIN_OS == "windows") else b.u32;
|
||||
@@ -0,0 +1,213 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
Array :: struct(T: typeid) {
|
||||
data: ^T,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
/*
|
||||
array_init :: proc {
|
||||
array_init_none,
|
||||
array_init_len,
|
||||
array_init_len_cap,
|
||||
}
|
||||
array_init
|
||||
array_delete
|
||||
array_len
|
||||
array_cap
|
||||
array_space
|
||||
array_slice
|
||||
array_get
|
||||
array_get_ptr
|
||||
array_set
|
||||
array_reserve
|
||||
array_resize
|
||||
array_push = array_append :: proc{
|
||||
array_push_back,
|
||||
array_push_back_elems,
|
||||
}
|
||||
array_push_front
|
||||
array_pop_back
|
||||
array_pop_front
|
||||
array_consume
|
||||
array_trim
|
||||
array_clear
|
||||
array_clone
|
||||
array_set_capacity
|
||||
array_grow
|
||||
*/
|
||||
|
||||
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
|
||||
array_init_len(a, 0, allocator);
|
||||
}
|
||||
array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
|
||||
array_init_len_cap(a, 0, 16, allocator);
|
||||
}
|
||||
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
|
||||
a.allocator = allocator;
|
||||
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator));
|
||||
a.len = len;
|
||||
a.cap = cap;
|
||||
}
|
||||
|
||||
array_init :: proc{array_init_none, array_init_len, array_init_len_cap};
|
||||
|
||||
array_delete :: proc(a: $A/Array) {
|
||||
mem.free(a.data, a.allocator);
|
||||
}
|
||||
|
||||
array_len :: proc(a: $A/Array) -> int {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
array_cap :: proc(a: $A/Array) -> int {
|
||||
return a.cap;
|
||||
}
|
||||
|
||||
array_space :: proc(a: $A/Array) -> int {
|
||||
return a.cap - a.len;
|
||||
}
|
||||
|
||||
array_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.len};
|
||||
return transmute([]T)s;
|
||||
}
|
||||
|
||||
array_cap_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.cap};
|
||||
return transmute([]T)s;
|
||||
}
|
||||
|
||||
array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a));
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^;
|
||||
}
|
||||
array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a));
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index));
|
||||
}
|
||||
|
||||
array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a^));
|
||||
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item;
|
||||
}
|
||||
|
||||
|
||||
array_reserve :: proc(a: ^$A/Array, capacity: int) {
|
||||
if capacity > a.len {
|
||||
array_set_capacity(a, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
array_resize :: proc(a: ^$A/Array, length: int) {
|
||||
if length > a.len {
|
||||
array_set_capacity(a, length);
|
||||
}
|
||||
a.len = length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
array_push_back :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a);
|
||||
}
|
||||
|
||||
a.len += 1;
|
||||
array_set(a, a.len-1, item);
|
||||
}
|
||||
|
||||
array_push_front :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a);
|
||||
}
|
||||
|
||||
a.len += 1;
|
||||
data := array_slice(a^);
|
||||
copy(data[1:], data[:]);
|
||||
data[0] = item;
|
||||
}
|
||||
|
||||
array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := array_get(a^, a.len-1);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := array_get(a^, 0);
|
||||
s := array_slice(a^);
|
||||
copy(s[:], s[1:]);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc);
|
||||
a.len -= count;
|
||||
}
|
||||
|
||||
|
||||
array_trim :: proc(a: ^$A/Array($T)) {
|
||||
array_set_capacity(a, a.len);
|
||||
}
|
||||
|
||||
array_clear :: proc(a: ^$A/Array($T)) {
|
||||
array_resize(a, 0);
|
||||
}
|
||||
|
||||
array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
|
||||
res: A;
|
||||
array_init(&res, array_len(a), array_len(a), allocator);
|
||||
copy(array_slice(res), array_slice(a));
|
||||
return res;
|
||||
}
|
||||
|
||||
array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
|
||||
if array_space(a^) < len(items) {
|
||||
array_grow(a, a.len + len(items));
|
||||
}
|
||||
offset := a.len;
|
||||
data := array_cap_slice(a^);
|
||||
n := copy(data[a.len:], items);
|
||||
a.len += n;
|
||||
}
|
||||
|
||||
array_push :: proc{array_push_back, array_push_back_elems};
|
||||
array_append :: proc{array_push_back, array_push_back_elems};
|
||||
|
||||
array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
|
||||
if new_capacity == a.cap {
|
||||
return;
|
||||
}
|
||||
|
||||
if new_capacity < a.len {
|
||||
array_resize(a, new_capacity);
|
||||
}
|
||||
|
||||
new_data: ^T;
|
||||
if new_capacity > 0 {
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator;
|
||||
}
|
||||
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator));
|
||||
if new_data != nil {
|
||||
mem.copy(new_data, a.data, size_of(T)*a.len);
|
||||
}
|
||||
}
|
||||
mem.free(a.data, a.allocator);
|
||||
a.data = new_data;
|
||||
a.cap = new_capacity;
|
||||
}
|
||||
array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(a^)*2 + 8, min_capacity);
|
||||
array_set_capacity(a, new_capacity);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Bloom_Hash_Proc :: #type proc(data: []byte) -> u32;
|
||||
|
||||
Bloom_Hash :: struct {
|
||||
hash_proc: Bloom_Hash_Proc,
|
||||
next: ^Bloom_Hash,
|
||||
}
|
||||
|
||||
Bloom_Filter :: struct {
|
||||
allocator: mem.Allocator,
|
||||
hash: ^Bloom_Hash,
|
||||
bits: []byte,
|
||||
}
|
||||
|
||||
bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
|
||||
b.allocator = allocator;
|
||||
b.bits = make([]byte, size, allocator);
|
||||
}
|
||||
|
||||
bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
|
||||
context.allocator = b.allocator;
|
||||
delete(b.bits);
|
||||
for b.hash != nil {
|
||||
hash := b.hash;
|
||||
b.hash = b.hash.next;
|
||||
free(hash);
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
|
||||
context.allocator = b.allocator;
|
||||
h := new(Bloom_Hash);
|
||||
h.hash_proc = hash_proc;
|
||||
|
||||
head := &b.hash;
|
||||
for head^ != nil {
|
||||
head = &(head^.next);
|
||||
}
|
||||
head^ = h;
|
||||
}
|
||||
|
||||
bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item);
|
||||
hash %= u32(len(b.bits) * 8);
|
||||
b.bits[hash >> 3] |= 1 << (hash & 3);
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
|
||||
bloom_filter_add(b, transmute([]byte)item);
|
||||
}
|
||||
|
||||
bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
|
||||
item := mem.slice_ptr((^byte)(data), size);
|
||||
bloom_filter_add(b, item);
|
||||
}
|
||||
|
||||
bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item);
|
||||
hash %= u32(len(b.bits) * 8);
|
||||
if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
|
||||
return bloom_filter_test(b, transmute([]byte)item);
|
||||
}
|
||||
|
||||
bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
|
||||
item := mem.slice_ptr((^byte)(data), size);
|
||||
return bloom_filter_test(b, item);
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
package container
|
||||
|
||||
|
||||
Map :: struct(Value: typeid) {
|
||||
hash: Array(int),
|
||||
entries: Array(Map_Entry(Value)),
|
||||
}
|
||||
|
||||
Map_Entry :: struct(Value: typeid) {
|
||||
key: u64,
|
||||
next: int,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map_init :: proc{
|
||||
map_init_none,
|
||||
map_init_cap,
|
||||
}
|
||||
map_delete
|
||||
|
||||
map_has
|
||||
map_get
|
||||
map_get_default
|
||||
map_get_ptr
|
||||
map_set
|
||||
map_remove
|
||||
map_reserve
|
||||
map_clear
|
||||
|
||||
// Multi Map
|
||||
|
||||
multi_map_find_first
|
||||
multi_map_find_next
|
||||
multi_map_count
|
||||
multi_map_get :: proc{
|
||||
multi_map_get_array,
|
||||
multi_map_get_slice,
|
||||
};
|
||||
multi_map_get_as_slice
|
||||
multi_map_insert
|
||||
multi_map_remove
|
||||
multi_map_remove_all
|
||||
|
||||
*/
|
||||
|
||||
map_init :: proc{map_init_none, map_init_cap};
|
||||
|
||||
map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
}
|
||||
|
||||
map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
map_reserve(m, cap);
|
||||
}
|
||||
|
||||
map_delete :: proc(m: $M/Map($Value)) {
|
||||
array_delete(m.hash);
|
||||
array_delete(m.entries);
|
||||
}
|
||||
|
||||
|
||||
map_has :: proc(m: $M/Map($Value), key: u64) -> bool {
|
||||
return _map_find_or_fail(m, key) >= 0;
|
||||
}
|
||||
|
||||
map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return {}, false;
|
||||
}
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return default, false;
|
||||
}
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
}
|
||||
return array_get_ptr(m.entries, i).value;
|
||||
}
|
||||
|
||||
map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
|
||||
i := _map_find_or_make(m, key);
|
||||
array_get_ptr(m.entries, i).value = value;
|
||||
if _map_full(m^) {
|
||||
_map_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
map_remove :: proc(m: ^$M/Map($Value), key: u64) {
|
||||
fr := _map_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
|
||||
nm: M;
|
||||
map_init(&nm, m.hash.allocator);
|
||||
array_resize(&nm.hash, new_size);
|
||||
array_reserve(&nm.entries, array_len(m.entries));
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1);
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i);
|
||||
multi_map_insert(&nm, e.key, e.value);
|
||||
}
|
||||
|
||||
map_delete(m^);
|
||||
m^ = nm;
|
||||
}
|
||||
|
||||
map_clear :: proc(m: ^$M/Map($Value)) {
|
||||
array_clear(&m.hash);
|
||||
array_clear(&m.entries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
}
|
||||
return array_get_ptr(m.entries, i);
|
||||
}
|
||||
|
||||
multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
|
||||
i := e.next;
|
||||
for i >= 0 {
|
||||
it := array_get_ptr(m.entries, i);
|
||||
if it.key == e.key {
|
||||
return it;
|
||||
}
|
||||
i = it.next;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
|
||||
n := 0;
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
n += 1;
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
|
||||
|
||||
multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
|
||||
if items == nil do return;
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
array_append(items, e.value);
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
|
||||
e := multi_map_find_first(m, key);
|
||||
i := 0;
|
||||
for e != nil && i < len(items) {
|
||||
items[i] = e.value;
|
||||
i += 1;
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
|
||||
items: Array(Value);
|
||||
array_init(&items, 0);
|
||||
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
array_append(&items, e.value);
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
|
||||
return array_slice(items);
|
||||
}
|
||||
|
||||
|
||||
multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
|
||||
i := _map_make(m, key);
|
||||
array_get_ptr(m.entries, i).value = value;
|
||||
if _map_full(m^) {
|
||||
_map_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) {
|
||||
fr := _map_find_entry(m, e);
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) {
|
||||
for map_exist(m^, key) {
|
||||
map_remove(m, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
|
||||
Map_Find_Result :: struct {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
e: Map_Entry(Value);
|
||||
e.key = key;
|
||||
e.next = -1;
|
||||
idx := array_len(m.entries);
|
||||
array_push(&m.entries, e);
|
||||
return idx;
|
||||
}
|
||||
|
||||
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries);
|
||||
return;
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
|
||||
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key);
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it.key == key {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it == e {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int {
|
||||
return _map_find_key(m, key).entry_index;
|
||||
}
|
||||
_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
fr := _map_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index;
|
||||
}
|
||||
|
||||
i := _map_add_entry(m, key);
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
fr := _map_find_key(m^, key);
|
||||
i := _map_add_entry(m, key);
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_map_full :: proc(m: $M/Map($Value)) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
|
||||
}
|
||||
|
||||
_map_grow :: proc(m: ^$M/Map($Value)) {
|
||||
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
|
||||
map_reserve(m, new_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package container
|
||||
|
||||
Queue :: struct(T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
offset: int,
|
||||
}
|
||||
|
||||
/*
|
||||
queue_init :: proc{
|
||||
queue_init_none,
|
||||
queue_init_len,
|
||||
queue_init_len_cap,
|
||||
}
|
||||
queue_delete
|
||||
queue_clear
|
||||
queue_len
|
||||
queue_cap
|
||||
queue_space
|
||||
queue_get
|
||||
queue_set
|
||||
queue_reserve
|
||||
queue_resize
|
||||
queue_push :: proc{
|
||||
queue_push_back,
|
||||
queue_push_elems,
|
||||
};
|
||||
queue_push_front
|
||||
queue_pop_front
|
||||
queue_pop_back
|
||||
queue_consume
|
||||
*/
|
||||
|
||||
queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
|
||||
queue_init_len(q, 0, allocator);
|
||||
}
|
||||
queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
|
||||
queue_init_len_cap(q, 0, 16, allocator);
|
||||
}
|
||||
queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
|
||||
array_init(&q.data, len, cap, allocator);
|
||||
q.len = len;
|
||||
q.offset = 0;
|
||||
}
|
||||
|
||||
queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap};
|
||||
|
||||
queue_delete :: proc(q: $Q/Queue($T)) {
|
||||
array_delete(q.data);
|
||||
}
|
||||
|
||||
queue_clear :: proc(q: ^$Q/Queue($T)) {
|
||||
q.len = 0;
|
||||
}
|
||||
|
||||
queue_len :: proc(q: $Q/Queue($T)) -> int {
|
||||
return q.len;
|
||||
}
|
||||
|
||||
queue_cap :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_cap(q.data);
|
||||
}
|
||||
|
||||
queue_space :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_len(q.data) - q.len;
|
||||
}
|
||||
|
||||
queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
|
||||
i := (index + q.offset) % array_len(q.data);
|
||||
data := array_slice(q.data);
|
||||
return data[i];
|
||||
}
|
||||
|
||||
queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T) {
|
||||
i := (index + q.offset) % array_len(q.data);
|
||||
data := array_slice(q.data);
|
||||
data[i] = item;
|
||||
}
|
||||
|
||||
|
||||
queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
|
||||
if capacity > q.len {
|
||||
_queue_increase_capacity(q, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
|
||||
if length > q.len {
|
||||
_queue_increase_capacity(q, length);
|
||||
}
|
||||
q.len = length;
|
||||
}
|
||||
|
||||
queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q);
|
||||
}
|
||||
|
||||
queue_set(q, q.len, item);
|
||||
q.len += 1;
|
||||
}
|
||||
|
||||
queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q);
|
||||
}
|
||||
|
||||
q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data);
|
||||
q.len += 1;
|
||||
queue_set(q, 0, item);
|
||||
}
|
||||
|
||||
queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
item := queue_get(q^, 0);
|
||||
q.offset = (q.offset + 1) % array_len(q.data);
|
||||
q.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
item := queue_get(q^, q.len-1);
|
||||
q.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
|
||||
q.offset = (q.offset + count) & array_len(q.data);
|
||||
q.len -= count;
|
||||
}
|
||||
|
||||
|
||||
queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
|
||||
if queue_space(q^) < len(items) {
|
||||
_queue_grow(q, q.len + len(items));
|
||||
}
|
||||
size := array_len(q.data);
|
||||
insert := (q.offset + q.len) % size;
|
||||
|
||||
to_insert := len(items);
|
||||
if insert + to_insert > size {
|
||||
to_insert = size - insert;
|
||||
}
|
||||
|
||||
the_items := items[:];
|
||||
|
||||
data := array_slice(q.data);
|
||||
|
||||
q.len += copy(data[insert:][:to_insert], the_items);
|
||||
the_items = the_items[to_insert:];
|
||||
q.len += copy(data[:], the_items);
|
||||
}
|
||||
|
||||
queue_push :: proc{queue_push_back, queue_push_elems};
|
||||
|
||||
|
||||
|
||||
_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
|
||||
end := array_len(q.data);
|
||||
array_resize(&q.data, new_capacity);
|
||||
if q.offset + q.len > end {
|
||||
end_items := q.len + end;
|
||||
data := array_slice(q.data);
|
||||
copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items]);
|
||||
q.offset += new_capacity - end;
|
||||
}
|
||||
}
|
||||
_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
|
||||
_queue_increase_capacity(q, new_capacity);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package container
|
||||
|
||||
|
||||
Ring :: struct(T: typeid) {
|
||||
next, prev: ^Ring(T),
|
||||
value: T,
|
||||
}
|
||||
|
||||
ring_init :: proc(r: ^$R/Ring) -> ^R {
|
||||
r.prev, r.next = r, r;
|
||||
return r;
|
||||
}
|
||||
|
||||
ring_next :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.next == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
return r.next;
|
||||
}
|
||||
ring_prev :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.prev == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
return r.prev;
|
||||
}
|
||||
|
||||
|
||||
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
if r.next == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
|
||||
switch {
|
||||
case n < 0:
|
||||
for _ in n..<0 {
|
||||
r = r.prev;
|
||||
}
|
||||
case n > 0:
|
||||
for _ in 0..<n {
|
||||
r = r.next;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ring_link :: proc(r, s: ^$R/Ring) -> ^R {
|
||||
n := ring_next(r);
|
||||
if s != nil {
|
||||
p := ring_prev(s);
|
||||
r.next = s;
|
||||
s.prev = r;
|
||||
n.prev = p;
|
||||
p.next = n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
if n <= 0 {
|
||||
return nil;
|
||||
}
|
||||
return ring_link(r, ring_move(r, n+1));
|
||||
}
|
||||
ring_len :: proc(r: ^$R/Ring) -> int {
|
||||
n := 0;
|
||||
if r != nil {
|
||||
n = 1;
|
||||
for p := ring_next(p); p != r; p = p.next {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package container
|
||||
|
||||
Set :: struct {
|
||||
hash: Array(int),
|
||||
entries: Array(Set_Entry),
|
||||
}
|
||||
|
||||
Set_Entry :: struct {
|
||||
key: u64,
|
||||
next: int,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
set_init :: proc{
|
||||
set_init_none,
|
||||
set_init_cap,
|
||||
}
|
||||
set_delete
|
||||
|
||||
set_in
|
||||
set_not_in
|
||||
set_add
|
||||
set_remove
|
||||
set_reserve
|
||||
set_clear
|
||||
*/
|
||||
|
||||
set_init :: proc{set_init_none, set_init_cap};
|
||||
|
||||
set_init_none :: proc(m: ^Set, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
}
|
||||
|
||||
set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
set_reserve(m, cap);
|
||||
}
|
||||
|
||||
set_delete :: proc(m: Set) {
|
||||
array_delete(m.hash);
|
||||
array_delete(m.entries);
|
||||
}
|
||||
|
||||
|
||||
set_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) >= 0;
|
||||
}
|
||||
set_not_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) < 0;
|
||||
}
|
||||
|
||||
set_add :: proc(m: ^Set, key: u64) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_set_grow(m);
|
||||
}
|
||||
|
||||
_ = _set_find_or_make(m, key);
|
||||
if _set_full(m^) {
|
||||
_set_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
set_remove :: proc(m: ^Set, key: u64) {
|
||||
fr := _set_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
_set_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set_reserve :: proc(m: ^Set, new_size: int) {
|
||||
nm: Set;
|
||||
set_init(&nm, m.hash.allocator);
|
||||
array_resize(&nm.hash, new_size);
|
||||
array_reserve(&nm.entries, array_len(m.entries));
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1);
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i);
|
||||
set_add(&nm, e.key);
|
||||
}
|
||||
|
||||
set_delete(m^);
|
||||
m^ = nm;
|
||||
}
|
||||
|
||||
set_clear :: proc(m: ^Set) {
|
||||
array_clear(&m.hash);
|
||||
array_clear(&m.entries);
|
||||
}
|
||||
|
||||
|
||||
set_equal :: proc(a, b: Set) -> bool {
|
||||
a_entries := array_slice(a.entries);
|
||||
b_entries := array_slice(b.entries);
|
||||
if len(a_entries) != len(b_entries) {
|
||||
return false;
|
||||
}
|
||||
for e in a_entries {
|
||||
if set_not_in(b, e.key) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
_set_add_entry :: proc(m: ^Set, key: u64) -> int {
|
||||
e: Set_Entry;
|
||||
e.key = key;
|
||||
e.next = -1;
|
||||
idx := array_len(m.entries);
|
||||
array_push(&m.entries, e);
|
||||
return idx;
|
||||
}
|
||||
|
||||
_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries);
|
||||
return;
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
|
||||
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key);
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it.key == key {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it == e {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_set_find_or_fail :: proc(m: Set, key: u64) -> int {
|
||||
return _set_find_key(m, key).entry_index;
|
||||
}
|
||||
_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index;
|
||||
}
|
||||
|
||||
i := _set_add_entry(m, key);
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_set_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key);
|
||||
i := _set_add_entry(m, key);
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_set_full :: proc(m: Set) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
|
||||
}
|
||||
|
||||
_set_grow :: proc(m: ^Set) {
|
||||
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
|
||||
set_reserve(m, new_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package container
|
||||
|
||||
Small_Array :: struct(N: int, T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
|
||||
small_array_len :: proc(a: $A/Small_Array) -> int {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
small_array_cap :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data);
|
||||
}
|
||||
|
||||
small_array_space :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data) - a.len;
|
||||
}
|
||||
|
||||
small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len];
|
||||
}
|
||||
|
||||
|
||||
small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
|
||||
return a.data[index];
|
||||
}
|
||||
small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
|
||||
return &a.data[index];
|
||||
}
|
||||
|
||||
small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
|
||||
a.data[index] = item;
|
||||
}
|
||||
|
||||
small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, len(a.data));
|
||||
}
|
||||
|
||||
|
||||
small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1;
|
||||
a.data[a.len-1] = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1;
|
||||
data := small_array_slice(a);
|
||||
copy(data[1:], data[:]);
|
||||
data[0] = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := a.data[a.len-1];
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
small_array_pop_font :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := a.data[0];
|
||||
s := small_array_slice(a);
|
||||
copy(s[:], s[1:]);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc);
|
||||
a.len -= count;
|
||||
}
|
||||
|
||||
small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
small_array_resize(a, 0);
|
||||
}
|
||||
|
||||
small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:]);
|
||||
a.len += n;
|
||||
}
|
||||
|
||||
small_array_push :: proc{small_array_push_back, small_array_push_back_elems};
|
||||
small_array_append :: proc{small_array_push_back, small_array_push_back_elems};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: opaque rawptr;
|
||||
@@ -0,0 +1,21 @@
|
||||
// +build linux, darwin
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW;
|
||||
if global_symbols do flags |= os.RTLD_GLOBAL;
|
||||
lib := os.dlopen(path, flags);
|
||||
return Library(lib), lib != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) {
|
||||
os.dlclose(rawptr(library));
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// +build windows
|
||||
package dynlib
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
handle := cast(Library)win32.LoadLibraryW(wide_path);
|
||||
return handle, handle != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library);
|
||||
return bool(ok);
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package base32
|
||||
|
||||
// @note(zh): Encoding utility for Base32
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base32 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [32]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7'
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [?]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
out_length := (len(data) + 4) / 5 * 8;
|
||||
out := make([]byte, out_length);
|
||||
_encode(out, data);
|
||||
return string(out);
|
||||
}
|
||||
|
||||
@private
|
||||
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
out := out;
|
||||
data := data;
|
||||
|
||||
for len(data) > 0 {
|
||||
carry: byte;
|
||||
switch len(data) {
|
||||
case:
|
||||
out[7] = ENC_TABLE[data[4] & 0x1f];
|
||||
carry = data[4] >> 5;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f];
|
||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f];
|
||||
carry = data[3] >> 7;
|
||||
fallthrough;
|
||||
case 3:
|
||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f];
|
||||
carry = (data[2] >> 4) & 0x1f;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f];
|
||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f];
|
||||
carry = (data[1] >> 6) & 0x1f;
|
||||
fallthrough;
|
||||
case 1:
|
||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f];
|
||||
out[0] = ENC_TABLE[data[0] >> 3];
|
||||
}
|
||||
|
||||
if len(data) < 5 {
|
||||
out[7] = byte(PADDING);
|
||||
if len(data) < 4 {
|
||||
out[6] = byte(PADDING);
|
||||
out[5] = byte(PADDING);
|
||||
if len(data) < 3 {
|
||||
out[4] = byte(PADDING);
|
||||
if len(data) < 2 {
|
||||
out[3] = byte(PADDING);
|
||||
out[2] = byte(PADDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
data = data[5:];
|
||||
out = out[8:];
|
||||
}
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 do return []byte{};
|
||||
|
||||
outi := 0;
|
||||
olen := len(data);
|
||||
data := data;
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator);
|
||||
end := false;
|
||||
for len(data) > 0 && !end {
|
||||
dbuf : [8]byte;
|
||||
dlen := 8;
|
||||
|
||||
for j := 0; j < 8; {
|
||||
if len(data) == 0 {
|
||||
dlen, end = j, true;
|
||||
break;
|
||||
}
|
||||
input := data[0];
|
||||
data = data[1:];
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 do assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
dlen, end = j, true;
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
|
||||
break;
|
||||
}
|
||||
dbuf[j] = DEC_TABLE[input];
|
||||
assert(dbuf[j] != 0xff, "Corrupted input");
|
||||
j += 1;
|
||||
}
|
||||
|
||||
switch dlen {
|
||||
case 8:
|
||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7];
|
||||
fallthrough;
|
||||
case 7:
|
||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3;
|
||||
fallthrough;
|
||||
case 5:
|
||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2;
|
||||
}
|
||||
outi += 5;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package base64
|
||||
|
||||
// @note(zh): Encoding utility for Base64
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base64 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [64]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [128]int {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return "";
|
||||
|
||||
out_length := ((4 * length / 3) + 3) &~ 3;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, block: int;
|
||||
|
||||
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
|
||||
c0, c1, c2 = int(data[i]), -1, -1;
|
||||
|
||||
if i + 1 < length do c1 = int(data[i + 1]);
|
||||
if i + 2 < length do c2 = int(data[i + 2]);
|
||||
|
||||
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
|
||||
|
||||
out[d] = ENC_TBL[block >> 18 & 63];
|
||||
out[d + 1] = ENC_TBL[block >> 12 & 63];
|
||||
out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63];
|
||||
out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63];
|
||||
}
|
||||
return string(out);
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return []byte{};
|
||||
|
||||
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
|
||||
out_length := ((length * 6) >> 3) - pad_count;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, c3: int;
|
||||
b0, b1, b2: int;
|
||||
|
||||
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
|
||||
c0 = DEC_TBL[data[i]];
|
||||
c1 = DEC_TBL[data[i + 1]];
|
||||
c2 = DEC_TBL[data[i + 2]];
|
||||
c3 = DEC_TBL[data[i + 3]];
|
||||
|
||||
b0 = (c0 << 2) | (c1 >> 4);
|
||||
b1 = (c1 << 4) | (c2 >> 2);
|
||||
b2 = (c2 << 6) | c3;
|
||||
|
||||
out[j] = byte(b0);
|
||||
out[j + 1] = byte(b1);
|
||||
out[j + 2] = byte(b2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,842 @@
|
||||
package cel;
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strings"
|
||||
|
||||
Array :: []Value;
|
||||
Dict :: map[string]Value;
|
||||
Nil_Value :: struct{};
|
||||
|
||||
Value :: union {
|
||||
Nil_Value,
|
||||
bool, i64, f64, string,
|
||||
Array, Dict,
|
||||
}
|
||||
|
||||
Parser :: struct {
|
||||
tokens: [dynamic]Token,
|
||||
prev_token: Token,
|
||||
curr_token: Token,
|
||||
curr_token_index: int,
|
||||
|
||||
allocated_strings: [dynamic]string,
|
||||
|
||||
error_count: int,
|
||||
|
||||
root: Dict,
|
||||
dict_stack: [dynamic]^Dict, // NOTE: Pointers may be stored on the stack
|
||||
}
|
||||
|
||||
|
||||
print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_indent :: proc(indent: int) {
|
||||
for _ in 0..<indent do fmt.print("\t");
|
||||
}
|
||||
|
||||
switch v in value {
|
||||
case bool: fmt.print(v);
|
||||
case i64: fmt.print(v);
|
||||
case f64: fmt.print(v);
|
||||
case string: fmt.print(v);
|
||||
case Array:
|
||||
fmt.print("[");
|
||||
if pretty do fmt.println();
|
||||
for e, i in v {
|
||||
if pretty {
|
||||
print_indent(indent+1);
|
||||
print_value(e, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
print_value(e);
|
||||
}
|
||||
}
|
||||
if pretty do print_indent(indent);
|
||||
fmt.print("]");
|
||||
case Dict:
|
||||
fmt.print("{");
|
||||
if pretty do fmt.println();
|
||||
|
||||
i := 0;
|
||||
for name, val in v {
|
||||
if pretty {
|
||||
print_indent(indent+1);
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if pretty do print_indent(indent);
|
||||
fmt.print("}");
|
||||
case:
|
||||
fmt.print("nil");
|
||||
case Nil_Value:
|
||||
fmt.print("nil");
|
||||
}
|
||||
}
|
||||
print :: proc(p: ^Parser, pretty := false) {
|
||||
for name, val in p.root {
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty);
|
||||
fmt.println(";");
|
||||
}
|
||||
}
|
||||
|
||||
create_from_string :: proc(src: string) -> (^Parser, bool) {
|
||||
return init(transmute([]byte)src);
|
||||
}
|
||||
|
||||
|
||||
init :: proc(src: []byte) -> (^Parser, bool) {
|
||||
t: Tokenizer;
|
||||
tokenizer_init(&t, src);
|
||||
return create_from_tokenizer(&t);
|
||||
}
|
||||
|
||||
|
||||
create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
p := new(Parser);
|
||||
for {
|
||||
tok := scan(t);
|
||||
if tok.kind == .Illegal {
|
||||
return p, false;
|
||||
}
|
||||
append(&p.tokens, tok);
|
||||
if tok.kind == .EOF {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if t.error_count > 0 {
|
||||
return p, false;
|
||||
}
|
||||
|
||||
if len(p.tokens) == 0 {
|
||||
tok := Token{kind = .EOF};
|
||||
tok.line, tok.column = 1, 1;
|
||||
append(&p.tokens, tok);
|
||||
return p, true;
|
||||
}
|
||||
|
||||
p.curr_token_index = 0;
|
||||
p.prev_token = p.tokens[p.curr_token_index];
|
||||
p.curr_token = p.tokens[p.curr_token_index];
|
||||
|
||||
p.root = Dict{};
|
||||
p.dict_stack = make([dynamic]^Dict, 0, 4);
|
||||
append(&p.dict_stack, &p.root);
|
||||
|
||||
for p.curr_token.kind != .EOF &&
|
||||
p.curr_token.kind != .Illegal &&
|
||||
p.curr_token_index < len(p.tokens) {
|
||||
if !parse_assignment(p) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p, true;
|
||||
}
|
||||
|
||||
destroy :: proc(p: ^Parser) {
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
delete(v);
|
||||
|
||||
case Dict:
|
||||
for _, dv in v do destroy_value(dv);
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
delete(p.tokens);
|
||||
for s in p.allocated_strings do delete(s);
|
||||
delete(p.allocated_strings);
|
||||
delete(p.dict_stack);
|
||||
|
||||
destroy_value(p.root);
|
||||
free(p);
|
||||
}
|
||||
|
||||
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
|
||||
p.error_count += 1;
|
||||
}
|
||||
|
||||
next_token :: proc(p: ^Parser) -> Token {
|
||||
p.prev_token = p.curr_token;
|
||||
prev := p.prev_token;
|
||||
|
||||
if p.curr_token_index+1 < len(p.tokens) {
|
||||
p.curr_token_index += 1;
|
||||
p.curr_token = p.tokens[p.curr_token_index];
|
||||
return prev;
|
||||
}
|
||||
p.curr_token_index = len(p.tokens);
|
||||
p.curr_token = p.tokens[p.curr_token_index-1];
|
||||
error(p, prev.pos, "Token is EOF");
|
||||
return prev;
|
||||
}
|
||||
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
w: int;
|
||||
|
||||
if str[0] == quote && quote == '"' {
|
||||
return;
|
||||
} else if str[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(str);
|
||||
return r, true, str[w:], true;
|
||||
} else if str[0] != '\\' {
|
||||
return rune(str[0]), false, str[1:], true;
|
||||
}
|
||||
|
||||
if len(str) <= 1 {
|
||||
return;
|
||||
}
|
||||
s := str;
|
||||
c := s[1];
|
||||
s = s[2:];
|
||||
|
||||
switch c {
|
||||
case:
|
||||
return;
|
||||
|
||||
case 'a': r = '\a';
|
||||
case 'b': r = '\b';
|
||||
case 'f': r = '\f';
|
||||
case 'n': r = '\n';
|
||||
case 'r': r = '\r';
|
||||
case 't': r = '\t';
|
||||
case 'v': r = '\v';
|
||||
case '\\': r = '\\';
|
||||
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..'7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
}
|
||||
for i in 0..<len(s) {
|
||||
d := int(s[i]-'0');
|
||||
if d < 0 || d > 7 {
|
||||
return;
|
||||
}
|
||||
v = (v<<3) | d;
|
||||
}
|
||||
s = s[2:];
|
||||
if v > 0xff {
|
||||
return;
|
||||
}
|
||||
r = rune(v);
|
||||
|
||||
case 'x', 'u', 'U':
|
||||
count: int;
|
||||
switch c {
|
||||
case 'x': count = 2;
|
||||
case 'u': count = 4;
|
||||
case 'U': count = 8;
|
||||
}
|
||||
|
||||
if len(s) < count {
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..<count {
|
||||
d := hex_to_int(s[i]);
|
||||
if d < 0 {
|
||||
return;
|
||||
}
|
||||
r = (r<<4) | rune(d);
|
||||
}
|
||||
s = s[count:];
|
||||
if c == 'x' {
|
||||
break;
|
||||
}
|
||||
if r > utf8.MAX_RUNE {
|
||||
return;
|
||||
}
|
||||
multiple_bytes = true;
|
||||
}
|
||||
|
||||
success = true;
|
||||
tail_string = s;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
|
||||
if t.kind != .String {
|
||||
return t.lit, true;
|
||||
}
|
||||
s := t.lit;
|
||||
quote := '"';
|
||||
|
||||
if s == `""` {
|
||||
return "", true;
|
||||
}
|
||||
|
||||
if strings.contains_rune(s, '\n') >= 0 {
|
||||
return s, false;
|
||||
}
|
||||
|
||||
if strings.contains_rune(s, '\\') < 0 && strings.contains_rune(s, quote) < 0 {
|
||||
if quote == '"' {
|
||||
return s, true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buf_len := 3*len(s) / 2;
|
||||
buf := make([]byte, buf_len);
|
||||
offset := 0;
|
||||
for len(s) > 0 {
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
|
||||
if !ok {
|
||||
delete(buf);
|
||||
return s, false;
|
||||
}
|
||||
s = tail_string;
|
||||
if r < 0x80 || !multiple_bytes {
|
||||
buf[offset] = byte(r);
|
||||
offset += 1;
|
||||
} else {
|
||||
b, w := utf8.encode_rune(r);
|
||||
copy(buf[offset:], b[:w]);
|
||||
offset += w;
|
||||
}
|
||||
}
|
||||
|
||||
new_string := string(buf[:offset]);
|
||||
|
||||
append(&p.allocated_strings, new_string);
|
||||
|
||||
return new_string, true;
|
||||
}
|
||||
|
||||
|
||||
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
next_token(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
|
||||
prev := p.curr_token;
|
||||
if prev.kind != kind {
|
||||
got := prev.lit;
|
||||
if got == "\n" do got = ";";
|
||||
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
|
||||
}
|
||||
next_token(p);
|
||||
return prev;
|
||||
}
|
||||
|
||||
expect_operator :: proc(p: ^Parser) -> Token {
|
||||
prev := p.curr_token;
|
||||
if !is_operator(prev.kind) {
|
||||
error(p, prev.pos, "Expected an operator, got %s", prev.lit);
|
||||
}
|
||||
|
||||
|
||||
next_token(p);
|
||||
return prev;
|
||||
}
|
||||
|
||||
fix_advance :: proc(p: ^Parser) {
|
||||
for {
|
||||
#partial switch t := p.curr_token; t.kind {
|
||||
case .EOF, .Semicolon:
|
||||
return;
|
||||
}
|
||||
next_token(p);
|
||||
}
|
||||
}
|
||||
|
||||
copy_value :: proc(value: Value) -> Value {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
a := make(Array, len(v));
|
||||
for elem, idx in v {
|
||||
a[idx] = copy_value(elem);
|
||||
}
|
||||
return a;
|
||||
case Dict:
|
||||
d := make(Dict, cap(v));
|
||||
for key, val in v {
|
||||
d[key] = copy_value(val);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
|
||||
for i := len(p.dict_stack)-1; i >= 0; i -= 1 {
|
||||
d := p.dict_stack[i];
|
||||
if val, ok := d[name]; ok {
|
||||
return copy_value(val), true;
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
tok := p.curr_token;
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident:
|
||||
next_token(p);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
return v, tok.pos;
|
||||
|
||||
case .True:
|
||||
next_token(p);
|
||||
return true, tok.pos;
|
||||
case .False:
|
||||
next_token(p);
|
||||
return false, tok.pos;
|
||||
|
||||
case .Nil:
|
||||
next_token(p);
|
||||
return Nil_Value{}, tok.pos;
|
||||
|
||||
case .Integer:
|
||||
next_token(p);
|
||||
i, _ := strconv.parse_i64(tok.lit);
|
||||
return i, tok.pos;
|
||||
|
||||
case .Float:
|
||||
next_token(p);
|
||||
f, _ := strconv.parse_f64(tok.lit);
|
||||
return f, tok.pos;
|
||||
|
||||
case .String:
|
||||
next_token(p);
|
||||
str, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
return string(str), tok.pos;
|
||||
|
||||
case .Open_Paren:
|
||||
expect_token(p, .Open_Paren);
|
||||
expr, _ := parse_expr(p);
|
||||
expect_token(p, .Close_Paren);
|
||||
return expr, tok.pos;
|
||||
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
elems := make([dynamic]Value, 0, 4);
|
||||
for p.curr_token.kind != .Close_Bracket &&
|
||||
p.curr_token.kind != .EOF {
|
||||
elem, _ := parse_expr(p);
|
||||
append(&elems, elem);
|
||||
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
expect_token(p, .Close_Bracket);
|
||||
return Array(elems[:]), tok.pos;
|
||||
|
||||
case .Open_Brace:
|
||||
expect_token(p, .Open_Brace);
|
||||
|
||||
dict := Dict{};
|
||||
append(&p.dict_stack, &dict);
|
||||
defer pop(&p.dict_stack);
|
||||
|
||||
for p.curr_token.kind != .Close_Brace &&
|
||||
p.curr_token.kind != .EOF {
|
||||
name_tok := p.curr_token;
|
||||
if !allow_token(p, .Ident) && !allow_token(p, .String) {
|
||||
name_tok = expect_token(p, .Ident);
|
||||
}
|
||||
|
||||
name, ok := unquote_string(p, name_tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
expect_token(p, .Assign);
|
||||
elem, _ := parse_expr(p);
|
||||
|
||||
if _, ok2 := dict[name]; ok2 {
|
||||
error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
|
||||
} else {
|
||||
dict[name] = elem;
|
||||
}
|
||||
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect_token(p, .Close_Brace);
|
||||
return dict, tok.pos;
|
||||
|
||||
}
|
||||
return nil, tok.pos;
|
||||
}
|
||||
|
||||
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
loop := true;
|
||||
for operand := operand; loop; {
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Period:
|
||||
next_token(p);
|
||||
tok := next_token(p);
|
||||
|
||||
#partial switch tok.kind {
|
||||
case .Ident:
|
||||
d, ok := operand.(Dict);
|
||||
if !ok || d == nil {
|
||||
error(p, tok.pos, "Expected a dictionary");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
name, usok := unquote_string(p, tok);
|
||||
if !usok do error(p, tok.pos, "Unable to unquote string");
|
||||
val, found := d[name];
|
||||
if !found {
|
||||
error(p, tok.pos, "Field %s not found in dictionary", name);
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
operand = val;
|
||||
case:
|
||||
error(p, tok.pos, "Expected a selector, got %s", tok.kind);
|
||||
operand = nil;
|
||||
}
|
||||
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
index, index_pos := parse_expr(p);
|
||||
expect_token(p, .Close_Bracket);
|
||||
|
||||
|
||||
#partial switch a in operand {
|
||||
case Array:
|
||||
i, ok := index.(i64);
|
||||
if !ok {
|
||||
error(p, index_pos, "Index must be an integer for an array");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
if 0 <= i && i < i64(len(a)) {
|
||||
operand = a[i];
|
||||
} else {
|
||||
error(p, index_pos, "Index %d out of bounds range 0..%d", i, len(a));
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Dict:
|
||||
key, ok := index.(string);
|
||||
if !ok {
|
||||
error(p, index_pos, "Index must be a string for a dictionary");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
val, found := a[key];
|
||||
if found {
|
||||
operand = val;
|
||||
} else {
|
||||
error(p, index_pos, "`%s` was not found in the dictionary", key);
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
case:
|
||||
error(p, index_pos, "Indexing is only allowed on an array or dictionary");
|
||||
}
|
||||
|
||||
case:
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
|
||||
return operand, pos;
|
||||
}
|
||||
|
||||
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
op := p.curr_token;
|
||||
#partial switch p.curr_token.kind {
|
||||
case .At:
|
||||
next_token(p);
|
||||
tok := expect_token(p, .String);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
return parse_atom_expr(p, v, tok.pos);
|
||||
|
||||
case .Add, .Sub:
|
||||
next_token(p);
|
||||
// TODO(bill): Calcuate values as you go!
|
||||
expr, pos := parse_unary_expr(p);
|
||||
|
||||
#partial switch e in expr {
|
||||
case i64: if op.kind == .Sub do return -e, pos;
|
||||
case f64: if op.kind == .Sub do return -e, pos;
|
||||
case:
|
||||
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
|
||||
return nil, op.pos;
|
||||
}
|
||||
|
||||
return expr, op.pos;
|
||||
|
||||
case .Not:
|
||||
next_token(p);
|
||||
expr, _ := parse_unary_expr(p);
|
||||
if v, ok := expr.(bool); ok {
|
||||
return !v, op.pos;
|
||||
}
|
||||
error(p, op.pos, "Unary operator %s can only be used on booleans", op.lit);
|
||||
return nil, op.pos;
|
||||
}
|
||||
|
||||
return parse_atom_expr(p, parse_operand(p));
|
||||
}
|
||||
|
||||
|
||||
value_order :: proc(v: Value) -> int {
|
||||
#partial switch _ in v {
|
||||
case bool, string:
|
||||
return 1;
|
||||
case i64:
|
||||
return 2;
|
||||
case f64:
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
match_values :: proc(left, right: ^Value) -> bool {
|
||||
if value_order(right^) < value_order(left^) {
|
||||
return match_values(right, left);
|
||||
}
|
||||
|
||||
#partial switch x in left^ {
|
||||
case:
|
||||
right^ = left^;
|
||||
case bool, string:
|
||||
return true;
|
||||
case i64:
|
||||
#partial switch y in right^ {
|
||||
case i64:
|
||||
return true;
|
||||
case f64:
|
||||
left^ = f64(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
case f64:
|
||||
#partial switch y in right {
|
||||
case f64:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
|
||||
// TODO(bill): Calculate value as you go!
|
||||
x, y := a, b;
|
||||
match_values(&x, &y);
|
||||
|
||||
|
||||
#partial switch a in x {
|
||||
case: return x, true;
|
||||
|
||||
case bool:
|
||||
b, ok := y.(bool);
|
||||
if !ok do return nil, false;
|
||||
#partial switch op {
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .And: return a && b, true;
|
||||
case .Or: return a || b, true;
|
||||
}
|
||||
|
||||
case i64:
|
||||
b, ok := y.(i64);
|
||||
if !ok do return nil, false;
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Rem: return a % b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case f64:
|
||||
b, ok := y.(f64);
|
||||
if !ok do return nil, false;
|
||||
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case string:
|
||||
b, ok := y.(string);
|
||||
if !ok do return nil, false;
|
||||
|
||||
#partial switch op {
|
||||
case .Add:
|
||||
n := len(a) + len(b);
|
||||
data := make([]byte, n);
|
||||
copy(data[:], a);
|
||||
copy(data[len(a):], b);
|
||||
s := string(data);
|
||||
append(&p.allocated_strings, s);
|
||||
return s, true;
|
||||
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
|
||||
expr, pos := parse_unary_expr(p);
|
||||
for prec := precedence(p.curr_token.kind); prec >= prec_in; prec -= 1 {
|
||||
for {
|
||||
op := p.curr_token;
|
||||
op_prec := precedence(op.kind);
|
||||
if op_prec != prec {
|
||||
break;
|
||||
}
|
||||
expect_operator(p);
|
||||
|
||||
if op.kind == .Question {
|
||||
cond := expr;
|
||||
x, _ := parse_expr(p);
|
||||
expect_token(p, .Colon);
|
||||
y, _ := parse_expr(p);
|
||||
|
||||
if t, ok := cond.(bool); ok {
|
||||
expr = t ? x : y;
|
||||
} else {
|
||||
error(p, pos, "Condition must be a boolean");
|
||||
}
|
||||
|
||||
} else {
|
||||
right, right_pos := parse_binary_expr(p, prec+1);
|
||||
if right == nil {
|
||||
error(p, right_pos, "Expected expression on the right-hand side of the binary operator %s", op.lit);
|
||||
}
|
||||
left := expr;
|
||||
ok: bool;
|
||||
expr, ok = calculate_binary_value(p, op.kind, left, right);
|
||||
if !ok {
|
||||
error(p, pos, "Invalid binary operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return expr, pos;
|
||||
}
|
||||
|
||||
parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
return parse_binary_expr(p, 1);
|
||||
}
|
||||
|
||||
expect_semicolon :: proc(p: ^Parser) {
|
||||
kind := p.curr_token.kind;
|
||||
|
||||
#partial switch kind {
|
||||
case .Comma:
|
||||
error(p, p.curr_token.pos, "Expected ';', got ','");
|
||||
next_token(p);
|
||||
case .Semicolon:
|
||||
next_token(p);
|
||||
case .EOF:
|
||||
// okay
|
||||
case:
|
||||
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
|
||||
fix_advance(p);
|
||||
}
|
||||
}
|
||||
|
||||
parse_assignment :: proc(p: ^Parser) -> bool {
|
||||
top_dict :: proc(p: ^Parser) -> ^Dict {
|
||||
assert(len(p.dict_stack) > 0);
|
||||
return p.dict_stack[len(p.dict_stack)-1];
|
||||
}
|
||||
|
||||
if p.curr_token.kind == .Semicolon {
|
||||
next_token(p);
|
||||
return true;
|
||||
}
|
||||
if p.curr_token.kind == .EOF {
|
||||
return false;
|
||||
}
|
||||
|
||||
tok := p.curr_token;
|
||||
if allow_token(p, .Ident) || allow_token(p, .String) {
|
||||
expect_token(p, .Assign);
|
||||
name, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
expr, _ := parse_expr(p);
|
||||
d := top_dict(p);
|
||||
if _, ok2 := d[name]; ok2 {
|
||||
error(p, tok.pos, "Previous declaration of %s", name);
|
||||
} else {
|
||||
d[name] = expr;
|
||||
}
|
||||
expect_semicolon(p);
|
||||
return true;
|
||||
}
|
||||
error(p, tok.pos, "Expected an assignment, got %s", kind_to_string[tok.kind]);
|
||||
fix_advance(p);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
package cel
|
||||
|
||||
sample := `
|
||||
x = 123;
|
||||
y = 321.456;
|
||||
z = x * (y - 1) / 2;
|
||||
w = "foo" + "bar";
|
||||
|
||||
# This is a comment
|
||||
|
||||
asd = "Semicolons are optional"
|
||||
|
||||
a = {id = {b = 123}} # Dict
|
||||
b = a.id.b
|
||||
|
||||
f = [1, 4, 9] # Array
|
||||
g = f[2]
|
||||
|
||||
h = x < y and w == "foobar"
|
||||
i = h ? 123 : "google"
|
||||
|
||||
j = nil
|
||||
|
||||
"127.0.0.1" = "value" # Keys can be strings
|
||||
|
||||
"foo" = {
|
||||
"bar" = {
|
||||
"baz" = 123, # optional commas if newline is present
|
||||
"zab" = 456,
|
||||
"abz" = 789,
|
||||
},
|
||||
};
|
||||
|
||||
bar = @"foo"["bar"].baz
|
||||
`;
|
||||
|
||||
|
||||
main :: proc() {
|
||||
p, ok := create_from_string(sample);
|
||||
if !ok {
|
||||
return;
|
||||
}
|
||||
defer destroy(p);
|
||||
|
||||
if p.error_count == 0 {
|
||||
print(p);
|
||||
}
|
||||
}
|
||||
*/
|
||||
package cel
|
||||
@@ -0,0 +1,523 @@
|
||||
package cel
|
||||
|
||||
import "core:fmt"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
using Kind :: enum {
|
||||
Illegal,
|
||||
EOF,
|
||||
Comment,
|
||||
|
||||
_literal_start,
|
||||
Ident,
|
||||
Integer,
|
||||
Float,
|
||||
Char,
|
||||
String,
|
||||
_literal_end,
|
||||
|
||||
_keyword_start,
|
||||
True, // true
|
||||
False, // false
|
||||
Nil, // nil
|
||||
_keyword_end,
|
||||
|
||||
|
||||
_operator_start,
|
||||
Question, // ?
|
||||
|
||||
And, // and
|
||||
Or, // or
|
||||
|
||||
Add, // +
|
||||
Sub, // -
|
||||
Mul, // *
|
||||
Quo, // /
|
||||
Rem, // %
|
||||
|
||||
Not, // !
|
||||
|
||||
Eq, // ==
|
||||
NotEq, // !=
|
||||
Lt, // <
|
||||
Gt, // >
|
||||
LtEq, // <=
|
||||
GtEq, // >=
|
||||
|
||||
At, // @
|
||||
_operator_end,
|
||||
|
||||
_punc_start,
|
||||
Assign, // =
|
||||
|
||||
Open_Paren, // (
|
||||
Close_Paren, // )
|
||||
Open_Bracket, // [
|
||||
Close_Bracket, // ]
|
||||
Open_Brace, // {
|
||||
Close_Brace, // }
|
||||
|
||||
Colon, // :
|
||||
Semicolon, // ;
|
||||
Comma, // ,
|
||||
Period, // .
|
||||
_punc_end,
|
||||
}
|
||||
|
||||
|
||||
Pos :: struct {
|
||||
file: string,
|
||||
line: int,
|
||||
column: int,
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind: Kind,
|
||||
using pos: Pos,
|
||||
lit: string,
|
||||
}
|
||||
|
||||
Tokenizer :: struct {
|
||||
src: []byte,
|
||||
|
||||
file: string, // May not be used
|
||||
|
||||
curr_rune: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
|
||||
insert_semi: bool,
|
||||
|
||||
error_count: int,
|
||||
}
|
||||
|
||||
|
||||
keywords := map[string]Kind{
|
||||
"true" = True,
|
||||
"false" = False,
|
||||
"nil" = Nil,
|
||||
"and" = And,
|
||||
"or" = Or,
|
||||
};
|
||||
|
||||
kind_to_string := [len(Kind)]string{
|
||||
"illegal",
|
||||
"EOF",
|
||||
"comment",
|
||||
|
||||
"",
|
||||
"identifier",
|
||||
"integer",
|
||||
"float",
|
||||
"character",
|
||||
"string",
|
||||
"",
|
||||
|
||||
"",
|
||||
"true", "false", "nil",
|
||||
"",
|
||||
|
||||
"",
|
||||
"?", "and", "or",
|
||||
"+", "-", "*", "/", "%",
|
||||
"!",
|
||||
"==", "!=", "<", ">", "<=", ">=",
|
||||
"@",
|
||||
"",
|
||||
|
||||
"",
|
||||
"=",
|
||||
"(", ")",
|
||||
"[", "]",
|
||||
"{", "}",
|
||||
":", ";", ",", ".",
|
||||
"",
|
||||
};
|
||||
|
||||
precedence :: proc(op: Kind) -> int {
|
||||
#partial switch op {
|
||||
case Question:
|
||||
return 1;
|
||||
case Or:
|
||||
return 2;
|
||||
case And:
|
||||
return 3;
|
||||
case Eq, NotEq, Lt, Gt, LtEq, GtEq:
|
||||
return 4;
|
||||
case Add, Sub:
|
||||
return 5;
|
||||
case Mul, Quo, Rem:
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
token_lookup :: proc(ident: string) -> Kind {
|
||||
if tok, is_keyword := keywords[ident]; is_keyword {
|
||||
return tok;
|
||||
}
|
||||
return Ident;
|
||||
}
|
||||
|
||||
is_literal :: proc(tok: Kind) -> bool do return _literal_start < tok && tok < _literal_end;
|
||||
is_operator :: proc(tok: Kind) -> bool do return _operator_start < tok && tok < _operator_end;
|
||||
is_keyword :: proc(tok: Kind) -> bool do return _keyword_start < tok && tok < _keyword_end;
|
||||
|
||||
|
||||
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
|
||||
t.src = src;
|
||||
t.file = file;
|
||||
t.curr_rune = ' ';
|
||||
t.offset = 0;
|
||||
t.read_offset = 0;
|
||||
t.line_offset = 0;
|
||||
t.line_count = 1;
|
||||
|
||||
advance_to_next_rune(t);
|
||||
if t.curr_rune == utf8.RUNE_BOM {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
t.error_count += 1;
|
||||
}
|
||||
|
||||
advance_to_next_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset;
|
||||
if t.curr_rune == '\n' {
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
r, w := rune(t.src[t.read_offset]), 1;
|
||||
switch {
|
||||
case r == 0:
|
||||
token_error(t, "Illegal character NUL");
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(t.src[t.read_offset:]);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
token_error(t, "Illegal utf-8 encoding");
|
||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
||||
token_error(t, "Illegal byte order mark");
|
||||
}
|
||||
}
|
||||
|
||||
t.read_offset += w;
|
||||
t.curr_rune = r;
|
||||
} else {
|
||||
t.offset = len(t.src);
|
||||
if t.curr_rune == '\n' {
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
t.curr_rune = utf8.RUNE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_pos :: proc(t: ^Tokenizer) -> Pos {
|
||||
return Pos {
|
||||
file = t.file,
|
||||
line = t.line_count,
|
||||
column = t.offset - t.line_offset + 1,
|
||||
};
|
||||
}
|
||||
|
||||
is_letter :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 'a'..'z', 'A'..'Z', '_':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
loop: for {
|
||||
switch t.curr_rune {
|
||||
case '\n':
|
||||
if t.insert_semi {
|
||||
break loop;
|
||||
}
|
||||
fallthrough;
|
||||
case ' ', '\t', '\r', '\v', '\f':
|
||||
advance_to_next_rune(t);
|
||||
|
||||
case:
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset;
|
||||
for is_letter(t.curr_rune) || is_digit(t.curr_rune) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
digit_value :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9': return int(r - '0');
|
||||
case 'a'..'f': return int(r - 'a' + 10);
|
||||
case 'A'..'F': return int(r - 'A' + 10);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_value(t.curr_rune) < base || t.curr_rune == '_' {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == 'e' || t.curr_rune == 'E' {
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
if t.curr_rune == '-' || t.curr_rune == '+' {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
if digit_value(t.curr_rune) < 10 {
|
||||
scan_mantissa(t, 10);
|
||||
} else {
|
||||
token_error(t, "Illegal floating point exponent");
|
||||
}
|
||||
}
|
||||
text = string(t.src[offset : t.offset]);
|
||||
return;
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == '.' {
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
return scan_exponent(t, kind, offset);
|
||||
}
|
||||
|
||||
offset := t.offset;
|
||||
tok := Integer;
|
||||
|
||||
if seen_decimal_point {
|
||||
offset -= 1;
|
||||
tok = Float;
|
||||
scan_mantissa(t, 10);
|
||||
return scan_exponent(t, tok, offset);
|
||||
}
|
||||
|
||||
if t.curr_rune == '0' {
|
||||
offset = t.offset;
|
||||
advance_to_next_rune(t);
|
||||
switch t.curr_rune {
|
||||
case 'b', 'B':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 2);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal binary number");
|
||||
}
|
||||
case 'o', 'O':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 8);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal octal number");
|
||||
}
|
||||
case 'x', 'X':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 16);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal hexadecimal number");
|
||||
}
|
||||
case:
|
||||
scan_mantissa(t, 10);
|
||||
switch t.curr_rune {
|
||||
case '.', 'e', 'E':
|
||||
return scan_fraction(t, tok, offset);
|
||||
}
|
||||
}
|
||||
|
||||
return tok, string(t.src[offset:t.offset]);
|
||||
}
|
||||
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
return scan_fraction(t, tok, offset);
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
skip_whitespace(t);
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
tok: Kind;
|
||||
pos := get_pos(t);
|
||||
lit: string;
|
||||
|
||||
insert_semi := false;
|
||||
|
||||
|
||||
switch r := t.curr_rune; {
|
||||
case is_letter(r):
|
||||
insert_semi = true;
|
||||
lit = scan_identifier(t);
|
||||
tok = Ident;
|
||||
if len(lit) > 1 {
|
||||
tok = token_lookup(lit);
|
||||
}
|
||||
|
||||
case '0' <= r && r <= '9':
|
||||
insert_semi = true;
|
||||
tok, lit = scan_number(t, false);
|
||||
|
||||
case:
|
||||
advance_to_next_rune(t);
|
||||
switch r {
|
||||
case -1:
|
||||
if t.insert_semi {
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
}
|
||||
return Token{EOF, pos, "\n"};
|
||||
|
||||
case '\n':
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
|
||||
case '"':
|
||||
insert_semi = true;
|
||||
quote := r;
|
||||
tok = String;
|
||||
for {
|
||||
this_r := t.curr_rune;
|
||||
if this_r == '\n' || r < 0 {
|
||||
token_error(t, "String literal not terminated");
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
if this_r == quote {
|
||||
break;
|
||||
}
|
||||
// TODO(bill); Handle properly
|
||||
if this_r == '\\' && t.curr_rune == quote {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
lit = string(t.src[offset+1:t.offset-1]);
|
||||
|
||||
|
||||
case '#':
|
||||
for t.curr_rune != '\n' && t.curr_rune >= 0 {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
if t.insert_semi {
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
}
|
||||
// Recursive!
|
||||
return scan(t);
|
||||
|
||||
case '?': tok = Question;
|
||||
case ':': tok = Colon;
|
||||
case '@': tok = At;
|
||||
|
||||
case ';':
|
||||
tok = Semicolon;
|
||||
lit = ";";
|
||||
case ',': tok = Comma;
|
||||
|
||||
case '(':
|
||||
tok = Open_Paren;
|
||||
case ')':
|
||||
insert_semi = true;
|
||||
tok = Close_Paren;
|
||||
|
||||
case '[':
|
||||
tok = Open_Bracket;
|
||||
case ']':
|
||||
insert_semi = true;
|
||||
tok = Close_Bracket;
|
||||
|
||||
case '{':
|
||||
tok = Open_Brace;
|
||||
case '}':
|
||||
insert_semi = true;
|
||||
tok = Close_Brace;
|
||||
|
||||
case '+': tok = Add;
|
||||
case '-': tok = Sub;
|
||||
case '*': tok = Mul;
|
||||
case '/': tok = Quo;
|
||||
case '%': tok = Rem;
|
||||
|
||||
case '!':
|
||||
tok = Not;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = NotEq;
|
||||
}
|
||||
|
||||
case '=':
|
||||
tok = Assign;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = Eq;
|
||||
}
|
||||
|
||||
case '<':
|
||||
tok = Lt;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = LtEq;
|
||||
}
|
||||
|
||||
case '>':
|
||||
tok = Gt;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = GtEq;
|
||||
}
|
||||
|
||||
case '.':
|
||||
if '0' <= t.curr_rune && t.curr_rune <= '9' {
|
||||
insert_semi = true;
|
||||
tok, lit = scan_number(t, true);
|
||||
} else {
|
||||
tok = Period;
|
||||
}
|
||||
|
||||
case:
|
||||
if r != utf8.RUNE_BOM {
|
||||
token_error(t, "Illegal character '%r'", r);
|
||||
}
|
||||
insert_semi = t.insert_semi;
|
||||
tok = Illegal;
|
||||
}
|
||||
}
|
||||
|
||||
t.insert_semi = insert_semi;
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset:t.offset]);
|
||||
}
|
||||
|
||||
return Token{tok, pos, lit};
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
Marshal_Error :: enum {
|
||||
None,
|
||||
Unsupported_Type,
|
||||
Invalid_Data,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
||||
b := strings.make_builder(allocator);
|
||||
|
||||
err := marshal_arg(&b, v);
|
||||
|
||||
if err != .None {
|
||||
strings.destroy_builder(&b);
|
||||
return nil, err;
|
||||
}
|
||||
if len(b.buf) == 0 {
|
||||
strings.destroy_builder(&b);
|
||||
return nil, err;
|
||||
}
|
||||
return b.buf[:], err;
|
||||
}
|
||||
|
||||
|
||||
marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
using strings;
|
||||
using runtime;
|
||||
if v == nil {
|
||||
write_string(b, "null");
|
||||
return .None;
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(v.id));
|
||||
a := any{v.data, ti.id};
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
unreachable();
|
||||
|
||||
case Type_Info_Integer:
|
||||
buf: [21]byte;
|
||||
u: u64;
|
||||
switch i in a {
|
||||
case i8: u = u64(i);
|
||||
case i16: u = u64(i);
|
||||
case i32: u = u64(i);
|
||||
case i64: u = u64(i);
|
||||
case int: u = u64(i);
|
||||
case u8: u = u64(i);
|
||||
case u16: u = u64(i);
|
||||
case u32: u = u64(i);
|
||||
case u64: u = u64(i);
|
||||
case uint: u = u64(i);
|
||||
case uintptr: u = u64(i);
|
||||
|
||||
case i16le: u = u64(i);
|
||||
case i32le: u = u64(i);
|
||||
case i64le: u = u64(i);
|
||||
case u16le: u = u64(i);
|
||||
case u32le: u = u64(i);
|
||||
case u64le: u = u64(i);
|
||||
|
||||
case i16be: u = u64(i);
|
||||
case i32be: u = u64(i);
|
||||
case i64be: u = u64(i);
|
||||
case u16be: u = u64(i);
|
||||
case u32be: u = u64(i);
|
||||
case u64be: u = u64(i);
|
||||
}
|
||||
|
||||
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil);
|
||||
write_string(b, s);
|
||||
|
||||
|
||||
case Type_Info_Rune:
|
||||
r := a.(rune);
|
||||
write_byte(b, '"');
|
||||
write_escaped_rune(b, r, '"', true);
|
||||
write_byte(b, '"');
|
||||
|
||||
case Type_Info_Float:
|
||||
val: f64;
|
||||
switch f in a {
|
||||
case f32: val = f64(f);
|
||||
case f64: val = f64(f);
|
||||
}
|
||||
|
||||
buf: [386]byte;
|
||||
|
||||
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
|
||||
s := buf[:len(str)+1];
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:];
|
||||
} else {
|
||||
s[0] = '+';
|
||||
}
|
||||
if s[0] == '+' {
|
||||
s = s[1:];
|
||||
}
|
||||
|
||||
write_string(b, string(s));
|
||||
|
||||
case Type_Info_Complex:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_String:
|
||||
switch s in a {
|
||||
case string: write_quoted_string(b, s);
|
||||
case cstring: write_quoted_string(b, string(s));
|
||||
}
|
||||
|
||||
case Type_Info_Boolean:
|
||||
val: bool;
|
||||
switch b in a {
|
||||
case bool: val = bool(b);
|
||||
case b8: val = bool(b);
|
||||
case b16: val = bool(b);
|
||||
case b32: val = bool(b);
|
||||
case b64: val = bool(b);
|
||||
}
|
||||
write_string(b, val ? "true" : "false");
|
||||
|
||||
case Type_Info_Any:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Type_Id:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Pointer:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Procedure:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Tuple:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Array:
|
||||
write_byte(b, '[');
|
||||
for i in 0..<info.count {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_byte(b, '[');
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data;
|
||||
for i in 0..<array.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Slice:
|
||||
write_byte(b, '[');
|
||||
slice := cast(^mem.Raw_Slice)v.data;
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data);
|
||||
|
||||
write_byte(b, '{');
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
entries := &m.entries;
|
||||
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct);
|
||||
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array);
|
||||
entry_type := ed.elem.variant.(Type_Info_Struct);
|
||||
entry_size := ed.elem_size;
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
header := cast(^Map_Entry_Header)data;
|
||||
|
||||
if reflect.is_string(info.key) {
|
||||
marshal_arg(b, header.key.str);
|
||||
} else {
|
||||
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
|
||||
}
|
||||
|
||||
write_string(b, ": ");
|
||||
|
||||
value := data + entry_type.offsets[2];
|
||||
marshal_arg(b, any{rawptr(value), info.value.id});
|
||||
}
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Struct:
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
write_quoted_string(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
id := info.types[i].id;
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i]);
|
||||
marshal_arg(b, any{data, id});
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset;
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
||||
|
||||
tag: i64 = -1;
|
||||
switch i in tag_any {
|
||||
case u8: tag = i64(i);
|
||||
case i8: tag = i64(i);
|
||||
case u16: tag = i64(i);
|
||||
case i16: tag = i64(i);
|
||||
case u32: tag = i64(i);
|
||||
case i32: tag = i64(i);
|
||||
case u64: tag = i64(i);
|
||||
case i64: tag = i64(i);
|
||||
case: panic("Invalid union tag type");
|
||||
}
|
||||
|
||||
if v.data == nil || tag == 0 {
|
||||
write_string(b, "null");
|
||||
} else {
|
||||
id := info.variants[tag-1].id;
|
||||
marshal_arg(b, any{v.data, id});
|
||||
}
|
||||
|
||||
case Type_Info_Enum:
|
||||
return marshal_arg(b, any{v.data, info.base.id});
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
data: u64 = 0;
|
||||
switch ti.size {
|
||||
case 1: data = cast(u64) (^u8)(v.data)^;
|
||||
case 2: data = cast(u64)(^u16)(v.data)^;
|
||||
case 4: data = cast(u64)(^u32)(v.data)^;
|
||||
case 8: data = cast(u64)(^u64)(v.data)^;
|
||||
}
|
||||
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
bits := u64(info.bits[i]);
|
||||
offset := u64(info.offsets[i]);
|
||||
marshal_arg(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
n := 8*u64(size_of(u64));
|
||||
sa := n - bits;
|
||||
u := data>>offset;
|
||||
u <<= sa;
|
||||
u >>= sa;
|
||||
|
||||
write_u64(b, u, 10);
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
return false;
|
||||
}
|
||||
t := runtime.type_info_base(ti);
|
||||
#partial switch info in t.variant {
|
||||
case runtime.Type_Info_Integer:
|
||||
switch info.endianness {
|
||||
case .Platform: return false;
|
||||
case .Little: return ODIN_ENDIAN != "little";
|
||||
case .Big: return ODIN_ENDIAN != "big";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bit_data: u64;
|
||||
bit_size := u64(8*ti.size);
|
||||
|
||||
do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying);
|
||||
|
||||
switch bit_size {
|
||||
case 0: bit_data = 0;
|
||||
case 8:
|
||||
x := (^u8)(v.data)^;
|
||||
bit_data = u64(x);
|
||||
case 16:
|
||||
x := (^u16)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
bit_data = u64(x);
|
||||
case 32:
|
||||
x := (^u32)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
bit_data = u64(x);
|
||||
case 64:
|
||||
x := (^u64)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
bit_data = u64(x);
|
||||
case: panic("unknown bit_size size");
|
||||
}
|
||||
write_u64(b, bit_data);
|
||||
|
||||
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Opaque:
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strconv"
|
||||
|
||||
Parser :: struct {
|
||||
tok: Tokenizer,
|
||||
prev_token: Token,
|
||||
curr_token: Token,
|
||||
spec: Specification,
|
||||
allocator: mem.Allocator,
|
||||
unmarshal_data: any,
|
||||
}
|
||||
|
||||
make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> Parser {
|
||||
p: Parser;
|
||||
p.tok = make_tokenizer(data, spec);
|
||||
p.spec = spec;
|
||||
p.allocator = allocator;
|
||||
assert(p.allocator.procedure != nil);
|
||||
advance_token(&p);
|
||||
return p;
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> (Value, Error) {
|
||||
context.allocator = allocator;
|
||||
p := make_parser(data, spec, allocator);
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
return parse_value(&p);
|
||||
}
|
||||
return parse_object(&p);
|
||||
}
|
||||
|
||||
token_end_pos :: proc(tok: Token) -> Pos {
|
||||
end := tok.pos;
|
||||
end.offset += len(tok.text);
|
||||
return end;
|
||||
}
|
||||
|
||||
advance_token :: proc(p: ^Parser) -> (Token, Error) {
|
||||
err: Error;
|
||||
p.prev_token = p.curr_token;
|
||||
p.curr_token, err = get_token(&p.tok);
|
||||
return p.prev_token, err;
|
||||
}
|
||||
|
||||
|
||||
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
advance_token(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
prev := p.curr_token;
|
||||
advance_token(p);
|
||||
if prev.kind == kind {
|
||||
return .None;
|
||||
}
|
||||
return .Unexpected_Token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
|
||||
token := p.curr_token;
|
||||
#partial switch token.kind {
|
||||
case .Null:
|
||||
value.value = Null{};
|
||||
advance_token(p);
|
||||
return;
|
||||
case .False:
|
||||
value.value = Boolean(false);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .True:
|
||||
value.value = Boolean(true);
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case .Integer:
|
||||
i, _ := strconv.parse_i64(token.text);
|
||||
value.value = Integer(i);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .Float:
|
||||
f, _ := strconv.parse_f64(token.text);
|
||||
value.value = Float(f);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .String:
|
||||
value.value = String(unquote_string(token, p.spec, p.allocator));
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case .Open_Brace:
|
||||
return parse_object(p);
|
||||
|
||||
case .Open_Bracket:
|
||||
return parse_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
#partial switch token.kind {
|
||||
case .Infinity:
|
||||
inf: u64 = 0x7ff0000000000000;
|
||||
if token.text[0] == '-' {
|
||||
inf = 0xfff0000000000000;
|
||||
}
|
||||
value.value = transmute(f64)inf;
|
||||
advance_token(p);
|
||||
return;
|
||||
case .NaN:
|
||||
nan: u64 = 0x7ff7ffffffffffff;
|
||||
if token.text[0] == '-' {
|
||||
nan = 0xfff7ffffffffffff;
|
||||
}
|
||||
value.value = transmute(f64)nan;
|
||||
advance_token(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = .Unexpected_Token;
|
||||
advance_token(p);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
if err = expect_token(p, .Open_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
array: Array;
|
||||
array.allocator = p.allocator;
|
||||
defer if err != .None {
|
||||
for elem in array {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(array);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
elem, elem_err := parse_value(p);
|
||||
if elem_err != .None {
|
||||
err = elem_err;
|
||||
return;
|
||||
}
|
||||
append(&array, elem);
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, .Close_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
value.value = array;
|
||||
return;
|
||||
}
|
||||
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
|
||||
n := len(s);
|
||||
b := make([]byte, n+1, allocator);
|
||||
copy(b, s);
|
||||
b[n] = 0;
|
||||
return string(b[:n]);
|
||||
}
|
||||
|
||||
parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
key = unquote_string(tok, p.spec, p.allocator);
|
||||
return;
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
key = clone_string(tok.text, p.allocator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if tok_err := expect_token(p, .String); tok_err != .None {
|
||||
err = .Expected_String_For_Object_Key;
|
||||
return;
|
||||
}
|
||||
key = unquote_string(tok, p.spec, p.allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
|
||||
if err = expect_token(p, .Open_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
obj: Object;
|
||||
obj.allocator = p.allocator;
|
||||
defer if err != .None {
|
||||
for key, elem in obj {
|
||||
delete(key, p.allocator);
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(obj);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
key: string;
|
||||
key, err = parse_object_key(p);
|
||||
if err != .None {
|
||||
delete(key, p.allocator);
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
if colon_err := expect_token(p, .Colon); colon_err != .None {
|
||||
err = .Expected_Colon_After_Key;
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
elem, elem_err := parse_value(p);
|
||||
if elem_err != .None {
|
||||
err = elem_err;
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
if key in obj {
|
||||
err = .Duplicate_Object_Key;
|
||||
value.pos = p.curr_token.pos;
|
||||
delete(key, p.allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
obj[key] = elem;
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, .Close_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
value.value = obj;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
|
||||
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> string {
|
||||
get_u2_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r: rune;
|
||||
for c in s[2:4] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
get_u4_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r: rune;
|
||||
for c in s[2:6] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
if token.kind != .String {
|
||||
return "";
|
||||
}
|
||||
s := token.text;
|
||||
if len(s) <= 2 {
|
||||
return "";
|
||||
}
|
||||
quote := s[0];
|
||||
if s[0] != s[len(s)-1] {
|
||||
// Invalid string
|
||||
return "";
|
||||
}
|
||||
s = s[1:len(s)-1];
|
||||
|
||||
i := 0;
|
||||
for i < len(s) {
|
||||
c := s[i];
|
||||
if c == '\\' || c == quote || c < ' ' {
|
||||
break;
|
||||
}
|
||||
if c < utf8.RUNE_SELF {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
r, w := utf8.decode_rune_in_string(s);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
break;
|
||||
}
|
||||
i += w;
|
||||
}
|
||||
if i == len(s) {
|
||||
return clone_string(s, allocator);
|
||||
}
|
||||
|
||||
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
|
||||
w := copy(b, s[0:i]);
|
||||
loop: for i < len(s) {
|
||||
c := s[i];
|
||||
switch {
|
||||
case c == '\\':
|
||||
i += 1;
|
||||
if i >= len(s) {
|
||||
break loop;
|
||||
}
|
||||
switch s[i] {
|
||||
case: break loop;
|
||||
case '"', '\'', '\\', '/':
|
||||
b[w] = s[i];
|
||||
i += 1;
|
||||
w += 1;
|
||||
|
||||
case 'b':
|
||||
b[w] = '\b';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'f':
|
||||
b[w] = '\f';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'r':
|
||||
b[w] = '\r';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 't':
|
||||
b[w] = '\t';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'n':
|
||||
b[w] = '\n';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'u':
|
||||
i -= 1; // Include the \u in the check for sanity sake
|
||||
r := get_u4_rune(s[i:]);
|
||||
if r < 0 {
|
||||
break loop;
|
||||
}
|
||||
i += 6;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
|
||||
|
||||
case '0':
|
||||
if spec == Specification.JSON5 {
|
||||
b[w] = '\x00';
|
||||
i += 1;
|
||||
w += 1;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
case 'v':
|
||||
if spec == Specification.JSON5 {
|
||||
b[w] = '\v';
|
||||
i += 1;
|
||||
w += 1;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
|
||||
case 'x':
|
||||
if spec == Specification.JSON5 {
|
||||
i -= 1; // Include the \x in the check for sanity sake
|
||||
r := get_u2_rune(s[i:]);
|
||||
if r < 0 {
|
||||
break loop;
|
||||
}
|
||||
i += 4;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
|
||||
case c == quote, c < ' ':
|
||||
break loop;
|
||||
|
||||
case c < utf8.RUNE_SELF:
|
||||
b[w] = c;
|
||||
i += 1;
|
||||
w += 1;
|
||||
|
||||
case:
|
||||
r, width := utf8.decode_rune_in_string(s[i:]);
|
||||
i += width;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
assert(buf_width <= width);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
}
|
||||
}
|
||||
|
||||
return string(b[:w]);
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
package json
|
||||
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Token :: struct {
|
||||
using pos: Pos,
|
||||
kind: Token_Kind,
|
||||
text: string,
|
||||
}
|
||||
|
||||
Token_Kind :: enum {
|
||||
Invalid,
|
||||
EOF,
|
||||
|
||||
Null,
|
||||
False,
|
||||
True,
|
||||
|
||||
Infinity,
|
||||
NaN,
|
||||
|
||||
Ident,
|
||||
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
|
||||
Colon,
|
||||
Comma,
|
||||
|
||||
Open_Brace,
|
||||
Close_Brace,
|
||||
|
||||
Open_Bracket,
|
||||
Close_Bracket,
|
||||
}
|
||||
|
||||
Tokenizer :: struct {
|
||||
using pos: Pos,
|
||||
data: []byte,
|
||||
r: rune, // current rune
|
||||
w: int, // current rune width in bytes
|
||||
curr_line_offset: int,
|
||||
spec: Specification,
|
||||
}
|
||||
|
||||
|
||||
|
||||
make_tokenizer :: proc(data: []byte, spec := Specification.JSON) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec};
|
||||
next_rune(&t);
|
||||
if t.r == utf8.RUNE_BOM {
|
||||
next_rune(&t);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
|
||||
if t.offset >= len(t.data) {
|
||||
return utf8.RUNE_EOF;
|
||||
}
|
||||
t.offset += t.w;
|
||||
t.r, t.w = utf8.decode_rune(t.data[t.offset:]);
|
||||
t.pos.column = t.offset - t.curr_line_offset;
|
||||
return t.r;
|
||||
}
|
||||
|
||||
|
||||
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_digits :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
if '0' <= t.r && t.r <= '9' {
|
||||
// Okay
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
next_rune(t);
|
||||
}
|
||||
}
|
||||
skip_hex_digits :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
switch t.r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
// Okay
|
||||
case:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_espace :: proc(t: ^Tokenizer) -> bool {
|
||||
switch t.r {
|
||||
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
|
||||
next_rune(t);
|
||||
return true;
|
||||
case 'u':
|
||||
// Expect 4 hexadecimal digits
|
||||
for i := 0; i < 4; i += 1 {
|
||||
r := next_rune(t);
|
||||
switch r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case:
|
||||
// Ignore the next rune regardless
|
||||
next_rune(t);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) -> rune {
|
||||
loop: for t.offset < len(t.data) {
|
||||
switch t.r {
|
||||
case ' ', '\t', '\v', '\f', '\r':
|
||||
next_rune(t);
|
||||
case '\n':
|
||||
t.line += 1;
|
||||
t.curr_line_offset = t.offset;
|
||||
t.pos.column = 1;
|
||||
next_rune(t);
|
||||
case:
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case 0x2028, 0x2029, 0xFEFF:
|
||||
next_rune(t);
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
return t.r;
|
||||
}
|
||||
|
||||
skip_to_next_line :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
r := next_rune(t);
|
||||
if r == '\n' {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_alphanum :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
switch next_rune(t) {
|
||||
case 'A'..'Z', 'a'..'z', '0'..'9', '_':
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
skip_whitespace(t);
|
||||
|
||||
token.pos = t.pos;
|
||||
|
||||
token.kind = .Invalid;
|
||||
|
||||
curr_rune := t.r;
|
||||
next_rune(t);
|
||||
|
||||
block: switch curr_rune {
|
||||
case utf8.RUNE_ERROR:
|
||||
err = .Illegal_Character;
|
||||
case utf8.RUNE_EOF, '\x00':
|
||||
token.kind = .EOF;
|
||||
err = .EOF;
|
||||
|
||||
case 'A'..'Z', 'a'..'z', '_':
|
||||
token.kind = .Ident;
|
||||
|
||||
skip_alphanum(t);
|
||||
|
||||
switch str := string(t.data[token.offset:t.offset]); str {
|
||||
case "null": token.kind = .Null;
|
||||
case "false": token.kind = .False;
|
||||
case "true": token.kind = .True;
|
||||
case:
|
||||
if t.spec == .JSON5 do switch str {
|
||||
case "Infinity": token.kind = .Infinity;
|
||||
case "NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
|
||||
case '+':
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '-':
|
||||
switch t.r {
|
||||
case '0'..'9':
|
||||
// Okay
|
||||
case:
|
||||
// Illegal use of +/-
|
||||
err = .Illegal_Character;
|
||||
|
||||
if t.spec == .JSON5 {
|
||||
if t.r == 'I' || t.r == 'N' {
|
||||
skip_alphanum(t);
|
||||
}
|
||||
switch string(t.data[token.offset:t.offset]) {
|
||||
case "-Infinity": token.kind = .Infinity;
|
||||
case "-NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
break block;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '0'..'9':
|
||||
token.kind = .Integer;
|
||||
if t.spec == .JSON5 { // Hexadecimal Numbers
|
||||
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
|
||||
next_rune(t);
|
||||
skip_hex_digits(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_digits(t);
|
||||
|
||||
if t.r == '.' {
|
||||
token.kind = .Float;
|
||||
next_rune(t);
|
||||
skip_digits(t);
|
||||
}
|
||||
if t.r == 'e' || t.r == 'E' {
|
||||
switch r := next_rune(t); r {
|
||||
case '+', '-':
|
||||
next_rune(t);
|
||||
}
|
||||
skip_digits(t);
|
||||
}
|
||||
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
|
||||
case '.':
|
||||
err = .Illegal_Character;
|
||||
if t.spec == .JSON5 { // Allow leading decimal point
|
||||
skip_digits(t);
|
||||
if t.r == 'e' || t.r == 'E' {
|
||||
switch r := next_rune(t); r {
|
||||
case '+', '-':
|
||||
next_rune(t);
|
||||
}
|
||||
skip_digits(t);
|
||||
}
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case '\'':
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case '"':
|
||||
token.kind = .String;
|
||||
quote := curr_rune;
|
||||
for t.offset < len(t.data) {
|
||||
r := t.r;
|
||||
if r == '\n' || r < 0 {
|
||||
err = .String_Not_Terminated;
|
||||
break;
|
||||
}
|
||||
next_rune(t);
|
||||
if r == quote {
|
||||
break;
|
||||
}
|
||||
if r == '\\' {
|
||||
scan_espace(t);
|
||||
}
|
||||
}
|
||||
|
||||
str := string(t.data[token.offset : t.offset]);
|
||||
if !is_valid_string_literal(str, t.spec) {
|
||||
err = .Invalid_String;
|
||||
}
|
||||
|
||||
|
||||
case ',': token.kind = .Comma;
|
||||
case ':': token.kind = .Colon;
|
||||
case '{': token.kind = .Open_Brace;
|
||||
case '}': token.kind = .Close_Brace;
|
||||
case '[': token.kind = .Open_Bracket;
|
||||
case ']': token.kind = .Close_Bracket;
|
||||
|
||||
case '/':
|
||||
err = .Illegal_Character;
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case '/':
|
||||
// Single-line comments
|
||||
skip_to_next_line(t);
|
||||
return get_token(t);
|
||||
case '*':
|
||||
// None-nested multi-line comments
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
if t.r == '*' {
|
||||
next_rune(t);
|
||||
if t.r == '/' {
|
||||
next_rune(t);
|
||||
return get_token(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
|
||||
case: err = .Illegal_Character;
|
||||
}
|
||||
|
||||
token.text = string(t.data[token.offset : t.offset]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
|
||||
if s[0] == '-' {
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
} else if spec == .JSON5 {
|
||||
if s[0] == '+' { // Allow positive sign
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch s[0] {
|
||||
case '0':
|
||||
s = s[1:];
|
||||
case '1'..'9':
|
||||
s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
case '.':
|
||||
if spec == .JSON5 { // Allow leading decimal point
|
||||
s = s[1:];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
|
||||
if spec == .JSON5 {
|
||||
if len(s) == 1 && s[0] == '.' { // Allow trailing decimal point
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
|
||||
s = s[2:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
}
|
||||
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
s = s[1:];
|
||||
switch s[0] {
|
||||
case '+', '-':
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
}
|
||||
|
||||
// The string should be empty now to be valid
|
||||
return s == "";
|
||||
}
|
||||
|
||||
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if len(s) < 2 {
|
||||
return false;
|
||||
}
|
||||
quote := s[0];
|
||||
if s[0] != s[len(s)-1] {
|
||||
return false;
|
||||
}
|
||||
if s[0] != '"' || s[len(s)-1] != '"' {
|
||||
if spec == .JSON5 {
|
||||
if s[0] != '\'' || s[len(s)-1] != '\'' {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
s = s[1 : len(s)-1];
|
||||
|
||||
i := 0;
|
||||
for i < len(s) {
|
||||
c := s[i];
|
||||
switch {
|
||||
case c == '\\':
|
||||
i += 1;
|
||||
if i >= len(s) {
|
||||
return false;
|
||||
}
|
||||
switch s[i] {
|
||||
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
|
||||
i += 1;
|
||||
case 'u':
|
||||
if i >= len(s) {
|
||||
return false;
|
||||
}
|
||||
hex := s[i+1:];
|
||||
if len(hex) < 4 {
|
||||
return false;
|
||||
}
|
||||
hex = hex[:4];
|
||||
i += 5;
|
||||
|
||||
for j := 0; j < 4; j += 1 {
|
||||
c2 := hex[j];
|
||||
switch c2 {
|
||||
case '0'..'9', 'a'..'z', 'A'..'Z':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case: return false;
|
||||
}
|
||||
|
||||
case c == quote, c < ' ':
|
||||
return false;
|
||||
|
||||
case c < utf8.RUNE_SELF:
|
||||
i += 1;
|
||||
|
||||
case:
|
||||
r, width := utf8.decode_rune_in_string(s[i:]);
|
||||
if r == utf8.RUNE_ERROR && width == 1 {
|
||||
return false;
|
||||
}
|
||||
i += width;
|
||||
}
|
||||
}
|
||||
if i == len(s) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package json
|
||||
|
||||
Specification :: enum {
|
||||
JSON,
|
||||
JSON5, // https://json5.org/
|
||||
// MJSON, // http://bitsquid.blogspot.com/2009/09/json-configuration-data.html
|
||||
}
|
||||
|
||||
Null :: distinct rawptr;
|
||||
Integer :: i64;
|
||||
Float :: f64;
|
||||
Boolean :: bool;
|
||||
String :: string;
|
||||
Array :: distinct [dynamic]Value;
|
||||
Object :: distinct map[string]Value;
|
||||
|
||||
Value :: struct {
|
||||
pos, end: Pos,
|
||||
value: union {
|
||||
Null,
|
||||
Integer,
|
||||
Float,
|
||||
Boolean,
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
}
|
||||
}
|
||||
|
||||
Pos :: struct {
|
||||
offset: int,
|
||||
line: int,
|
||||
column: int,
|
||||
}
|
||||
|
||||
|
||||
Error :: enum {
|
||||
None,
|
||||
|
||||
EOF, // Not necessarily an error
|
||||
|
||||
// Tokenizing Errors
|
||||
Illegal_Character,
|
||||
Invalid_Number,
|
||||
String_Not_Terminated,
|
||||
Invalid_String,
|
||||
|
||||
|
||||
// Parsing Errors
|
||||
Unexpected_Token,
|
||||
Expected_String_For_Object_Key,
|
||||
Duplicate_Object_Key,
|
||||
Expected_Colon_After_Key,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value.value {
|
||||
case Object:
|
||||
for key, elem in v {
|
||||
delete(key);
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
delete(v);
|
||||
case String:
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
|
||||
p := make_parser(data, spec, mem.nil_allocator());
|
||||
if p.spec == Specification.JSON5 {
|
||||
return validate_value(&p);
|
||||
}
|
||||
return validate_object(&p);
|
||||
}
|
||||
|
||||
validate_object_key :: proc(p: ^Parser) -> bool {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
return true;
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
err := expect_token(p, .String);
|
||||
return err == Error.None;
|
||||
}
|
||||
validate_object :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Brace); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
if !validate_object_key(p) {
|
||||
return false;
|
||||
}
|
||||
if colon_err := expect_token(p, .Colon); colon_err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !validate_value(p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := expect_token(p, .Close_Brace); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
validate_array :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
if !validate_value(p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err := expect_token(p, .Close_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
validate_value :: proc(p: ^Parser) -> bool {
|
||||
token := p.curr_token;
|
||||
|
||||
#partial switch token.kind {
|
||||
case .Null, .False, .True:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case .Integer, .Float:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case .String:
|
||||
advance_token(p);
|
||||
return is_valid_string_literal(token.text, p.spec);
|
||||
|
||||
case .Open_Brace:
|
||||
return validate_object(p);
|
||||
|
||||
case .Open_Bracket:
|
||||
return validate_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
#partial switch token.kind {
|
||||
case .Infinity, .NaN:
|
||||
advance_token(p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
-1211
File diff suppressed because it is too large
Load Diff
+1947
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
package hash
|
||||
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~u32(0);
|
||||
for b in data {
|
||||
@@ -13,197 +15,7 @@ crc64 :: proc(data: []byte) -> u64 {
|
||||
return ~result;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := &data[0];
|
||||
p1 := p + 4*nblocks;
|
||||
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := ^u32(p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
k1 *= c2_32;
|
||||
|
||||
h1 ~= k1;
|
||||
h1 = (h1 << 13) | (h1 >> 19);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4 ..];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
h1 ~= u32(len(data));
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ~= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ~= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := slice_ptr(^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(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough;
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough;
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough;
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough;
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough;
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= u64(data[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ~= h>>r;
|
||||
h *= m;
|
||||
h ~= h>>r;
|
||||
|
||||
return h;
|
||||
} else {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
|
||||
data32 := slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
|
||||
i := 0;
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data32[i]; i++;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data32[i]; i++;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
h2 *= m;
|
||||
h2 ~= k2;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data32[i]; i++;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := slice_to_bytes(data32[i..])[0..<3];
|
||||
match len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= u32(data8[0]);
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
h1 ~= h2>>18;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>22;
|
||||
h2 *= m;
|
||||
h1 ~= h2>>17;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
h := cast(u64)(h1)<<32 | cast(u64)(h2);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
immutable _crc32_table := [256]u32{
|
||||
@private _crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
@@ -269,7 +81,7 @@ immutable _crc32_table := [256]u32{
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
immutable _crc64_table := [256]u64{
|
||||
@private _crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
@@ -0,0 +1,229 @@
|
||||
package hash
|
||||
|
||||
import "core:mem"
|
||||
|
||||
adler32 :: proc(data: []byte) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = 1, 0;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
djb2 :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 5381;
|
||||
for b in data {
|
||||
hash = (hash << 5) + hash + u32(b); // hash * 33 + u32(b)
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
jenkins :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
hash += u32(b);
|
||||
hash += hash << 10;
|
||||
hash ~= hash >> 6;
|
||||
}
|
||||
hash += hash << 3;
|
||||
hash ~= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := raw_data(data);
|
||||
p1 := mem.ptr_offset(p, 4*nblocks);
|
||||
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
k1 := (cast(^u32)p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
k1 *= c2_32;
|
||||
|
||||
h1 ~= k1;
|
||||
h1 = (h1 << 13) | (h1 >> 19);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4:];
|
||||
k1: u32;
|
||||
switch 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;
|
||||
}
|
||||
|
||||
h1 ~= u32(len(data));
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ~= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ~= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []byte) -> 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)raw_data(data), len(data)/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
|
||||
k *= m;
|
||||
k ~= k>>r;
|
||||
k *= m;
|
||||
|
||||
h ~= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough;
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough;
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough;
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough;
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough;
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= u64(data[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ~= h>>r;
|
||||
h *= m;
|
||||
h ~= h>>r;
|
||||
|
||||
return h;
|
||||
} else {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
i := 0;
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data32[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
h2 *= m;
|
||||
h2 ~= k2;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3];
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= u32(data8[0]);
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
h1 ~= h2>>18;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>22;
|
||||
h2 *= m;
|
||||
h1 ~= h2>>17;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
return u64(h1)<<32 | u64(h2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sdbm :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
hash = u32(b) + (hash<<6) + (hash<<16) - hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// This is purely for documentation
|
||||
package intrinsics
|
||||
|
||||
// Types
|
||||
|
||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
||||
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
|
||||
// Atomics
|
||||
|
||||
atomic_fence :: proc() ---
|
||||
atomic_fence_acq :: proc() ---
|
||||
atomic_fence_rel :: proc() ---
|
||||
atomic_fence_acqrel :: proc() ---
|
||||
|
||||
atomic_store :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_rel :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_relaxed :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_unordered :: proc(dst: ^$T, val: $T) ---
|
||||
|
||||
atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_acq :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_unordered :: proc(dst: ^$T) -> T ---
|
||||
|
||||
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
|
||||
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
|
||||
cpu_relax :: proc() ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
type_base_type :: proc($T: typeid) -> type ---
|
||||
type_core_type :: proc($T: typeid) -> type ---
|
||||
type_elem_type :: proc($T: typeid) -> type ---
|
||||
|
||||
type_is_boolean :: proc($T: typeid) -> bool ---
|
||||
type_is_integer :: proc($T: typeid) -> bool ---
|
||||
type_is_rune :: proc($T: typeid) -> bool ---
|
||||
type_is_float :: proc($T: typeid) -> bool ---
|
||||
type_is_complex :: proc($T: typeid) -> bool ---
|
||||
type_is_quaternion :: proc($T: typeid) -> bool ---
|
||||
type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_big :: proc($T: typeid) -> bool ---
|
||||
type_is_unsigned :: proc($T: typeid) -> bool ---
|
||||
type_is_numeric :: proc($T: typeid) -> bool ---
|
||||
type_is_ordered :: proc($T: typeid) -> bool ---
|
||||
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
||||
type_is_indexable :: proc($T: typeid) -> bool ---
|
||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||
type_is_comparable :: proc($T: typeid) -> bool ---
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
|
||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_named :: proc($T: typeid) -> bool ---
|
||||
type_is_pointer :: proc($T: typeid) -> bool ---
|
||||
type_is_opaque :: proc($T: typeid) -> bool ---
|
||||
type_is_array :: proc($T: typeid) -> bool ---
|
||||
type_is_enumerated_array :: proc($T: typeid) -> bool ---
|
||||
type_is_slice :: proc($T: typeid) -> bool ---
|
||||
type_is_dynamic_array :: proc($T: typeid) -> bool ---
|
||||
type_is_map :: proc($T: typeid) -> bool ---
|
||||
type_is_struct :: proc($T: typeid) -> bool ---
|
||||
type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field_value :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
|
||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
|
||||
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||
|
||||
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
||||
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
||||
@@ -0,0 +1,151 @@
|
||||
package log
|
||||
|
||||
import "core:fmt";
|
||||
import "core:strings";
|
||||
import "core:os";
|
||||
import "core:time";
|
||||
|
||||
Level_Headers := [?]string{
|
||||
0..<10 = "[DEBUG] --- ",
|
||||
10..<20 = "[INFO ] --- ",
|
||||
20..<30 = "[WARN ] --- ",
|
||||
30..<40 = "[ERROR] --- ",
|
||||
40..<50 = "[FATAL] --- ",
|
||||
};
|
||||
|
||||
Default_Console_Logger_Opts :: Options{
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
Default_File_Logger_Opts :: Options{
|
||||
.Level,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
|
||||
File_Console_Logger_Data :: struct {
|
||||
file_handle: os.Handle,
|
||||
ident: string,
|
||||
}
|
||||
|
||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.file_handle = h;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, lowest, opt};
|
||||
}
|
||||
|
||||
destroy_file_logger :: proc(log: ^Logger) {
|
||||
data := cast(^File_Console_Logger_Data)log.data;
|
||||
if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.file_handle = os.INVALID_HANDLE;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, lowest, opt};
|
||||
}
|
||||
|
||||
destroy_console_logger :: proc(log: ^Logger) {
|
||||
free(log.data);
|
||||
}
|
||||
|
||||
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
data := cast(^File_Console_Logger_Data)logger_data;
|
||||
h: os.Handle = os.stdout if level <= Level.Error else os.stderr;
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
h = data.file_handle;
|
||||
}
|
||||
backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_slice(backing[:]);
|
||||
|
||||
do_level_header(options, level, &buf);
|
||||
|
||||
when time.IS_SUPPORTED {
|
||||
if Full_Timestamp_Opts & options != nil {
|
||||
fmt.sbprint(&buf, "[");
|
||||
t := time.now();
|
||||
y, m, d := time.date(t);
|
||||
h, min, s := time.clock(t);
|
||||
if .Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
|
||||
if .Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
|
||||
fmt.sbprint(&buf, "] ");
|
||||
}
|
||||
}
|
||||
|
||||
do_location_header(options, &buf, location);
|
||||
|
||||
if .Thread_Id in options {
|
||||
// NOTE(Oskar): not using context.thread_id here since that could be
|
||||
// incorrect when replacing context for a thread.
|
||||
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id());
|
||||
}
|
||||
|
||||
if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
RESET :: "\x1b[0m";
|
||||
RED :: "\x1b[31m";
|
||||
YELLOW :: "\x1b[33m";
|
||||
DARK_GREY :: "\x1b[90m";
|
||||
|
||||
col := RESET;
|
||||
switch level {
|
||||
case .Debug: col = DARK_GREY;
|
||||
case .Info: col = RESET;
|
||||
case .Warning: col = YELLOW;
|
||||
case .Error, .Fatal: col = RED;
|
||||
}
|
||||
|
||||
if .Level in opts {
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, col);
|
||||
fmt.sbprint(str, Level_Headers[level]);
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
|
||||
}
|
||||
}
|
||||
|
||||
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
|
||||
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
|
||||
|
||||
file := location.file_path;
|
||||
if .Short_File_Path in opts {
|
||||
last := 0;
|
||||
for r, i in location.file_path {
|
||||
if r == '/' {
|
||||
last = i+1;
|
||||
}
|
||||
}
|
||||
file = location.file_path[last:];
|
||||
}
|
||||
|
||||
if Location_File_Opts & opts != nil {
|
||||
fmt.sbprint(buf, file);
|
||||
}
|
||||
if .Line in opts {
|
||||
if Location_File_Opts & opts != nil {
|
||||
fmt.sbprint(buf, ":");
|
||||
}
|
||||
fmt.sbprint(buf, location.line);
|
||||
}
|
||||
|
||||
if .Procedure in opts {
|
||||
if (Location_File_Opts | {.Line}) & opts != nil {
|
||||
fmt.sbprint(buf, ":");
|
||||
}
|
||||
fmt.sbprintf(buf, "%s()", location.procedure);
|
||||
}
|
||||
|
||||
fmt.sbprint(buf, "] ");
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
import "core:sync"
|
||||
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
|
||||
Level :: runtime.Logger_Level;
|
||||
/*
|
||||
Logger_Level :: enum {
|
||||
Debug = 0,
|
||||
Info = 10,
|
||||
Warning = 20,
|
||||
Error = 30,
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
|
||||
Option :: runtime.Logger_Option;
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
Date,
|
||||
Time,
|
||||
Short_File_Path,
|
||||
Long_File_Path,
|
||||
Line,
|
||||
Procedure,
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
|
||||
Options :: runtime.Logger_Options;
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
.Time
|
||||
};
|
||||
Location_Header_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
};
|
||||
Location_File_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path
|
||||
};
|
||||
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc;
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
|
||||
Logger :: runtime.Logger;
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
lowest_level: Level,
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil, Level.Debug, nil};
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
infof :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Info, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Warning, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Error, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
|
||||
debug :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Debug, args=args, sep=sep, location=location);
|
||||
}
|
||||
info :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Info, args=args, sep=sep, location=location);
|
||||
}
|
||||
warn :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Warning, args=args, sep=sep, location=location);
|
||||
}
|
||||
error :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Error, args=args, sep=sep, location=location);
|
||||
}
|
||||
fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Fatal, args=args, sep=sep, location=location);
|
||||
}
|
||||
|
||||
panic :: proc(args: ..any, location := #caller_location) -> ! {
|
||||
log(level=.Fatal, args=args, location=location);
|
||||
runtime.panic("log.panic");
|
||||
}
|
||||
panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! {
|
||||
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
runtime.panic("log.panicf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) {
|
||||
logger := context.logger;
|
||||
if level < logger.lowest_level {
|
||||
return;
|
||||
}
|
||||
str := fmt.tprint(args=args, sep=sep); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
}
|
||||
|
||||
logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logger := context.logger;
|
||||
if level < logger.lowest_level {
|
||||
return;
|
||||
}
|
||||
str := fmt.tprintf(fmt_str, ..args);
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package log
|
||||
|
||||
|
||||
Multi_Logger_Data :: struct {
|
||||
loggers: []Logger,
|
||||
}
|
||||
|
||||
create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
data := new(Multi_Logger_Data);
|
||||
data.loggers = make([]Logger, len(logs));
|
||||
copy(data.loggers, logs);
|
||||
return Logger{multi_logger_proc, data, Level.Debug, nil};
|
||||
}
|
||||
|
||||
destroy_multi_logger :: proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
}
|
||||
|
||||
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
|
||||
options: Options, location := #caller_location) {
|
||||
data := cast(^Multi_Logger_Data)logger_data;
|
||||
for log in data.loggers {
|
||||
if level < log.lowest_level {
|
||||
return;
|
||||
}
|
||||
log.procedure(log.data, level, text, log.options, location);
|
||||
}
|
||||
}
|
||||
-378
@@ -1,378 +0,0 @@
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
|
||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||
|
||||
EPSILON :: 1.19209290e-7;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
|
||||
Vec2 :: [vector 2]f32;
|
||||
Vec3 :: [vector 3]f32;
|
||||
Vec4 :: [vector 4]f32;
|
||||
|
||||
Mat2 :: [2]Vec2;
|
||||
Mat3 :: [3]Vec3;
|
||||
Mat4 :: [4]Vec4;
|
||||
|
||||
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
|
||||
sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
|
||||
cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
|
||||
tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); }
|
||||
tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); }
|
||||
|
||||
pow :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32";
|
||||
pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
|
||||
|
||||
|
||||
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
|
||||
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
|
||||
|
||||
sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
|
||||
sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
|
||||
|
||||
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
|
||||
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
|
||||
bit_reverse :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64";
|
||||
|
||||
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
|
||||
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32, x);
|
||||
iy := transmute(u32, y);
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32, ix);
|
||||
}
|
||||
|
||||
copy_sign :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64, x);
|
||||
iy := transmute(u64, y);
|
||||
ix &= 0x7fff_ffff_ffff_ff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64, ix);
|
||||
}
|
||||
|
||||
round :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
round :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
|
||||
floor :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
|
||||
ceil :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); } // TODO: Get accurate versions
|
||||
ceil :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); } // TODO: Get accurate versions
|
||||
|
||||
remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
|
||||
mod :: proc(x, y: f32) -> f32 {
|
||||
y = abs(y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod :: proc(x, y: f64) -> f64 {
|
||||
y = abs(y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
cross :: proc(x, y: Vec3) -> Vec3 {
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
|
||||
mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); }
|
||||
|
||||
norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{mag(v)}; }
|
||||
norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{mag(v)}; }
|
||||
norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{mag(v)}; }
|
||||
|
||||
norm0 :: proc(v: Vec2) -> Vec2 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec2{0};
|
||||
}
|
||||
return v / Vec2{m};
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec3) -> Vec3 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec3{0};
|
||||
}
|
||||
return v / Vec3{m};
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec4) -> Vec4 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec4{0};
|
||||
}
|
||||
return v / Vec4{m};
|
||||
}
|
||||
|
||||
|
||||
|
||||
mat4_identity :: proc() -> Mat4 {
|
||||
return Mat4{
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
};
|
||||
}
|
||||
|
||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4;
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2] +
|
||||
a[3][i]*b[j][3];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
return Vec4{
|
||||
m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w,
|
||||
m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
|
||||
m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w,
|
||||
m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
|
||||
};
|
||||
}
|
||||
|
||||
inverse :: proc(m: Mat4) -> Mat4 {
|
||||
o: Mat4;
|
||||
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
|
||||
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
|
||||
|
||||
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||
m[0][1] * o[0][1] +
|
||||
m[0][2] * o[0][2] +
|
||||
m[0][3] * o[0][3]);
|
||||
|
||||
o[0][0] *= ood;
|
||||
o[0][1] *= ood;
|
||||
o[0][2] *= ood;
|
||||
o[0][3] *= ood;
|
||||
o[1][0] *= ood;
|
||||
o[1][1] *= ood;
|
||||
o[1][2] *= ood;
|
||||
o[1][3] *= ood;
|
||||
o[2][0] *= ood;
|
||||
o[2][1] *= ood;
|
||||
o[2][2] *= ood;
|
||||
o[2][3] *= ood;
|
||||
o[3][0] *= ood;
|
||||
o[3][1] *= ood;
|
||||
o[3][2] *= ood;
|
||||
o[3][3] *= ood;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[3][0] = v.x;
|
||||
m[3][1] = v.y;
|
||||
m[3][2] = v.z;
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
c := cos(angle_radians);
|
||||
s := sin(angle_radians);
|
||||
|
||||
a := norm(v);
|
||||
t := a * Vec3{1-c};
|
||||
|
||||
rot := mat4_identity();
|
||||
|
||||
rot[0][0] = c + t.x*a.x;
|
||||
rot[0][1] = 0 + t.x*a.y + s*a.z;
|
||||
rot[0][2] = 0 + t.x*a.z - s*a.y;
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t.y*a.x - s*a.z;
|
||||
rot[1][1] = c + t.y*a.y;
|
||||
rot[1][2] = 0 + t.y*a.z + s*a.x;
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t.z*a.x + s*a.y;
|
||||
rot[2][1] = 0 + t.z*a.y - s*a.x;
|
||||
rot[2][2] = c + t.z*a.z;
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||
m[0][0] *= v.x;
|
||||
m[1][1] *= v.y;
|
||||
m[2][2] *= v.z;
|
||||
return m;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||
m[0][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[2][2] *= s;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
f := norm(centre - eye);
|
||||
s := norm(cross(f, up));
|
||||
u := cross(s, f);
|
||||
|
||||
m: Mat4;
|
||||
|
||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
|
||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
|
||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
|
||||
m[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
|
||||
|
||||
return m;
|
||||
}
|
||||
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan(0.5 * fovy);
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[2][3] = -1.0;
|
||||
m[3][2] = -2.0*far*near / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[0][0] = +2.0 / (right - left);
|
||||
m[1][1] = +2.0 / (top - bottom);
|
||||
m[2][2] = -2.0 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
@@ -0,0 +1,531 @@
|
||||
package math_bits
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
U8_MIN :: 0;
|
||||
U16_MIN :: 0;
|
||||
U32_MIN :: 0;
|
||||
U64_MIN :: 0;
|
||||
|
||||
U8_MAX :: 1 << 8 - 1;
|
||||
U16_MAX :: 1 << 16 - 1;
|
||||
U32_MAX :: 1 << 32 - 1;
|
||||
U64_MAX :: 1 << 64 - 1;
|
||||
|
||||
I8_MIN :: - 1 << 7;
|
||||
I16_MIN :: - 1 << 15;
|
||||
I32_MIN :: - 1 << 31;
|
||||
I64_MIN :: - 1 << 63;
|
||||
|
||||
I8_MAX :: 1 << 7 - 1;
|
||||
I16_MAX :: 1 << 15 - 1;
|
||||
I32_MAX :: 1 << 31 - 1;
|
||||
I64_MAX :: 1 << 63 - 1;
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
|
||||
@(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 ---
|
||||
}
|
||||
|
||||
|
||||
trailing_zeros_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u64) {
|
||||
return uint(trailing_zeros64(u64(i)));
|
||||
} else {
|
||||
return uint(trailing_zeros32(u32(i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
leading_zeros_u8 :: proc(i: u8) -> int {
|
||||
return 8*size_of(i) - len_u8(i);
|
||||
}
|
||||
leading_zeros_u16 :: proc(i: u16) -> int {
|
||||
return 8*size_of(i) - len_u16(i);
|
||||
}
|
||||
leading_zeros_u32 :: proc(i: u32) -> int {
|
||||
return 8*size_of(i) - len_u32(i);
|
||||
}
|
||||
leading_zeros_u64 :: proc(i: u64) -> int {
|
||||
return 8*size_of(i) - len_u64(i);
|
||||
}
|
||||
|
||||
|
||||
byte_swap_u16 :: proc(x: u16) -> u16 {
|
||||
return u16(runtime.bswap_16(u16(x)));
|
||||
}
|
||||
byte_swap_u32 :: proc(x: u32) -> u32 {
|
||||
return u32(runtime.bswap_32(u32(x)));
|
||||
}
|
||||
byte_swap_u64 :: proc(x: u64) -> u64 {
|
||||
return u64(runtime.bswap_64(u64(x)));
|
||||
}
|
||||
byte_swap_i16 :: proc(x: i16) -> i16 {
|
||||
return i16(runtime.bswap_16(u16(x)));
|
||||
}
|
||||
byte_swap_i32 :: proc(x: i32) -> i32 {
|
||||
return i32(runtime.bswap_32(u32(x)));
|
||||
}
|
||||
byte_swap_i64 :: proc(x: i64) -> i64 {
|
||||
return i64(runtime.bswap_64(u64(x)));
|
||||
}
|
||||
byte_swap_u128 :: proc(x: u128) -> u128 {
|
||||
return u128(runtime.bswap_128(u128(x)));
|
||||
}
|
||||
byte_swap_i128 :: proc(x: i128) -> i128 {
|
||||
return i128(runtime.bswap_128(u128(x)));
|
||||
}
|
||||
|
||||
byte_swap_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
return uint(byte_swap_u32(u32(i)));
|
||||
} else {
|
||||
return uint(byte_swap_u64(u64(i)));
|
||||
}
|
||||
}
|
||||
byte_swap_int :: proc(i: int) -> int {
|
||||
when size_of(int) == size_of(i32) {
|
||||
return int(byte_swap_i32(i32(i)));
|
||||
} else {
|
||||
return int(byte_swap_i64(i64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
byte_swap :: proc{
|
||||
byte_swap_u16,
|
||||
byte_swap_u32,
|
||||
byte_swap_u64,
|
||||
byte_swap_u128,
|
||||
byte_swap_i16,
|
||||
byte_swap_i32,
|
||||
byte_swap_i64,
|
||||
byte_swap_i128,
|
||||
byte_swap_uint,
|
||||
byte_swap_int,
|
||||
};
|
||||
|
||||
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
|
||||
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
|
||||
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
|
||||
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
|
||||
|
||||
|
||||
rotate_left8 :: proc(x: u8, k: int) -> u8 {
|
||||
n :: 8;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left16 :: proc(x: u16, k: int) -> u16 {
|
||||
n :: 16;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left32 :: proc(x: u32, k: int) -> u32 {
|
||||
n :: 32;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left64 :: proc(x: u64, k: int) -> u64 {
|
||||
n :: 64;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
|
||||
rotate_left :: proc(x: uint, k: int) -> uint {
|
||||
n :: 8*size_of(uint);
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
|
||||
from_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
from_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
to_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
to_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
|
||||
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_add :: proc{
|
||||
overflowing_add_u8, overflowing_add_i8,
|
||||
overflowing_add_u16, overflowing_add_i16,
|
||||
overflowing_add_u32, overflowing_add_i32,
|
||||
overflowing_add_u64, overflowing_add_i64,
|
||||
overflowing_add_uint, overflowing_add_int,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_sub :: proc{
|
||||
overflowing_sub_u8, overflowing_sub_i8,
|
||||
overflowing_sub_u16, overflowing_sub_i16,
|
||||
overflowing_sub_u32, overflowing_sub_i32,
|
||||
overflowing_sub_u64, overflowing_sub_i64,
|
||||
overflowing_sub_uint, overflowing_sub_int,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc{
|
||||
overflowing_mul_u8, overflowing_mul_i8,
|
||||
overflowing_mul_u16, overflowing_mul_i16,
|
||||
overflowing_mul_u32, overflowing_mul_i32,
|
||||
overflowing_mul_u64, overflowing_mul_i64,
|
||||
overflowing_mul_uint, overflowing_mul_int,
|
||||
};
|
||||
|
||||
|
||||
len_u8 :: proc(x: u8) -> int {
|
||||
return int(len_u8_table[x]);
|
||||
}
|
||||
len_u16 :: proc(x: u16) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n = 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_u32 :: proc(x: u32) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<16 {
|
||||
x >>= 16;
|
||||
n = 16;
|
||||
}
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n += 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_u64 :: proc(x: u64) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<32 {
|
||||
x >>= 32;
|
||||
n = 32;
|
||||
}
|
||||
if x >= 1<<16 {
|
||||
x >>= 16;
|
||||
n += 16;
|
||||
}
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n += 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_uint :: proc(x: uint) -> (n: int) {
|
||||
when size_of(uint) == size_of(u64) {
|
||||
return len_u64(u64(x));
|
||||
} else {
|
||||
return len_u32(u32(x));
|
||||
}
|
||||
}
|
||||
|
||||
// returns the minimum number of bits required to represent x
|
||||
len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint};
|
||||
|
||||
|
||||
add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add :: proc{add_u32, add_u64, add_uint};
|
||||
|
||||
|
||||
sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub :: proc{sub_u32, sub_u64, sub_uint};
|
||||
|
||||
|
||||
mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
|
||||
z := u64(x) * u64(y);
|
||||
hi, lo = u32(z>>32), u32(z);
|
||||
return;
|
||||
}
|
||||
mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
|
||||
mask :: 1<<32 - 1;
|
||||
|
||||
x0, x1 := x & mask, x >> 32;
|
||||
y0, y1 := y & mask, y >> 32;
|
||||
|
||||
w0 := x0 * y0;
|
||||
t := x1*y0 + w0>>32;
|
||||
|
||||
w1, w2 := t & mask, t >> 32;
|
||||
w1 += x0 * y1;
|
||||
hi = x1*y1 + w2 + w1>>32;
|
||||
lo = x * y;
|
||||
return;
|
||||
}
|
||||
|
||||
mul_uint :: proc(x, y: uint) -> (hi, lo: uint) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
a, b := mul_u32(u32(x), u32(y));
|
||||
} else {
|
||||
#assert(size_of(uint) == size_of(u64));
|
||||
a, b := mul_u64(u64(x), u64(y));
|
||||
}
|
||||
return uint(a), uint(b);
|
||||
}
|
||||
|
||||
mul :: proc{mul_u32, mul_u64, mul_uint};
|
||||
|
||||
|
||||
div_u32 :: proc(hi, lo, y: u32) -> (quo, rem: u32) {
|
||||
assert(y != 0 && y <= hi);
|
||||
z := u64(hi)<<32 | u64(lo);
|
||||
quo, rem = u32(z/u64(y)), u32(z%u64(y));
|
||||
return;
|
||||
}
|
||||
div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
|
||||
y := y;
|
||||
two32 :: 1 << 32;
|
||||
mask32 :: two32 - 1;
|
||||
if y == 0 {
|
||||
panic("divide error");
|
||||
}
|
||||
if y <= hi {
|
||||
panic("overflow error");
|
||||
}
|
||||
|
||||
s := uint(leading_zeros_u64(y));
|
||||
y <<= s;
|
||||
|
||||
yn1 := y >> 32;
|
||||
yn0 := y & mask32;
|
||||
un32 := hi<<s | lo>>(64-s);
|
||||
un10 := lo << s;
|
||||
un1 := un10 >> 32;
|
||||
un0 := un10 & mask32;
|
||||
q1 := un32 / yn1;
|
||||
rhat := un32 - q1*yn1;
|
||||
|
||||
for q1 >= two32 || q1*yn0 > two32*rhat+un1 {
|
||||
q1 -= 1;
|
||||
rhat += yn1;
|
||||
if rhat >= two32 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
un21 := un32*two32 + un1 - q1*y;
|
||||
q0 := un21 / yn1;
|
||||
rhat = un21 - q0*yn1;
|
||||
|
||||
for q0 >= two32 || q0*yn0 > two32*rhat+un0 {
|
||||
q0 -= 1;
|
||||
rhat += yn1;
|
||||
if rhat >= two32 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s;
|
||||
}
|
||||
div_uint :: proc(hi, lo, y: uint) -> (quo, rem: uint) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
a, b := div_u32(u32(hi), u32(lo), u32(y));
|
||||
} else {
|
||||
#assert(size_of(uint) == size_of(u64));
|
||||
a, b := div_u64(u64(hi), u64(lo), u64(y));
|
||||
}
|
||||
return uint(a), uint(b);
|
||||
}
|
||||
div :: proc{div_u32, div_u64, div_uint};
|
||||
|
||||
|
||||
|
||||
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
|
||||
is_power_of_two :: proc{
|
||||
is_power_of_two_u8, is_power_of_two_i8,
|
||||
is_power_of_two_u16, is_power_of_two_i16,
|
||||
is_power_of_two_u32, is_power_of_two_i32,
|
||||
is_power_of_two_u64, is_power_of_two_i64,
|
||||
is_power_of_two_uint, is_power_of_two_int,
|
||||
};
|
||||
|
||||
|
||||
@private
|
||||
len_u8_table := [256]u8{
|
||||
0 = 0,
|
||||
1 = 1,
|
||||
2..<4 = 2,
|
||||
4..<8 = 3,
|
||||
8..<16 = 4,
|
||||
16..<32 = 5,
|
||||
32..<64 = 6,
|
||||
64..<128 = 7,
|
||||
128..<256 = 8,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
package linalg
|
||||
|
||||
import "core:math"
|
||||
import "intrinsics"
|
||||
|
||||
// Generic
|
||||
|
||||
@private IS_NUMERIC :: intrinsics.type_is_numeric;
|
||||
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
|
||||
@private IS_ARRAY :: intrinsics.type_is_array;
|
||||
|
||||
|
||||
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
c += a[i] * b[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
dot :: proc{vector_dot, quaternion128_dot, quaternion256_dot};
|
||||
|
||||
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
|
||||
return a[0]*b[1] - b[0]*a[1];
|
||||
}
|
||||
|
||||
vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
c[0] = a[1]*b[2] - b[1]*a[2];
|
||||
c[1] = a[2]*b[0] - b[2]*a[0];
|
||||
c[2] = a[0]*b[1] - b[0]*a[1];
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{vector_cross2, vector_cross3};
|
||||
cross :: vector_cross;
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return v / length(v);
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return q/abs(q);
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize};
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
m := length(v);
|
||||
return 0 if m == 0 else v/m;
|
||||
}
|
||||
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
m := abs(q);
|
||||
return 0 if m == 0 else q/m;
|
||||
}
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return math.sqrt(dot(v, v));
|
||||
}
|
||||
|
||||
vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return abs(q);
|
||||
}
|
||||
|
||||
quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return dot(q, q);
|
||||
}
|
||||
|
||||
length :: proc{vector_length, quaternion_length};
|
||||
length2 :: proc{vector_length2, quaternion_length2};
|
||||
|
||||
|
||||
vector_lerp :: proc(x, y, t: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ti := t[i];
|
||||
s[i] = x[i]*(1-ti) + y[i]*ti;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_unlerp :: proc(a, b, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ai := a[i];
|
||||
s[i] = (x[i]-ai)/(b[i]-ai);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sin :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sin(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_cos :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.cos(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_tan :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.tan(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_asin :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.asin(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_acos :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.acos(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan2 :: proc(y, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(y[i], x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_pow :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.pow(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_expr :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.expr(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sqrt :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sqrt(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_abs :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = abs(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sign :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sign(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_floor :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.floor(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_ceil :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.ceil(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_mod :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.mod(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_min :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = min(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_max :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = max(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_clamp :: proc(x, a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = clamp(x[i], a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_mix :: proc(x, y, a: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i]*(1-a[i]) + y[i]*a[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_step :: proc(edge, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = 0 if x[i] < edge[i] else 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smoothstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * (3 - 2*t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smootherstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * t * (t * (6*t - 15) + 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_distance :: proc(p0, p1: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
return length(p1 - p0);
|
||||
}
|
||||
|
||||
vector_reflect :: proc(i, n: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
b := n * (2 * dot(n, i));
|
||||
return i - b;
|
||||
}
|
||||
|
||||
vector_refract :: proc(i, n: $V/[$N]$E, eta: E) -> V where IS_NUMERIC(E) {
|
||||
dv := dot(n, i);
|
||||
k := 1 - eta*eta - (1 - dv*dv);
|
||||
a := i * eta;
|
||||
b := n * eta*dv*math.sqrt(k);
|
||||
return (a - b) * E(int(k >= 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
|
||||
for i in 0..<N do m[i][i] = E(1);
|
||||
return m;
|
||||
}
|
||||
|
||||
trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
||||
for i in 0..<N {
|
||||
tr += m[i][i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
|
||||
for j in 0..<M {
|
||||
for i in 0..<N {
|
||||
m[j][i] = a[i][j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
for k in 0..<N {
|
||||
for j in 0..<N {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E),
|
||||
I != K {
|
||||
for k in 0..<K {
|
||||
for j in 0..<J {
|
||||
for i in 0..<I {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<I {
|
||||
for j in 0..<J {
|
||||
c[j] += a[i][j] * b[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f32)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
|
||||
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f64)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion_mul_vector3 :: proc{quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
|
||||
mul :: proc{
|
||||
matrix_mul,
|
||||
matrix_mul_differ,
|
||||
matrix_mul_vector,
|
||||
quaternion128_mul_vector3,
|
||||
quaternion256_mul_vector3,
|
||||
};
|
||||
|
||||
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
|
||||
return &v[0];
|
||||
}
|
||||
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
|
||||
return &m[0][0];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,799 @@
|
||||
package linalg
|
||||
|
||||
import "core:math"
|
||||
|
||||
|
||||
// Specific
|
||||
|
||||
Float :: f32;
|
||||
|
||||
FLOAT_EPSILON :: 1e-7 when size_of(Float) == 4 else 1e-15;
|
||||
|
||||
Vector2 :: distinct [2]Float;
|
||||
Vector3 :: distinct [3]Float;
|
||||
Vector4 :: distinct [4]Float;
|
||||
|
||||
Matrix1x1 :: distinct [1][1]Float;
|
||||
Matrix1x2 :: distinct [1][2]Float;
|
||||
Matrix1x3 :: distinct [1][3]Float;
|
||||
Matrix1x4 :: distinct [1][4]Float;
|
||||
|
||||
Matrix2x1 :: distinct [2][1]Float;
|
||||
Matrix2x2 :: distinct [2][2]Float;
|
||||
Matrix2x3 :: distinct [2][3]Float;
|
||||
Matrix2x4 :: distinct [2][4]Float;
|
||||
|
||||
Matrix3x1 :: distinct [3][1]Float;
|
||||
Matrix3x2 :: distinct [3][2]Float;
|
||||
Matrix3x3 :: distinct [3][3]Float;
|
||||
Matrix3x4 :: distinct [3][4]Float;
|
||||
|
||||
Matrix4x1 :: distinct [4][1]Float;
|
||||
Matrix4x2 :: distinct [4][2]Float;
|
||||
Matrix4x3 :: distinct [4][3]Float;
|
||||
Matrix4x4 :: distinct [4][4]Float;
|
||||
|
||||
Matrix1 :: Matrix1x1;
|
||||
Matrix2 :: Matrix2x2;
|
||||
Matrix3 :: Matrix3x3;
|
||||
Matrix4 :: Matrix4x4;
|
||||
|
||||
Quaternion :: distinct (quaternion128 when size_of(Float) == size_of(f32) else quaternion256);
|
||||
|
||||
MATRIX1_IDENTITY :: Matrix1{{1}};
|
||||
MATRIX2_IDENTITY :: Matrix2{{1, 0}, {0, 1}};
|
||||
MATRIX3_IDENTITY :: Matrix3{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
||||
MATRIX4_IDENTITY :: Matrix4{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
|
||||
|
||||
QUATERNION_IDENTITY :: Quaternion(1);
|
||||
|
||||
VECTOR3_X_AXIS :: Vector3{1, 0, 0};
|
||||
VECTOR3_Y_AXIS :: Vector3{0, 1, 0};
|
||||
VECTOR3_Z_AXIS :: Vector3{0, 0, 1};
|
||||
|
||||
|
||||
radians :: proc(degrees: Float) -> Float {
|
||||
return math.TAU * degrees / 360.0;
|
||||
}
|
||||
|
||||
degrees :: proc(radians: Float) -> Float {
|
||||
return 360.0 * radians / math.TAU;
|
||||
}
|
||||
|
||||
|
||||
vector2_orthogonal :: proc(v: Vector2) -> Vector2 {
|
||||
return {-v.y, v.x};
|
||||
}
|
||||
|
||||
vector3_orthogonal :: proc(v: Vector3) -> Vector3 {
|
||||
x := abs(v.x);
|
||||
y := abs(v.y);
|
||||
z := abs(v.z);
|
||||
|
||||
other: Vector3;
|
||||
if x < y {
|
||||
if x < z {
|
||||
other = {1, 0, 0};
|
||||
} else {
|
||||
other = {0, 0, 1};
|
||||
}
|
||||
} else {
|
||||
if y < z {
|
||||
other = {0, 1, 0};
|
||||
} else {
|
||||
other = {0, 0, 1};
|
||||
}
|
||||
}
|
||||
return normalize(cross(v, other));
|
||||
}
|
||||
|
||||
|
||||
vector4_srgb_to_linear :: proc(col: Vector4) -> Vector4 {
|
||||
r := math.pow(col.x, 2.2);
|
||||
g := math.pow(col.y, 2.2);
|
||||
b := math.pow(col.z, 2.2);
|
||||
a := col.w;
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
vector4_linear_to_srgb :: proc(col: Vector4) -> Vector4 {
|
||||
a :: 2.51;
|
||||
b :: 0.03;
|
||||
c :: 2.43;
|
||||
d :: 0.59;
|
||||
e :: 0.14;
|
||||
|
||||
x := col.x;
|
||||
y := col.y;
|
||||
z := col.z;
|
||||
|
||||
x = (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
y = (y * (a * y + b)) / (y * (c * y + d) + e);
|
||||
z = (z * (a * z + b)) / (z * (c * z + d) + e);
|
||||
|
||||
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2);
|
||||
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2);
|
||||
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2);
|
||||
|
||||
return {x, y, z, col.w};
|
||||
}
|
||||
|
||||
vector4_hsl_to_rgb :: proc(h, s, l: Float, a: Float = 1) -> Vector4 {
|
||||
hue_to_rgb :: proc(p, q, t: Float) -> Float {
|
||||
t := t;
|
||||
if t < 0 do t += 1;
|
||||
if t > 1 do t -= 1;
|
||||
switch {
|
||||
case t < 1.0/6.0: return p + (q - p) * 6.0 * t;
|
||||
case t < 1.0/2.0: return q;
|
||||
case t < 2.0/3.0: return p + (q - p) * 6.0 * (2.0/3.0 - t);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
r, g, b: Float;
|
||||
if s == 0 {
|
||||
r = l;
|
||||
g = l;
|
||||
b = l;
|
||||
} else {
|
||||
q := l * (1+s) if l < 0.5 else l+s - l*s;
|
||||
p := 2*l - q;
|
||||
r = hue_to_rgb(p, q, h + 1.0/3.0);
|
||||
g = hue_to_rgb(p, q, h);
|
||||
b = hue_to_rgb(p, q, h - 1.0/3.0);
|
||||
}
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
vector4_rgb_to_hsl :: proc(col: Vector4) -> Vector4 {
|
||||
r := col.x;
|
||||
g := col.y;
|
||||
b := col.z;
|
||||
a := col.w;
|
||||
v_min := min(r, g, b);
|
||||
v_max := max(r, g, b);
|
||||
h, s, l: Float;
|
||||
h = 0.0;
|
||||
s = 0.0;
|
||||
l = (v_min + v_max) * 0.5;
|
||||
|
||||
if v_max != v_min {
|
||||
d: = v_max - v_min;
|
||||
s = d / (2.0 - v_max - v_min) if l > 0.5 else d / (v_max + v_min);
|
||||
switch {
|
||||
case v_max == r:
|
||||
h = (g - b) / d + (6.0 if g < b else 0.0);
|
||||
case v_max == g:
|
||||
h = (b - r) / d + 2.0;
|
||||
case v_max == b:
|
||||
h = (r - g) / d + 4.0;
|
||||
}
|
||||
|
||||
h *= 1.0/6.0;
|
||||
}
|
||||
|
||||
return {h, s, l, a};
|
||||
}
|
||||
|
||||
|
||||
|
||||
quaternion_angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion {
|
||||
t := angle_radians*0.5;
|
||||
w := math.cos(t);
|
||||
v := normalize(axis) * math.sin(t);
|
||||
return quaternion(w, v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_euler_angles :: proc(roll, pitch, yaw: Float) -> Quaternion {
|
||||
x, y, z := roll, pitch, yaw;
|
||||
a, b, c := x, y, z;
|
||||
|
||||
ca, sa := math.cos(a*0.5), math.sin(a*0.5);
|
||||
cb, sb := math.cos(b*0.5), math.sin(b*0.5);
|
||||
cc, sc := math.cos(c*0.5), math.sin(c*0.5);
|
||||
|
||||
q: Quaternion;
|
||||
q.x = sa*cb*cc - ca*sb*sc;
|
||||
q.y = ca*sb*cc + sa*cb*sc;
|
||||
q.z = ca*cb*sc - sa*sb*cc;
|
||||
q.w = ca*cb*cc + sa*sb*sc;
|
||||
return q;
|
||||
}
|
||||
|
||||
euler_angles_from_quaternion :: proc(q: Quaternion) -> (roll, pitch, yaw: Float) {
|
||||
// roll, x-axis rotation
|
||||
sinr_cosp: Float = 2 * (q.w * q.x + q.y * q.z);
|
||||
cosr_cosp: Float = 1 - 2 * (q.x * q.x + q.y * q.y);
|
||||
roll = math.atan2(sinr_cosp, cosr_cosp);
|
||||
|
||||
// pitch, y-axis rotation
|
||||
sinp: Float = 2 * (q.w * q.y - q.z * q.x);
|
||||
if abs(sinp) >= 1 {
|
||||
pitch = math.copy_sign(math.TAU * 0.25, sinp);
|
||||
} else {
|
||||
pitch = 2 * math.asin(sinp);
|
||||
}
|
||||
|
||||
// yaw, z-axis rotation
|
||||
siny_cosp: Float = 2 * (q.w * q.z + q.x * q.y);
|
||||
cosy_cosp: Float = 1 - 2 * (q.y * q.y + q.z * q.z);
|
||||
yaw = math.atan2(siny_cosp, cosy_cosp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_from_forward_and_up :: proc(forward, up: Vector3) -> Quaternion {
|
||||
f := normalize(forward);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
m := Matrix3{
|
||||
{+s.x, +u.x, -f.x},
|
||||
{+s.y, +u.y, -f.y},
|
||||
{+s.z, +u.z, -f.z},
|
||||
};
|
||||
|
||||
tr := trace(m);
|
||||
|
||||
q: Quaternion;
|
||||
|
||||
switch {
|
||||
case tr > 0:
|
||||
S := 2 * math.sqrt(1 + tr);
|
||||
q.w = 0.25 * S;
|
||||
q.x = (m[2][1] - m[1][2]) / S;
|
||||
q.y = (m[0][2] - m[2][0]) / S;
|
||||
q.z = (m[1][0] - m[0][1]) / S;
|
||||
case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
|
||||
S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2]);
|
||||
q.w = (m[2][1] - m[1][2]) / S;
|
||||
q.x = 0.25 * S;
|
||||
q.y = (m[0][1] + m[1][0]) / S;
|
||||
q.z = (m[0][2] + m[2][0]) / S;
|
||||
case m[1][1] > m[2][2]:
|
||||
S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2]);
|
||||
q.w = (m[0][2] - m[2][0]) / S;
|
||||
q.x = (m[0][1] + m[1][0]) / S;
|
||||
q.y = 0.25 * S;
|
||||
q.z = (m[1][2] + m[2][1]) / S;
|
||||
case:
|
||||
S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1]);
|
||||
q.w = (m[1][0] - m[0][1]) / S;
|
||||
q.x = (m[0][2] - m[2][0]) / S;
|
||||
q.y = (m[1][2] + m[2][1]) / S;
|
||||
q.z = 0.25 * S;
|
||||
}
|
||||
|
||||
return normalize(q);
|
||||
}
|
||||
|
||||
quaternion_look_at :: proc(eye, centre: Vector3, up: Vector3) -> Quaternion {
|
||||
return quaternion_from_forward_and_up(centre-eye, up);
|
||||
}
|
||||
|
||||
|
||||
quaternion_nlerp :: proc(a, b: Quaternion, t: Float) -> Quaternion {
|
||||
c := a + (b-a)*quaternion(t, 0, 0, 0);
|
||||
return normalize(c);
|
||||
}
|
||||
|
||||
|
||||
quaternion_slerp :: proc(x, y: Quaternion, t: Float) -> Quaternion {
|
||||
|
||||
a, b := x, y;
|
||||
cos_angle := dot(a, b);
|
||||
if cos_angle < 0 {
|
||||
b = -b;
|
||||
cos_angle = -cos_angle;
|
||||
}
|
||||
if cos_angle > 1 - FLOAT_EPSILON {
|
||||
return a + (b-a)*quaternion(t, 0, 0, 0);
|
||||
}
|
||||
|
||||
angle := math.acos(cos_angle);
|
||||
sin_angle := math.sin(angle);
|
||||
factor_a, factor_b: Quaternion;
|
||||
factor_a = quaternion(math.sin((1-t) * angle) / sin_angle, 0, 0, 0);
|
||||
factor_b = quaternion(math.sin(t * angle) / sin_angle, 0, 0, 0);
|
||||
|
||||
return factor_a * a + factor_b * b;
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_matrix4 :: proc(m: Matrix4) -> Quaternion {
|
||||
four_x_squared_minus_1, four_y_squared_minus_1,
|
||||
four_z_squared_minus_1, four_w_squared_minus_1,
|
||||
four_biggest_squared_minus_1: Float;
|
||||
|
||||
/* xyzw */
|
||||
/* 0123 */
|
||||
biggest_index := 3;
|
||||
biggest_value, mult: Float;
|
||||
|
||||
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
|
||||
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
|
||||
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
|
||||
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
four_biggest_squared_minus_1 = four_w_squared_minus_1;
|
||||
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_x_squared_minus_1;
|
||||
biggest_index = 0;
|
||||
}
|
||||
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_y_squared_minus_1;
|
||||
biggest_index = 1;
|
||||
}
|
||||
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_z_squared_minus_1;
|
||||
biggest_index = 2;
|
||||
}
|
||||
|
||||
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
|
||||
mult = 0.25 / biggest_value;
|
||||
|
||||
|
||||
switch biggest_index {
|
||||
case 0:
|
||||
return quaternion(
|
||||
biggest_value,
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
);
|
||||
case 1:
|
||||
return quaternion(
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
biggest_value,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
);
|
||||
case 2:
|
||||
return quaternion(
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
biggest_value,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
);
|
||||
case 3:
|
||||
return quaternion(
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
biggest_value,
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_matrix3 :: proc(m: Matrix3) -> Quaternion {
|
||||
four_x_squared_minus_1, four_y_squared_minus_1,
|
||||
four_z_squared_minus_1, four_w_squared_minus_1,
|
||||
four_biggest_squared_minus_1: Float;
|
||||
|
||||
/* xyzw */
|
||||
/* 0123 */
|
||||
biggest_index := 3;
|
||||
biggest_value, mult: Float;
|
||||
|
||||
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
|
||||
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
|
||||
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
|
||||
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
four_biggest_squared_minus_1 = four_w_squared_minus_1;
|
||||
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_x_squared_minus_1;
|
||||
biggest_index = 0;
|
||||
}
|
||||
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_y_squared_minus_1;
|
||||
biggest_index = 1;
|
||||
}
|
||||
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_z_squared_minus_1;
|
||||
biggest_index = 2;
|
||||
}
|
||||
|
||||
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
|
||||
mult = 0.25 / biggest_value;
|
||||
|
||||
|
||||
switch biggest_index {
|
||||
case 0:
|
||||
return quaternion(
|
||||
biggest_value,
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
);
|
||||
case 1:
|
||||
return quaternion(
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
biggest_value,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
);
|
||||
case 2:
|
||||
return quaternion(
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
biggest_value,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
);
|
||||
case 3:
|
||||
return quaternion(
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
biggest_value,
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
quaternion_between_two_vector3 :: proc(from, to: Vector3) -> Quaternion {
|
||||
x := normalize(from);
|
||||
y := normalize(to);
|
||||
|
||||
cos_theta := dot(x, y);
|
||||
if abs(cos_theta + 1) < 2*FLOAT_EPSILON {
|
||||
v := vector3_orthogonal(x);
|
||||
return quaternion(0, v.x, v.y, v.z);
|
||||
}
|
||||
v := cross(x, y);
|
||||
w := cos_theta + 1;
|
||||
return Quaternion(normalize(quaternion(w, v.x, v.y, v.z)));
|
||||
}
|
||||
|
||||
|
||||
matrix2_inverse_transpose :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
id := 1.0/d;
|
||||
c[0][0] = +m[1][1] * id;
|
||||
c[0][1] = -m[0][1] * id;
|
||||
c[1][0] = -m[1][0] * id;
|
||||
c[1][1] = +m[0][0] * id;
|
||||
return c;
|
||||
}
|
||||
matrix2_determinant :: proc(m: Matrix2) -> Float {
|
||||
return m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
}
|
||||
matrix2_inverse :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
id := 1.0/d;
|
||||
c[0][0] = +m[1][1] * id;
|
||||
c[1][0] = -m[0][1] * id;
|
||||
c[0][1] = -m[1][0] * id;
|
||||
c[1][1] = +m[0][0] * id;
|
||||
return c;
|
||||
}
|
||||
|
||||
matrix2_adjoint :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
c[0][0] = +m[1][1];
|
||||
c[0][1] = -m[1][0];
|
||||
c[1][0] = -m[0][1];
|
||||
c[1][1] = +m[0][0];
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
matrix3_from_quaternion :: proc(q: Quaternion) -> Matrix3 {
|
||||
xx := q.x * q.x;
|
||||
xy := q.x * q.y;
|
||||
xz := q.x * q.z;
|
||||
xw := q.x * q.w;
|
||||
yy := q.y * q.y;
|
||||
yz := q.y * q.z;
|
||||
yw := q.y * q.w;
|
||||
zz := q.z * q.z;
|
||||
zw := q.z * q.w;
|
||||
|
||||
m: Matrix3;
|
||||
m[0][0] = 1 - 2 * (yy + zz);
|
||||
m[1][0] = 2 * (xy - zw);
|
||||
m[2][0] = 2 * (xz + yw);
|
||||
|
||||
m[0][1] = 2 * (xy + zw);
|
||||
m[1][1] = 1 - 2 * (xx + zz);
|
||||
m[2][1] = 2 * (yz - xw);
|
||||
|
||||
m[0][2] = 2 * (xz - yw);
|
||||
m[1][2] = 2 * (yz + xw);
|
||||
m[2][2] = 1 - 2 * (xx + yy);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix3_inverse :: proc(m: Matrix3) -> Matrix3 {
|
||||
return transpose(matrix3_inverse_transpose(m));
|
||||
}
|
||||
|
||||
|
||||
matrix3_determinant :: proc(m: Matrix3) -> Float {
|
||||
a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]);
|
||||
b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]);
|
||||
c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
matrix3_adjoint :: proc(m: Matrix3) -> Matrix3 {
|
||||
adjoint: Matrix3;
|
||||
adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1]);
|
||||
adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]);
|
||||
adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1]);
|
||||
adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]);
|
||||
adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0]);
|
||||
adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]);
|
||||
adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0]);
|
||||
adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]);
|
||||
adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0]);
|
||||
return adjoint;
|
||||
}
|
||||
|
||||
matrix3_inverse_transpose :: proc(m: Matrix3) -> Matrix3 {
|
||||
inverse_transpose: Matrix3;
|
||||
|
||||
adjoint := matrix3_adjoint(m);
|
||||
determinant := matrix3_determinant(m);
|
||||
inv_determinant := 1.0 / determinant;
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
|
||||
}
|
||||
}
|
||||
return inverse_transpose;
|
||||
}
|
||||
|
||||
|
||||
matrix3_scale :: proc(s: Vector3) -> Matrix3 {
|
||||
m: Matrix3;
|
||||
m[0][0] = s[0];
|
||||
m[1][1] = s[1];
|
||||
m[2][2] = s[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix3_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix3 {
|
||||
c := math.cos(angle_radians);
|
||||
s := math.sin(angle_radians);
|
||||
|
||||
a := normalize(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot: Matrix3 = ---;
|
||||
|
||||
rot[0][0] = c + t[0]*a[0];
|
||||
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
|
||||
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
|
||||
|
||||
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
|
||||
rot[1][1] = c + t[1]*a[1];
|
||||
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
|
||||
|
||||
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
|
||||
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
|
||||
rot[2][2] = c + t[2]*a[2];
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
matrix3_look_at :: proc(eye, centre, up: Vector3) -> Matrix3 {
|
||||
f := normalize(centre - eye);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
return Matrix3{
|
||||
{+s.x, +u.x, -f.x},
|
||||
{+s.y, +u.y, -f.y},
|
||||
{+s.z, +u.z, -f.z},
|
||||
};
|
||||
}
|
||||
|
||||
matrix4_from_quaternion :: proc(q: Quaternion) -> Matrix4 {
|
||||
m := identity(Matrix4);
|
||||
|
||||
xx := q.x * q.x;
|
||||
xy := q.x * q.y;
|
||||
xz := q.x * q.z;
|
||||
xw := q.x * q.w;
|
||||
yy := q.y * q.y;
|
||||
yz := q.y * q.z;
|
||||
yw := q.y * q.w;
|
||||
zz := q.z * q.z;
|
||||
zw := q.z * q.w;
|
||||
|
||||
m[0][0] = 1 - 2 * (yy + zz);
|
||||
m[1][0] = 2 * (xy - zw);
|
||||
m[2][0] = 2 * (xz + yw);
|
||||
|
||||
m[0][1] = 2 * (xy + zw);
|
||||
m[1][1] = 1 - 2 * (xx + zz);
|
||||
m[2][1] = 2 * (yz - xw);
|
||||
|
||||
m[0][2] = 2 * (xz - yw);
|
||||
m[1][2] = 2 * (yz + xw);
|
||||
m[2][2] = 1 - 2 * (xx + yy);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix4_from_trs :: proc(t: Vector3, r: Quaternion, s: Vector3) -> Matrix4 {
|
||||
translation := matrix4_translate(t);
|
||||
rotation := matrix4_from_quaternion(r);
|
||||
scale := matrix4_scale(s);
|
||||
return mul(translation, mul(rotation, scale));
|
||||
}
|
||||
|
||||
|
||||
matrix4_inverse :: proc(m: Matrix4) -> Matrix4 {
|
||||
return transpose(matrix4_inverse_transpose(m));
|
||||
}
|
||||
|
||||
|
||||
matrix4_minor :: proc(m: Matrix4, c, r: int) -> Float {
|
||||
cut_down: Matrix3;
|
||||
for i in 0..<3 {
|
||||
col := i if i < c else i+1;
|
||||
for j in 0..<3 {
|
||||
row := j if j < r else j+1;
|
||||
cut_down[i][j] = m[col][row];
|
||||
}
|
||||
}
|
||||
return matrix3_determinant(cut_down);
|
||||
}
|
||||
|
||||
matrix4_cofactor :: proc(m: Matrix4, c, r: int) -> Float {
|
||||
sign, minor: Float;
|
||||
sign = 1 if (c + r) % 2 == 0 else -1;
|
||||
minor = matrix4_minor(m, c, r);
|
||||
return sign * minor;
|
||||
}
|
||||
|
||||
matrix4_adjoint :: proc(m: Matrix4) -> Matrix4 {
|
||||
adjoint: Matrix4;
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
adjoint[i][j] = matrix4_cofactor(m, i, j);
|
||||
}
|
||||
}
|
||||
return adjoint;
|
||||
}
|
||||
|
||||
matrix4_determinant :: proc(m: Matrix4) -> Float {
|
||||
adjoint := matrix4_adjoint(m);
|
||||
determinant: Float = 0;
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0];
|
||||
}
|
||||
return determinant;
|
||||
|
||||
}
|
||||
|
||||
matrix4_inverse_transpose :: proc(m: Matrix4) -> Matrix4 {
|
||||
adjoint := matrix4_adjoint(m);
|
||||
determinant: Float = 0;
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0];
|
||||
}
|
||||
inv_determinant := 1.0 / determinant;
|
||||
inverse_transpose: Matrix4;
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
|
||||
}
|
||||
}
|
||||
return inverse_transpose;
|
||||
}
|
||||
|
||||
matrix4_translate :: proc(v: Vector3) -> Matrix4 {
|
||||
m := identity(Matrix4);
|
||||
m[3][0] = v[0];
|
||||
m[3][1] = v[1];
|
||||
m[3][2] = v[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
matrix4_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix4 {
|
||||
c := math.cos(angle_radians);
|
||||
s := math.sin(angle_radians);
|
||||
|
||||
a := normalize(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot := identity(Matrix4);
|
||||
|
||||
rot[0][0] = c + t[0]*a[0];
|
||||
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
|
||||
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
|
||||
rot[1][1] = c + t[1]*a[1];
|
||||
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
|
||||
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
|
||||
rot[2][2] = c + t[2]*a[2];
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
matrix4_scale :: proc(v: Vector3) -> Matrix4 {
|
||||
m: Matrix4;
|
||||
m[0][0] = v[0];
|
||||
m[1][1] = v[1];
|
||||
m[2][2] = v[2];
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix4_look_at :: proc(eye, centre, up: Vector3) -> Matrix4 {
|
||||
f := normalize(centre - eye);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
return Matrix4{
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), +dot(f, eye), 1},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
matrix4_perspective :: proc(fovy, aspect, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
tan_half_fovy := math.tan(0.5 * fovy);
|
||||
m[0][0] = 1 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1 / (tan_half_fovy);
|
||||
m[2][2] = +(far + near) / (far - near);
|
||||
m[2][3] = +1;
|
||||
m[3][2] = -2*far*near / (far - near);
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix_ortho3d :: proc(left, right, bottom, top, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
m[0][0] = +2 / (right - left);
|
||||
m[1][1] = +2 / (top - bottom);
|
||||
m[2][2] = +2 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far- near);
|
||||
m[3][3] = 1;
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix4_infinite_perspective :: proc(fovy, aspect, near: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
tan_half_fovy := math.tan(0.5 * fovy);
|
||||
m[0][0] = 1 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1 / (tan_half_fovy);
|
||||
m[2][2] = +1;
|
||||
m[2][3] = +1;
|
||||
m[3][2] = -2*near;
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,821 @@
|
||||
package math
|
||||
|
||||
import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
Float_Class :: enum {
|
||||
Normal, // an ordinary nonzero floating point value
|
||||
Subnormal, // a subnormal floating point value
|
||||
Zero, // zero
|
||||
Neg_Zero, // the negative zero
|
||||
NaN, // Not-A-Number (NaN)
|
||||
Inf, // positive infinity
|
||||
Neg_Inf // negative infinity
|
||||
};
|
||||
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
e :: E;
|
||||
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LN2 :: 0.693147180559945309417232121458176568;
|
||||
LN10 :: 2.30258509299404568401799145468436421;
|
||||
|
||||
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
|
||||
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
|
||||
|
||||
RAD_PER_DEG :: TAU/360.0;
|
||||
DEG_PER_RAD :: 360.0/TAU;
|
||||
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@(link_name="llvm.sqrt.f32")
|
||||
sqrt_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.sqrt.f64")
|
||||
sqrt_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.sin.f32")
|
||||
sin_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.sin.f64")
|
||||
sin_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.cos.f32")
|
||||
cos_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.cos.f64")
|
||||
cos_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.pow.f32")
|
||||
pow_f32 :: proc(x, power: f32) -> f32 ---;
|
||||
@(link_name="llvm.pow.f64")
|
||||
pow_f64 :: proc(x, power: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.fmuladd.f32")
|
||||
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
|
||||
@(link_name="llvm.fmuladd.f64")
|
||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.log.f32")
|
||||
ln_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.log.f64")
|
||||
ln_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.exp.f32")
|
||||
exp_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.exp.f64")
|
||||
exp_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.ldexp.f32")
|
||||
ldexp_f32 :: proc(val: f32, exp: i32) -> f32 ---;
|
||||
|
||||
@(link_name="llvm.ldexp.f64")
|
||||
ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---;
|
||||
}
|
||||
|
||||
sqrt :: proc{sqrt_f32, sqrt_f64};
|
||||
sin :: proc{sin_f32, sin_f64};
|
||||
cos :: proc{cos_f32, cos_f64};
|
||||
pow :: proc{pow_f32, pow_f64};
|
||||
fmuladd :: proc{fmuladd_f32, fmuladd_f64};
|
||||
ln :: proc{ln_f32, ln_f64};
|
||||
exp :: proc{exp_f32, exp_f64};
|
||||
|
||||
ldexp :: proc{ldexp_f32, ldexp_f64};
|
||||
|
||||
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
|
||||
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
|
||||
log :: proc{log_f32, log_f64};
|
||||
|
||||
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
|
||||
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
|
||||
log2 :: proc{log2_f32, log2_f64};
|
||||
|
||||
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
|
||||
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
|
||||
log10 :: proc{log10_f32, log10_f64};
|
||||
|
||||
|
||||
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
tan :: proc{tan_f32, tan_f64};
|
||||
|
||||
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
|
||||
|
||||
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
|
||||
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
|
||||
unlerp :: proc{unlerp_f32, unlerp_f64};
|
||||
|
||||
|
||||
wrap :: proc(x, y: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
tmp := mod(x, y);
|
||||
return y + tmp if tmp < 0 else tmp;
|
||||
}
|
||||
angle_diff :: proc(a, b: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
|
||||
dist := wrap(b - a, TAU);
|
||||
return wrap(dist*2, TAU) - dist;
|
||||
}
|
||||
|
||||
angle_lerp :: proc(a, b, t: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
return a + angle_diff(a, b) * t;
|
||||
}
|
||||
|
||||
step :: proc(edge, x: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
return 0 if x < edge else 1;
|
||||
}
|
||||
|
||||
smoothstep :: proc(edge0, edge1, x: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
t := clamp((x - edge0) / (edge1 - edge0), 0, 1);
|
||||
return t * t * (3 - 2*t);
|
||||
}
|
||||
|
||||
bias :: proc(t, b: $T) -> T where intrinsics.type_is_numeric(T) {
|
||||
return t / (((1/b) - 2) * (1 - t) + 1);
|
||||
}
|
||||
gain :: proc(t, g: $T) -> T where intrinsics.type_is_numeric(T) {
|
||||
if t < 0.5 {
|
||||
return bias(t*2, g)*0.5;
|
||||
}
|
||||
return bias(t*2 - 1, 1 - g)*0.5 + 0.5;
|
||||
}
|
||||
|
||||
|
||||
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
|
||||
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
|
||||
sign :: proc{sign_f32, sign_f64};
|
||||
|
||||
sign_bit_f32 :: proc(x: f32) -> bool {
|
||||
return (transmute(u32)x) & (1<<31) != 0;
|
||||
}
|
||||
sign_bit_f64 :: proc(x: f64) -> bool {
|
||||
return (transmute(u64)x) & (1<<63) != 0;
|
||||
}
|
||||
sign_bit :: proc{sign_bit_f32, sign_bit_f64};
|
||||
|
||||
copy_sign_f32 :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
copy_sign_f64 :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64)x;
|
||||
iy := transmute(u64)y;
|
||||
ix &= 0x7fff_ffff_ffff_ffff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64)ix;
|
||||
}
|
||||
copy_sign :: proc{copy_sign_f32, copy_sign_f64};
|
||||
|
||||
|
||||
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
|
||||
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
|
||||
to_radians :: proc{to_radians_f32, to_radians_f64};
|
||||
to_degrees :: proc{to_degrees_f32, to_degrees_f64};
|
||||
|
||||
trunc_f32 :: proc(x: f32) -> f32 {
|
||||
trunc_internal :: proc(f: f32) -> f32 {
|
||||
mask :: 0xff;
|
||||
shift :: 32 - 9;
|
||||
bias :: 0x7f;
|
||||
|
||||
if f < 1 {
|
||||
switch {
|
||||
case f < 0: return -trunc_internal(-f);
|
||||
case f == 0: return f;
|
||||
case: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
x := transmute(u32)f;
|
||||
e := (x >> shift) & mask - bias;
|
||||
|
||||
if e < shift {
|
||||
x &= ~(1 << (shift-e)) - 1;
|
||||
}
|
||||
return transmute(f32)x;
|
||||
}
|
||||
switch classify(x) {
|
||||
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
|
||||
return x;
|
||||
case .Normal, .Subnormal: // carry on
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
|
||||
trunc_f64 :: proc(x: f64) -> f64 {
|
||||
trunc_internal :: proc(f: f64) -> f64 {
|
||||
mask :: 0x7ff;
|
||||
shift :: 64 - 12;
|
||||
bias :: 0x3ff;
|
||||
|
||||
if f < 1 {
|
||||
switch {
|
||||
case f < 0: return -trunc_internal(-f);
|
||||
case f == 0: return f;
|
||||
case: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
x := transmute(u64)f;
|
||||
e := (x >> shift) & mask - bias;
|
||||
|
||||
if e < shift {
|
||||
x &= ~(1 << (shift-e)) - 1;
|
||||
}
|
||||
return transmute(f64)x;
|
||||
}
|
||||
switch classify(x) {
|
||||
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
|
||||
return x;
|
||||
case .Normal, .Subnormal: // carry on
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
|
||||
trunc :: proc{trunc_f32, trunc_f64};
|
||||
|
||||
round_f32 :: proc(x: f32) -> f32 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64 :: proc(x: f64) -> f64 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round :: proc{round_f32, round_f64};
|
||||
|
||||
|
||||
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
|
||||
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
|
||||
ceil :: proc{ceil_f32, ceil_f64};
|
||||
|
||||
floor_f32 :: proc(x: f32) -> f32 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
if x < 0 {
|
||||
d, fract := modf(-x);
|
||||
if fract != 0.0 {
|
||||
d = d + 1;
|
||||
}
|
||||
return -d;
|
||||
}
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor_f64 :: proc(x: f64) -> f64 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
if x < 0 {
|
||||
d, fract := modf(-x);
|
||||
if fract != 0.0 {
|
||||
d = d + 1;
|
||||
}
|
||||
return -d;
|
||||
}
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor :: proc{floor_f32, floor_f64};
|
||||
|
||||
|
||||
floor_div :: proc(x, y: $T) -> T
|
||||
where intrinsics.type_is_integer(T) {
|
||||
a := x / y;
|
||||
r := x % y;
|
||||
if (r > 0 && y < 0) || (r < 0 && y > 0) {
|
||||
a -= 1;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
floor_mod :: proc(x, y: $T) -> T
|
||||
where intrinsics.type_is_integer(T) {
|
||||
r := x % y;
|
||||
if (r > 0 && y < 0) || (r < 0 && y > 0) {
|
||||
r += y;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
|
||||
shift :: 32 - 8 - 1;
|
||||
mask :: 0xff;
|
||||
bias :: 127;
|
||||
|
||||
if x < 1 {
|
||||
switch {
|
||||
case x < 0:
|
||||
int, frac = modf(-x);
|
||||
return -int, -frac;
|
||||
case x == 0:
|
||||
return x, x;
|
||||
}
|
||||
return 0, x;
|
||||
}
|
||||
|
||||
i := transmute(u32)x;
|
||||
e := uint(i>>shift)&mask - bias;
|
||||
|
||||
if e < shift {
|
||||
i &~= 1<<(shift-e) - 1;
|
||||
}
|
||||
int = transmute(f32)i;
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
|
||||
shift :: 64 - 11 - 1;
|
||||
mask :: 0x7ff;
|
||||
bias :: 1023;
|
||||
|
||||
if x < 1 {
|
||||
switch {
|
||||
case x < 0:
|
||||
int, frac = modf(-x);
|
||||
return -int, -frac;
|
||||
case x == 0:
|
||||
return x, x;
|
||||
}
|
||||
return 0, x;
|
||||
}
|
||||
|
||||
i := transmute(u64)x;
|
||||
e := uint(i>>shift)&mask - bias;
|
||||
|
||||
if e < shift {
|
||||
i &~= 1<<(shift-e) - 1;
|
||||
}
|
||||
int = transmute(f64)i;
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf :: proc{modf_f32, modf_f64};
|
||||
split_decimal :: modf;
|
||||
|
||||
mod_f32 :: proc(x, y: f32) -> (n: f32) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
n += z;
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod_f64 :: proc(x, y: f64) -> (n: f64) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
n += z;
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod :: proc{mod_f32, mod_f64};
|
||||
|
||||
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
remainder :: proc{remainder_f32, remainder_f64};
|
||||
|
||||
|
||||
|
||||
gcd :: proc(x, y: $T) -> T
|
||||
where intrinsics.type_is_ordered_numeric(T) {
|
||||
x, y := x, y;
|
||||
for y != 0 {
|
||||
x %= y;
|
||||
x, y = y, x;
|
||||
}
|
||||
return abs(x);
|
||||
}
|
||||
|
||||
lcm :: proc(x, y: $T) -> T
|
||||
where intrinsics.type_is_ordered_numeric(T) {
|
||||
return x / gcd(x, y) * y;
|
||||
}
|
||||
|
||||
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
|
||||
switch {
|
||||
case x == 0:
|
||||
return 0, 0;
|
||||
case x < 0:
|
||||
significand, exponent = frexp(-x);
|
||||
return -significand, exponent;
|
||||
}
|
||||
ex := trunc(log2(x));
|
||||
exponent = int(ex);
|
||||
significand = x / pow(2.0, ex);
|
||||
if abs(significand) >= 1 {
|
||||
exponent += 1;
|
||||
significand /= 2;
|
||||
}
|
||||
if exponent == 1024 && significand == 0 {
|
||||
significand = 0.99999999999999988898;
|
||||
}
|
||||
return;
|
||||
}
|
||||
frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
|
||||
switch {
|
||||
case x == 0:
|
||||
return 0, 0;
|
||||
case x < 0:
|
||||
significand, exponent = frexp(-x);
|
||||
return -significand, exponent;
|
||||
}
|
||||
ex := trunc(log2(x));
|
||||
exponent = int(ex);
|
||||
significand = x / pow(2.0, ex);
|
||||
if abs(significand) >= 1 {
|
||||
exponent += 1;
|
||||
significand /= 2;
|
||||
}
|
||||
if exponent == 1024 && significand == 0 {
|
||||
significand = 0.99999999999999988898;
|
||||
}
|
||||
return;
|
||||
}
|
||||
frexp :: proc{frexp_f32, frexp_f64};
|
||||
|
||||
|
||||
|
||||
|
||||
binomial :: proc(n, k: int) -> int {
|
||||
switch {
|
||||
case k <= 0: return 1;
|
||||
case 2*k > n: return binomial(n, n-k);
|
||||
}
|
||||
|
||||
b := n;
|
||||
for i in 2..<k {
|
||||
b = (b * (n+1-i))/i;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
factorial :: proc(n: int) -> int {
|
||||
when size_of(int) == size_of(i64) {
|
||||
@static table := [21]int{
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
6,
|
||||
24,
|
||||
120,
|
||||
720,
|
||||
5_040,
|
||||
40_320,
|
||||
362_880,
|
||||
3_628_800,
|
||||
39_916_800,
|
||||
479_001_600,
|
||||
6_227_020_800,
|
||||
87_178_291_200,
|
||||
1_307_674_368_000,
|
||||
20_922_789_888_000,
|
||||
355_687_428_096_000,
|
||||
6_402_373_705_728_000,
|
||||
121_645_100_408_832_000,
|
||||
2_432_902_008_176_640_000,
|
||||
};
|
||||
} else {
|
||||
@static table := [13]int{
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
6,
|
||||
24,
|
||||
120,
|
||||
720,
|
||||
5_040,
|
||||
40_320,
|
||||
362_880,
|
||||
3_628_800,
|
||||
39_916_800,
|
||||
479_001_600,
|
||||
};
|
||||
}
|
||||
|
||||
assert(n >= 0, "parameter must not be negative");
|
||||
assert(n < len(table), "parameter is too large to lookup in the table");
|
||||
return 0;
|
||||
}
|
||||
|
||||
classify_f32 :: proc(x: f32) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i32)x;
|
||||
if i < 0 {
|
||||
return .Neg_Zero;
|
||||
}
|
||||
return .Zero;
|
||||
case x*0.5 == x:
|
||||
if x < 0 {
|
||||
return .Neg_Inf;
|
||||
}
|
||||
return .Inf;
|
||||
case !(x == x):
|
||||
return .NaN;
|
||||
}
|
||||
|
||||
u := transmute(u32)x;
|
||||
exp := int(u>>23) & (1<<8 - 1);
|
||||
if exp == 0 {
|
||||
return .Subnormal;
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify_f64 :: proc(x: f64) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i64)x;
|
||||
if i < 0 {
|
||||
return .Neg_Zero;
|
||||
}
|
||||
return .Zero;
|
||||
case x*0.5 == x:
|
||||
if x < 0 {
|
||||
return .Neg_Inf;
|
||||
}
|
||||
return .Inf;
|
||||
case !(x == x):
|
||||
return .NaN;
|
||||
}
|
||||
u := transmute(u64)x;
|
||||
exp := int(u>>52) & (1<<11 - 1);
|
||||
if exp == 0 {
|
||||
return .Subnormal;
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify :: proc{classify_f32, classify_f64};
|
||||
|
||||
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
|
||||
is_nan :: proc{is_nan_f32, is_nan_f64};
|
||||
|
||||
|
||||
// is_inf reports whether f is an infinity, according to sign.
|
||||
// If sign > 0, is_inf reports whether f is positive infinity.
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f32 :: proc(x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf;
|
||||
case sign < 0:
|
||||
return class == .Neg_Inf;
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf_f64 :: proc(x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf;
|
||||
case sign < 0:
|
||||
return class == .Neg_Inf;
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf :: proc{is_inf_f32, is_inf_f64};
|
||||
|
||||
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
return x > 0 && (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
next_power_of_two :: proc(x: int) -> int {
|
||||
k := x -1;
|
||||
when size_of(int) == 8 {
|
||||
k = k | (k >> 32);
|
||||
}
|
||||
k = k | (k >> 16);
|
||||
k = k | (k >> 8);
|
||||
k = k | (k >> 4);
|
||||
k = k | (k >> 2);
|
||||
k = k | (k >> 1);
|
||||
k += 1 + int(x <= 0);
|
||||
return k;
|
||||
}
|
||||
|
||||
sum :: proc(x: $T/[]$E) -> (res: E)
|
||||
where intrinsics.type_is_numeric(E) {
|
||||
for i in x {
|
||||
res += i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
prod :: proc(x: $T/[]$E) -> (res: E)
|
||||
where intrinsics.type_is_numeric(E) {
|
||||
for i in x {
|
||||
res *= i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cumsum_inplace :: proc(x: $T/[]$E) -> T
|
||||
where intrinsics.type_is_numeric(E) {
|
||||
for i in 1..<len(x) {
|
||||
x[i] = x[i-1] + x[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cumsum :: proc(dst, src: $T/[]$E) -> T
|
||||
where intrinsics.type_is_numeric(E) {
|
||||
N := min(len(dst), len(src));
|
||||
if N > 0 {
|
||||
dst[0] = src[0];
|
||||
for i in 1..<N {
|
||||
dst[i] = dst[i-1] + src[i];
|
||||
}
|
||||
}
|
||||
return dst[:N];
|
||||
}
|
||||
|
||||
|
||||
|
||||
atan2_f32 :: proc(y, x: f32) -> f32 {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f32(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
|
||||
atan2_f64 :: proc(y, x: f64) -> f64 {
|
||||
// TODO(bill): Faster atan2_f64 if possible
|
||||
|
||||
// The original C code:
|
||||
// Stephen L. Moshier
|
||||
// moshier@na-net.ornl.gov
|
||||
|
||||
NAN :: 0h7fff_ffff_ffff_ffff;
|
||||
INF :: 0h7FF0_0000_0000_0000;
|
||||
PI :: 0h4009_21fb_5444_2d18;
|
||||
|
||||
atan :: proc(x: f64) -> f64 {
|
||||
if x == 0 {
|
||||
return x;
|
||||
}
|
||||
if x > 0 {
|
||||
return s_atan(x);
|
||||
}
|
||||
return -s_atan(-x);
|
||||
}
|
||||
// s_atan reduces its argument (known to be positive) to the range [0, 0.66] and calls x_atan.
|
||||
s_atan :: proc(x: f64) -> f64 {
|
||||
MORE_BITS :: 6.123233995736765886130e-17; // pi/2 = PIO2 + MORE_BITS
|
||||
TAN3PI08 :: 2.41421356237309504880; // tan(3*pi/8)
|
||||
if x <= 0.66 {
|
||||
return x_atan(x);
|
||||
}
|
||||
if x > TAN3PI08 {
|
||||
return PI/2 - x_atan(1/x) + MORE_BITS;
|
||||
}
|
||||
return PI/4 + x_atan((x-1)/(x+1)) + 0.5*MORE_BITS;
|
||||
}
|
||||
// x_atan evaluates a series valid in the range [0, 0.66].
|
||||
x_atan :: proc(x: f64) -> f64 {
|
||||
P0 :: -8.750608600031904122785e-01;
|
||||
P1 :: -1.615753718733365076637e+01;
|
||||
P2 :: -7.500855792314704667340e+01;
|
||||
P3 :: -1.228866684490136173410e+02;
|
||||
P4 :: -6.485021904942025371773e+01;
|
||||
Q0 :: +2.485846490142306297962e+01;
|
||||
Q1 :: +1.650270098316988542046e+02;
|
||||
Q2 :: +4.328810604912902668951e+02;
|
||||
Q3 :: +4.853903996359136964868e+02;
|
||||
Q4 :: +1.945506571482613964425e+02;
|
||||
|
||||
z := x * x;
|
||||
z = z * ((((P0*z+P1)*z+P2)*z+P3)*z + P4) / (((((z+Q0)*z+Q1)*z+Q2)*z+Q3)*z + Q4);
|
||||
z = x*z + x;
|
||||
return z;
|
||||
}
|
||||
|
||||
switch {
|
||||
case is_nan(y) || is_nan(x):
|
||||
return NAN;
|
||||
case y == 0:
|
||||
if x >= 0 && !sign_bit(x) {
|
||||
return copy_sign(0.0, y);
|
||||
}
|
||||
return copy_sign(PI, y);
|
||||
case x == 0:
|
||||
return copy_sign(PI*0.5, y);
|
||||
case is_inf(x, 0):
|
||||
if is_inf(x, 1) {
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.25, y);
|
||||
}
|
||||
return copy_sign(0, y);
|
||||
}
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.75, y);
|
||||
}
|
||||
return copy_sign(PI, y);
|
||||
case is_inf(y, 0):
|
||||
return copy_sign(PI*0.5, y);
|
||||
}
|
||||
|
||||
q := atan(y / x);
|
||||
if x < 0 {
|
||||
if q <= 0 {
|
||||
return q + PI;
|
||||
}
|
||||
return q - PI;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
atan2 :: proc{atan2_f32, atan2_f64};
|
||||
|
||||
atan_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1);
|
||||
}
|
||||
atan_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1);
|
||||
}
|
||||
atan :: proc{atan_f32, atan_f64};
|
||||
|
||||
asin_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1 + sqrt_f32(1 - x*x));
|
||||
}
|
||||
asin_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1 + sqrt_f64(1 - x*x));
|
||||
}
|
||||
asin :: proc{asin_f32, asin_f64};
|
||||
|
||||
acos_f32 :: proc(x: f32) -> f32 {
|
||||
return 2 * atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
|
||||
}
|
||||
acos_f64 :: proc(x: f64) -> f64 {
|
||||
return 2 * atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
|
||||
}
|
||||
acos :: proc{acos_f32, acos_f64};
|
||||
|
||||
|
||||
sinh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh :: proc{sinh_f32, sinh_f64};
|
||||
|
||||
cosh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh :: proc{cosh_f32, cosh_f64};
|
||||
|
||||
tanh_f32 :: proc(x: f32) -> f32 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh_f64 :: proc(x: f64) -> f64 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh :: proc{tanh_f32, tanh_f64};
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
@@ -0,0 +1,146 @@
|
||||
package rand
|
||||
|
||||
import "core:math"
|
||||
|
||||
//
|
||||
// Normal distribution
|
||||
//
|
||||
// "The Ziggurat Method for Generating Random Variables"
|
||||
// Authors: George Marsaglia, Wai Wan Tsang
|
||||
// Submitted: 2000-04-15. Published: 2000-10-02.
|
||||
// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
|
||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||
//
|
||||
|
||||
// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
|
||||
// with a standard normal distribution with a mean of 0 and standard deviation of 1.
|
||||
//
|
||||
// sample = norm_float64() * std_dev + mean
|
||||
//
|
||||
norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
rn :: 3.442619855899;
|
||||
|
||||
@(static)
|
||||
kn := [128]u32{
|
||||
0x76ad2212, 0x00000000, 0x600f1b53, 0x6ce447a6, 0x725b46a2,
|
||||
0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,
|
||||
0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7,
|
||||
0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883,
|
||||
0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30,
|
||||
0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa,
|
||||
0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d,
|
||||
0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18,
|
||||
0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924,
|
||||
0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a,
|
||||
0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4,
|
||||
0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62,
|
||||
0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e,
|
||||
0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473,
|
||||
0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd,
|
||||
0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568,
|
||||
0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08,
|
||||
0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc,
|
||||
0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94,
|
||||
0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb,
|
||||
0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075,
|
||||
0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba,
|
||||
0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded,
|
||||
0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72,
|
||||
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
|
||||
0x7ba90bdc, 0x7a722176, 0x77d664e5,
|
||||
};
|
||||
|
||||
@(static)
|
||||
wn := [128]f32{
|
||||
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
|
||||
2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,
|
||||
2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10,
|
||||
3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10,
|
||||
3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10,
|
||||
4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10,
|
||||
4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10,
|
||||
4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10,
|
||||
5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10,
|
||||
5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10,
|
||||
5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10,
|
||||
5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10,
|
||||
6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10,
|
||||
6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10,
|
||||
6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10,
|
||||
6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10,
|
||||
7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10,
|
||||
7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10,
|
||||
7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10,
|
||||
7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10,
|
||||
8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10,
|
||||
8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10,
|
||||
8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10,
|
||||
9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10,
|
||||
9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10,
|
||||
9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09,
|
||||
1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09,
|
||||
1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09,
|
||||
1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09,
|
||||
1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09,
|
||||
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
|
||||
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
|
||||
};
|
||||
|
||||
@(static)
|
||||
fn := [128]f32{
|
||||
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
|
||||
0.87324303, 0.8555006, 0.8387836, 0.8229072, 0.8077383,
|
||||
0.793177, 0.7791461, 0.7655842, 0.7524416, 0.73967725,
|
||||
0.7272569, 0.7151515, 0.7033361, 0.69178915, 0.68049186,
|
||||
0.6694277, 0.658582, 0.6479418, 0.63749546, 0.6272325,
|
||||
0.6171434, 0.6072195, 0.5974532, 0.58783704, 0.5783647,
|
||||
0.56903, 0.5598274, 0.5507518, 0.54179835, 0.5329627,
|
||||
0.52424055, 0.5156282, 0.50712204, 0.49871865, 0.49041483,
|
||||
0.48220766, 0.4740943, 0.46607214, 0.4581387, 0.45029163,
|
||||
0.44252872, 0.43484783, 0.427247, 0.41972435, 0.41227803,
|
||||
0.40490642, 0.39760786, 0.3903808, 0.3832238, 0.37613547,
|
||||
0.36911446, 0.3621595, 0.35526937, 0.34844297, 0.34167916,
|
||||
0.33497685, 0.3283351, 0.3217529, 0.3152294, 0.30876362,
|
||||
0.30235484, 0.29600215, 0.28970486, 0.2834622, 0.2772735,
|
||||
0.27113807, 0.2650553, 0.25902456, 0.2530453, 0.24711695,
|
||||
0.241239, 0.23541094, 0.22963232, 0.2239027, 0.21822165,
|
||||
0.21258877, 0.20700371, 0.20146611, 0.19597565, 0.19053204,
|
||||
0.18513499, 0.17978427, 0.17447963, 0.1692209, 0.16400786,
|
||||
0.15884037, 0.15371831, 0.14864157, 0.14361008, 0.13862377,
|
||||
0.13368265, 0.12878671, 0.12393598, 0.119130544, 0.11437051,
|
||||
0.10965602, 0.104987256, 0.10036444, 0.095787846, 0.0912578,
|
||||
0.08677467, 0.0823389, 0.077950984, 0.073611505, 0.06932112,
|
||||
0.06508058, 0.06089077, 0.056752663, 0.0526674, 0.048636295,
|
||||
0.044660863, 0.040742867, 0.03688439, 0.033087887, 0.029356318,
|
||||
0.025693292, 0.022103304, 0.018592102, 0.015167298, 0.011839478,
|
||||
0.008624485, 0.005548995, 0.0026696292,
|
||||
};
|
||||
|
||||
|
||||
for {
|
||||
j := i32(uint32(r));
|
||||
i := j & 0x7f;
|
||||
x := f64(j) * f64(wn[i]);
|
||||
if u32(abs(j)) < kn[i] {
|
||||
// 99% of the time this will be hit
|
||||
return x;
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
for {
|
||||
x = -math.ln(float64(r)) * (1.0/ rn);
|
||||
y := -math.ln(float64(r));
|
||||
if y+y >= x*x {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return j > 0 ? rn + x : -rn - x;
|
||||
}
|
||||
if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // NOTE(bill): Will never be hit but this is here for sanity's sake
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package rand
|
||||
|
||||
Rand :: struct {
|
||||
state: u64,
|
||||
inc: u64,
|
||||
}
|
||||
|
||||
|
||||
@(private, static)
|
||||
_GLOBAL_SEED_DATA := 1234567890;
|
||||
@(private, static)
|
||||
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
|
||||
@(private, static)
|
||||
global_rand_ptr := &global_rand;
|
||||
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
init(global_rand_ptr, seed);
|
||||
}
|
||||
|
||||
create :: proc(seed: u64) -> Rand {
|
||||
r: Rand;
|
||||
init(&r, seed);
|
||||
return r;
|
||||
}
|
||||
|
||||
init :: proc(r: ^Rand, seed: u64) {
|
||||
r.state = 0;
|
||||
r.inc = (seed << 1) | 1;
|
||||
_random(r);
|
||||
r.state += seed;
|
||||
_random(r);
|
||||
}
|
||||
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
old_state := r.state;
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1);
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
|
||||
rot := u32(old_state >> 59);
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
|
||||
|
||||
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
|
||||
a := u64(_random(r));
|
||||
b := u64(_random(r));
|
||||
return (a<<32) | b;
|
||||
}
|
||||
|
||||
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
a := u128(_random(r));
|
||||
b := u128(_random(r));
|
||||
c := u128(_random(r));
|
||||
d := u128(_random(r));
|
||||
return (a<<96) | (b<<64) | (c<<32) | d;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
|
||||
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
if n <= 0 do panic("Invalid argument to int31_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int31(r) & (n-1);
|
||||
}
|
||||
max := i32((1<<31) - 1 - (1<<31)&u32(n));
|
||||
v := int31(r);
|
||||
for v > max {
|
||||
v = int31(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int63(r) & (n-1);
|
||||
}
|
||||
max := i64((1<<63) - 1 - (1<<63)&u64(n));
|
||||
v := int63(r);
|
||||
for v > max {
|
||||
v = int63(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int127(r) & (n-1);
|
||||
}
|
||||
max := i128((1<<63) - 1 - (1<<63)&u128(n));
|
||||
v := int127(r);
|
||||
for v > max {
|
||||
v = int127(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
if n <= 0 do panic("Invalid argument to int_max");
|
||||
when size_of(int) == 4 {
|
||||
return int(int31_max(i32(n), r));
|
||||
} else {
|
||||
return int(int63_max(i64(n), r));
|
||||
}
|
||||
}
|
||||
|
||||
float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand = global_rand_ptr) -> f32 { return f32(float64(r)); }
|
||||
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = global_rand_ptr) -> f64 { return (hi-lo)*float64(r) + lo; }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = global_rand_ptr) -> f32 { return (hi-lo)*float32(r) + lo; }
|
||||
|
||||
|
||||
read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
|
||||
pos := i8(0);
|
||||
val := i64(0);
|
||||
for n = 0; n < len(p); n += 1 {
|
||||
if pos == 0 {
|
||||
val = int63(r);
|
||||
pos = 7;
|
||||
}
|
||||
p[n] = byte(val);
|
||||
val >>= 8;
|
||||
pos -= 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
|
||||
perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
|
||||
m := make([]int, n);
|
||||
for i := 0; i < n; i += 1 {
|
||||
j := int_max(i+1);
|
||||
m[i] = m[j];
|
||||
m[j] = i;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = global_rand_ptr) {
|
||||
n := i64(len(array));
|
||||
if n < 2 do return;
|
||||
|
||||
for i := i64(0); i < n; i += 1 {
|
||||
j := int63_max(n, r);
|
||||
array[i], array[j] = array[j], array[i];
|
||||
}
|
||||
}
|
||||
-305
@@ -1,305 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
|
||||
swap :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
|
||||
swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
|
||||
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
return __mem_set(data, value, len);
|
||||
}
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_zero(data, len);
|
||||
}
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy(dst, src, len);
|
||||
}
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: proc(a, b: []byte) -> int {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int #inline { return megabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false;
|
||||
}
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uint(align);
|
||||
p := uint(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 {
|
||||
p += a - modulo;
|
||||
}
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Allocation_Header :: struct {
|
||||
size: int,
|
||||
}
|
||||
|
||||
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := ^int(header+1);
|
||||
|
||||
for i := 0; rawptr(ptr) < data; i++ {
|
||||
(ptr+i)^ = -1;
|
||||
}
|
||||
}
|
||||
allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
|
||||
if data == nil {
|
||||
return nil;
|
||||
}
|
||||
p := ^int(data);
|
||||
for (p-1)^ == -1 {
|
||||
p = (p-1);
|
||||
}
|
||||
return ^Allocation_Header(p-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Custom allocators
|
||||
Arena :: struct {
|
||||
backing: Allocator,
|
||||
offset: int,
|
||||
memory: []byte,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena,
|
||||
original_count: int,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
||||
backing = Allocator{};
|
||||
memory = data[0..<0];
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator;
|
||||
memory = make([]byte, size);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
free_arena :: proc(using a: ^Arena) {
|
||||
if backing.procedure != nil {
|
||||
push_allocator backing {
|
||||
free(memory);
|
||||
memory = nil;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = arena_allocator_proc,
|
||||
data = arena,
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
arena := ^Arena(allocator_data);
|
||||
|
||||
match mode {
|
||||
case ALLOC:
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.memory) {
|
||||
fmt.fprintln(os.stderr, "Arena out of memory");
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.memory[arena.offset];
|
||||
|
||||
ptr := align_forward(end, alignment);
|
||||
arena.offset += total_size;
|
||||
return zero(ptr, size);
|
||||
|
||||
case FREE:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
|
||||
case FREE_ALL:
|
||||
arena.offset = 0;
|
||||
|
||||
case RESIZE:
|
||||
return default_resize_align(old_memory, old_size, size, alignment);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count++;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.memory = arena.memory[0..<original_count];
|
||||
arena.temp_count--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
prev_pow2 :: proc(n: i64) -> i64 {
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
WORD_SIZE :: size_of(int);
|
||||
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
|
||||
using Type_Info;
|
||||
match info in type_info {
|
||||
case Named:
|
||||
return align_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size;
|
||||
case String:
|
||||
return WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
return align_of_type_info(info.elem);
|
||||
case 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 info.align;
|
||||
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);
|
||||
case Map:
|
||||
return align_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_formula :: proc(size, align: int) -> int {
|
||||
result := size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
|
||||
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
WORD_SIZE :: size_of(int);
|
||||
using Type_Info;
|
||||
match info in type_info {
|
||||
case Named:
|
||||
return size_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size;
|
||||
case String:
|
||||
return 2*WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return 2*WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case Dynamic_Array:
|
||||
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
|
||||
case Slice:
|
||||
return 2*WORD_SIZE;
|
||||
case Vector:
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case Struct:
|
||||
return info.size;
|
||||
case Union:
|
||||
return info.size;
|
||||
case Raw_Union:
|
||||
return info.size;
|
||||
case Enum:
|
||||
return size_of_type_info(info.base);
|
||||
case Map:
|
||||
return size_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package mem
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
Allocator_Mode :: runtime.Allocator_Mode;
|
||||
/*
|
||||
Allocator_Mode :: enum byte {
|
||||
Alloc,
|
||||
Free,
|
||||
Free_All,
|
||||
Resize,
|
||||
Query_Features,
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Mode_Set :: runtime.Allocator_Mode_Set;
|
||||
/*
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode];
|
||||
*/
|
||||
|
||||
Allocator_Query_Info :: runtime.Allocator_Query_Info;
|
||||
/*
|
||||
Allocator_Query_Info :: struct {
|
||||
pointer: Maybe(rawptr),
|
||||
size: Maybe(int),
|
||||
alignment: Maybe(int),
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Proc :: runtime.Allocator_Proc;
|
||||
/*
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
*/
|
||||
|
||||
Allocator :: runtime.Allocator;
|
||||
/*
|
||||
Allocator :: struct {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
*/
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
||||
}
|
||||
}
|
||||
|
||||
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
return nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, 0, loc);
|
||||
return set;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer;
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, 0, loc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc);
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc);
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc);
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc);
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m;
|
||||
delete_slice(raw.hashes);
|
||||
free(raw.entries.data, raw.entries.allocator, loc);
|
||||
}
|
||||
|
||||
|
||||
delete :: proc{
|
||||
delete_string,
|
||||
delete_cstring,
|
||||
delete_dynamic_array,
|
||||
delete_slice,
|
||||
delete_map,
|
||||
};
|
||||
|
||||
|
||||
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
return new_aligned(T, align_of(T), allocator, loc);
|
||||
}
|
||||
new_aligned :: inline proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
|
||||
if ptr != nil do ptr^ = T{};
|
||||
return ptr;
|
||||
}
|
||||
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil do ptr^ = data;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||
}
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_slice_error_loc(loc, len);
|
||||
data := alloc(size_of(E)*len, alignment, allocator, loc);
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return nil;
|
||||
}
|
||||
zero(data, size_of(E)*len);
|
||||
s := Raw_Slice{data, len};
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
|
||||
}
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_dynamic_array_error_loc(loc, len, cap);
|
||||
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||
if data == nil && size_of(E) != 0 {
|
||||
s.len, s.cap = 0, 0;
|
||||
}
|
||||
zero(data, size_of(E)*len);
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap);
|
||||
context.allocator = allocator;
|
||||
|
||||
m: T;
|
||||
reserve_map(&m, cap);
|
||||
return m;
|
||||
}
|
||||
|
||||
make :: proc{
|
||||
make_slice,
|
||||
make_dynamic_array,
|
||||
make_dynamic_array_len,
|
||||
make_dynamic_array_len_cap,
|
||||
make_map,
|
||||
};
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory, allocator, loc);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size do return old_memory;
|
||||
|
||||
new_memory := alloc(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil do return nil;
|
||||
|
||||
copy(new_memory, old_memory, min(old_size, new_size));
|
||||
free(old_memory, allocator, loc);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,882 @@
|
||||
package mem
|
||||
|
||||
import "intrinsics"
|
||||
import "core:runtime"
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
// Custom allocators
|
||||
|
||||
Arena :: struct {
|
||||
data: []byte,
|
||||
offset: int,
|
||||
peak_used: int,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena,
|
||||
prev_offset: int,
|
||||
}
|
||||
|
||||
|
||||
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data;
|
||||
a.offset = 0;
|
||||
a.peak_used = 0;
|
||||
a.temp_count = 0;
|
||||
}
|
||||
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = arena_allocator_proc,
|
||||
data = arena,
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
arena := cast(^Arena)allocator_data;
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset];
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
arena.offset += total_size;
|
||||
arena.peak_used = max(arena.peak_used, arena.offset);
|
||||
return zero(ptr, size);
|
||||
|
||||
case .Free:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
|
||||
case .Free_All:
|
||||
arena.offset = 0;
|
||||
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.prev_offset = a.offset;
|
||||
a.temp_count += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(arena.offset >= prev_offset);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.offset = prev_offset;
|
||||
arena.temp_count -= 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
default_to_default_allocator: bool,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
scratch.data = data;
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
|
||||
if scratch == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
scratch^ = {};
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
scratch := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
if scratch.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself");
|
||||
}
|
||||
scratch_allocator_init(scratch, make([]byte, 1<<22));
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := scratch.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
scratch.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := alloc(size, alignment, a, loc);
|
||||
if scratch.leaked_allocations == nil {
|
||||
scratch.leaked_allocations = make([dynamic]rawptr, a);
|
||||
}
|
||||
append(&scratch.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
|
||||
case .Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, full_size);
|
||||
return nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
case .Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
for ptr in scratch.leaked_allocations {
|
||||
free(ptr, scratch.backup_allocator);
|
||||
}
|
||||
clear(&scratch.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
return old_memory;
|
||||
}
|
||||
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = scratch_allocator_proc,
|
||||
data = scratch,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Stack_Allocation_Header :: struct {
|
||||
prev_offset: int,
|
||||
padding: int,
|
||||
}
|
||||
|
||||
// Stack is a stack-like allocator which has a strict memory freeing order
|
||||
Stack :: struct {
|
||||
data: []byte,
|
||||
prev_offset: int,
|
||||
curr_offset: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data;
|
||||
s.prev_offset = 0;
|
||||
s.curr_offset = 0;
|
||||
s.peak_used = 0;
|
||||
}
|
||||
|
||||
stack_allocator :: proc(stack: ^Stack) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = stack_allocator_proc,
|
||||
data = stack,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
s := cast(^Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
}
|
||||
s.prev_offset = s.curr_offset;
|
||||
s.curr_offset += padding;
|
||||
|
||||
next_addr := curr_addr + uintptr(padding);
|
||||
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header));
|
||||
header.padding = auto_cast padding;
|
||||
header.prev_offset = auto_cast s.prev_offset;
|
||||
|
||||
s.curr_offset += size;
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
panic("Out of order stack allocator free");
|
||||
}
|
||||
|
||||
s.curr_offset = int(old_offset);
|
||||
s.prev_offset = int(header.prev_offset);
|
||||
|
||||
|
||||
case .Free_All:
|
||||
s.prev_offset = 0;
|
||||
s.curr_offset = 0;
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, alignment);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
ptr := raw_alloc(s, size, alignment);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
|
||||
assert(old_memory_size == uintptr(old_size));
|
||||
|
||||
diff := size - old_size;
|
||||
s.curr_offset += diff; // works for smaller sizes too
|
||||
if diff > 0 {
|
||||
zero(rawptr(curr_addr + uintptr(diff)), diff);
|
||||
}
|
||||
|
||||
return old_memory;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Small_Stack_Allocation_Header :: struct {
|
||||
padding: u8,
|
||||
}
|
||||
|
||||
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
|
||||
Small_Stack :: struct {
|
||||
data: []byte,
|
||||
offset: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data;
|
||||
s.offset = 0;
|
||||
s.peak_used = 0;
|
||||
}
|
||||
|
||||
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = small_stack_allocator_proc,
|
||||
data = stack,
|
||||
};
|
||||
}
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
s := cast(^Small_Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
}
|
||||
s.offset += padding;
|
||||
|
||||
next_addr := curr_addr + uintptr(padding);
|
||||
header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header));
|
||||
header.padding = auto_cast padding;
|
||||
|
||||
s.offset += size;
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, align);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
}
|
||||
|
||||
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
s.offset = int(old_offset);
|
||||
|
||||
case .Free_All:
|
||||
s.offset = 0;
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, align);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Treat as a double free
|
||||
return nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
ptr := raw_alloc(s, size, align);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Dynamic_Pool :: struct {
|
||||
block_size: int,
|
||||
out_band_size: int,
|
||||
alignment: int,
|
||||
|
||||
unused_blocks: [dynamic]rawptr,
|
||||
used_blocks: [dynamic]rawptr,
|
||||
out_band_allocations: [dynamic]rawptr,
|
||||
|
||||
current_block: rawptr,
|
||||
current_pos: rawptr,
|
||||
bytes_left: int,
|
||||
|
||||
block_allocator: Allocator,
|
||||
}
|
||||
|
||||
|
||||
DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: 65536;
|
||||
DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
|
||||
|
||||
|
||||
|
||||
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
pool := (^Dynamic_Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return dynamic_pool_alloc(pool, size);
|
||||
case .Free:
|
||||
//
|
||||
case .Free_All:
|
||||
dynamic_pool_free_all(pool);
|
||||
case .Resize:
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
}
|
||||
ptr := dynamic_pool_alloc(pool, size);
|
||||
copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
info.size = pool.block_size;
|
||||
info.alignment = pool.alignment;
|
||||
return info;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = dynamic_pool_allocator_proc,
|
||||
data = pool,
|
||||
};
|
||||
}
|
||||
|
||||
dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
|
||||
block_allocator := context.allocator,
|
||||
array_allocator := context.allocator,
|
||||
block_size := DYNAMIC_POOL_BLOCK_SIZE_DEFAULT,
|
||||
out_band_size := DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT,
|
||||
alignment := 8) {
|
||||
pool.block_size = block_size;
|
||||
pool.out_band_size = out_band_size;
|
||||
pool.alignment = alignment;
|
||||
pool.block_allocator = block_allocator;
|
||||
pool.out_band_allocations.allocator = array_allocator;
|
||||
pool. unused_blocks.allocator = array_allocator;
|
||||
pool. used_blocks.allocator = array_allocator;
|
||||
}
|
||||
|
||||
dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_free_all(pool);
|
||||
delete(unused_blocks);
|
||||
delete(used_blocks);
|
||||
|
||||
zero(pool, size_of(pool^));
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
|
||||
if block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it");
|
||||
}
|
||||
|
||||
if current_block != nil {
|
||||
append(&used_blocks, current_block);
|
||||
}
|
||||
|
||||
new_block: rawptr;
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks);
|
||||
} else {
|
||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
}
|
||||
|
||||
bytes_left = block_size;
|
||||
current_pos = new_block;
|
||||
current_block = new_block;
|
||||
}
|
||||
|
||||
|
||||
n := bytes;
|
||||
extra := alignment - (n % alignment);
|
||||
n += extra;
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, (^byte)(memory));
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
if bytes_left < n {
|
||||
cycle_new_block(pool);
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||
bytes_left -= n;
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
|
||||
if current_block != nil {
|
||||
append(&unused_blocks, current_block);
|
||||
current_block = nil;
|
||||
}
|
||||
|
||||
for block in used_blocks {
|
||||
append(&unused_blocks, block);
|
||||
}
|
||||
clear(&used_blocks);
|
||||
|
||||
for a in out_band_allocations {
|
||||
free(a, block_allocator);
|
||||
}
|
||||
clear(&out_band_allocations);
|
||||
}
|
||||
|
||||
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_reset(pool);
|
||||
|
||||
for block in unused_blocks {
|
||||
free(block, block_allocator);
|
||||
}
|
||||
clear(&unused_blocks);
|
||||
}
|
||||
|
||||
|
||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called");
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called");
|
||||
}
|
||||
case .Free:
|
||||
if old_memory != nil {
|
||||
panic("mem: panic allocator, .Free called");
|
||||
}
|
||||
case .Free_All:
|
||||
panic("mem: panic allocator, .Free_All called");
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = panic_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch alignment {
|
||||
case: return intrinsics.alloca(size, 2*align_of(uintptr));
|
||||
case 0: return intrinsics.alloca(size, 0);
|
||||
|
||||
case 1: return intrinsics.alloca(size, 1);
|
||||
case 2: return intrinsics.alloca(size, 2);
|
||||
case 4: return intrinsics.alloca(size, 4);
|
||||
case 8: return intrinsics.alloca(size, 8);
|
||||
case 16: return intrinsics.alloca(size, 16);
|
||||
case 32: return intrinsics.alloca(size, 32);
|
||||
case 64: return intrinsics.alloca(size, 64);
|
||||
case 128: return intrinsics.alloca(size, 128);
|
||||
case 256: return intrinsics.alloca(size, 256);
|
||||
case 512: return intrinsics.alloca(size, 512);
|
||||
case 1024: return intrinsics.alloca(size, 1024);
|
||||
case 2048: return intrinsics.alloca(size, 2048);
|
||||
case 4096: return intrinsics.alloca(size, 4096);
|
||||
case 8192: return intrinsics.alloca(size, 8192);
|
||||
case 16384: return intrinsics.alloca(size, 16384);
|
||||
case 32768: return intrinsics.alloca(size, 32768);
|
||||
case 65536: return intrinsics.alloca(size, 65536);
|
||||
}
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
|
||||
|
||||
case .Free:
|
||||
// Do nothing
|
||||
case .Free_All:
|
||||
// Do nothing
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
alloca_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = alloca_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator :: struct {
|
||||
backing: Allocator,
|
||||
allocation_map: map[rawptr]Tracking_Allocator_Entry,
|
||||
clear_on_free_all: bool,
|
||||
}
|
||||
|
||||
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, allocation_map_allocator := context.allocator) {
|
||||
t.backing = backing_allocator;
|
||||
t.allocation_map.allocator = allocation_map_allocator;
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.allocation_map);
|
||||
}
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
procedure = tracking_allocator_proc,
|
||||
};
|
||||
}
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
data := (^Tracking_Allocator)(allocator_data);
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
if entry, ok := data.allocation_map[info.pointer]; ok {
|
||||
info.size = entry.size;
|
||||
info.alignment = entry.alignment;
|
||||
return info;
|
||||
}
|
||||
info.pointer = nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
result := data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, flags, loc);
|
||||
|
||||
if data.allocation_map.allocator.procedure == nil {
|
||||
data.allocation_map.allocator = context.allocator;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
location = loc,
|
||||
};
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
case .Resize:
|
||||
if old_memory != result {
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
}
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
location = loc,
|
||||
};
|
||||
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map);
|
||||
}
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
package mem
|
||||
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
|
||||
set :: proc(data: rawptr, value: byte, len: int) -> rawptr {
|
||||
return runtime.memset(data, i32(value), len);
|
||||
}
|
||||
zero :: inline proc(data: rawptr, len: int) -> rawptr {
|
||||
return set(data, 0, len);
|
||||
}
|
||||
zero_item :: inline proc(item: $P/^$T) {
|
||||
set(item, 0, size_of(T));
|
||||
}
|
||||
zero_slice :: proc(data: $T/[]$E) {
|
||||
zero(raw_data(data), size_of(E)*len(data));
|
||||
}
|
||||
|
||||
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return runtime.mem_copy(dst, src, len);
|
||||
}
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return runtime.mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: inline proc(a, b: []byte) -> int {
|
||||
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)));
|
||||
if res == 0 && len(a) != len(b) {
|
||||
return len(a) <= len(b) ? -1 : +1;
|
||||
} else if len(a) == 0 && len(b) == 0 {
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
compare_byte_ptrs :: proc(a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||
switch {
|
||||
case a == b:
|
||||
return 0;
|
||||
case a == nil:
|
||||
return -1;
|
||||
case b == nil:
|
||||
return -1;
|
||||
case n == 0:
|
||||
return 0;
|
||||
}
|
||||
|
||||
x := slice_ptr(a, n);
|
||||
y := slice_ptr(b, n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := n/SU + 1;
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := 0;
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
la := slice_ptr((^uintptr)(a), fast);
|
||||
lb := slice_ptr((^uintptr)(b), fast);
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
if la[curr_block] ~ lb[curr_block] != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
if x[pos] ~ y[pos] != 0 {
|
||||
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
if x[offset] ~ y[offset] != 0 {
|
||||
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
check_zero :: proc(data: []byte) -> bool {
|
||||
return check_zero_ptr(raw_data(data), len(data));
|
||||
}
|
||||
|
||||
check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
|
||||
switch {
|
||||
case len <= 0:
|
||||
return true;
|
||||
case ptr == nil:
|
||||
return true;
|
||||
}
|
||||
|
||||
start := uintptr(ptr);
|
||||
start_aligned := align_forward_uintptr(start, align_of(uintptr));
|
||||
end := start + uintptr(len);
|
||||
end_aligned := align_backward_uintptr(end, align_of(uintptr));
|
||||
|
||||
for b in start..<start_aligned {
|
||||
if (^byte)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for b := start_aligned; b < end_aligned; b += size_of(uintptr) {
|
||||
if (^uintptr)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for b in end_aligned..<end {
|
||||
if (^byte)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
simple_equal :: proc(a, b: $T) -> bool where intrinsics.type_is_simple_compare(T) {
|
||||
a, b := a, b;
|
||||
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0;
|
||||
}
|
||||
|
||||
compare_ptrs :: inline proc(a, b: rawptr, n: int) -> int {
|
||||
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
|
||||
}
|
||||
|
||||
ptr_offset :: inline proc(ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
ptr_sub :: inline proc(a, b: $P/^$T) -> int {
|
||||
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
|
||||
}
|
||||
|
||||
slice_ptr :: inline proc(ptr: ^$T, len: int) -> []T {
|
||||
assert(len >= 0);
|
||||
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
||||
}
|
||||
|
||||
slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{data = ptr, len = len};
|
||||
}
|
||||
|
||||
slice_to_bytes :: inline proc(slice: $E/[]$T) -> []byte {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len *= size_of(T);
|
||||
return transmute([]byte)s;
|
||||
}
|
||||
|
||||
slice_data_cast :: inline proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
when size_of(A) == 0 || size_of(B) == 0 {
|
||||
return nil;
|
||||
} else {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len = (len(slice) * size_of(B)) / size_of(A);
|
||||
return transmute(T)s;
|
||||
}
|
||||
}
|
||||
|
||||
slice_to_components :: proc(slice: $E/[]$T) -> (data: ^T, len: int) {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
return s.data, s.len;
|
||||
}
|
||||
|
||||
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
return transmute([dynamic]E)Raw_Dynamic_Array{
|
||||
data = raw_data(backing),
|
||||
len = 0,
|
||||
cap = len(backing),
|
||||
allocator = nil_allocator(),
|
||||
};
|
||||
}
|
||||
|
||||
ptr_to_bytes :: inline proc(ptr: ^$T, len := 1) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
|
||||
}
|
||||
|
||||
any_to_bytes :: inline proc(val: any) -> []byte {
|
||||
ti := type_info_of(val.id);
|
||||
size := ti != nil ? ti.size : 0;
|
||||
return transmute([]byte)Raw_Slice{val.data, size};
|
||||
}
|
||||
|
||||
|
||||
kilobytes :: inline proc(x: int) -> int do return (x) * 1024;
|
||||
megabytes :: inline proc(x: int) -> int do return kilobytes(x) * 1024;
|
||||
gigabytes :: inline proc(x: int) -> int do return megabytes(x) * 1024;
|
||||
terabytes :: inline proc(x: int) -> int do return gigabytes(x) * 1024;
|
||||
|
||||
is_power_of_two :: inline proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_forward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_forward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_backward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
return align_forward_uintptr(ptr - align + 1, align);
|
||||
}
|
||||
|
||||
align_backward_int :: inline proc(ptr, align: int) -> int {
|
||||
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
|
||||
context.allocator = a;
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Fixed_Byte_Buffer :: distinct [dynamic]byte;
|
||||
|
||||
make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
|
||||
s := transmute(Raw_Slice)backing;
|
||||
d: Raw_Dynamic_Array;
|
||||
d.data = s.data;
|
||||
d.len = 0;
|
||||
d.cap = s.len;
|
||||
d.allocator = nil_allocator();
|
||||
return transmute(Fixed_Byte_Buffer)d;
|
||||
}
|
||||
|
||||
|
||||
|
||||
align_formula :: proc(size, align: int) -> int {
|
||||
result := size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
|
||||
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
|
||||
p := uintptr(ptr);
|
||||
a := uintptr(align);
|
||||
modulo := p & (a-1);
|
||||
|
||||
padding := uintptr(0);
|
||||
if modulo != 0 do padding = a - modulo;
|
||||
|
||||
needed_space := uintptr(header_size);
|
||||
if padding < needed_space {
|
||||
needed_space -= padding;
|
||||
|
||||
if needed_space & (a-1) > 0 {
|
||||
padding += align * (1+(needed_space/align));
|
||||
} else {
|
||||
padding += align * (needed_space/align);
|
||||
}
|
||||
}
|
||||
|
||||
return int(padding);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package mem
|
||||
|
||||
Raw_Any :: struct {
|
||||
data: rawptr,
|
||||
id: typeid,
|
||||
}
|
||||
|
||||
Raw_String :: struct {
|
||||
data: ^byte,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Cstring :: struct {
|
||||
data: ^byte,
|
||||
}
|
||||
|
||||
Raw_Slice :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Dynamic_Array :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
Raw_Map :: struct {
|
||||
hashes: []int,
|
||||
entries: Raw_Dynamic_Array,
|
||||
}
|
||||
|
||||
Raw_Complex64 :: struct {real, imag: f32};
|
||||
Raw_Complex128 :: struct {real, imag: f64};
|
||||
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32};
|
||||
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
|
||||
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
|
||||
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
|
||||
|
||||
make_any :: inline proc(data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id};
|
||||
}
|
||||
|
||||
raw_array_data :: proc(a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a);
|
||||
}
|
||||
raw_string_data :: inline proc(s: $T/string) -> ^byte {
|
||||
return (transmute(Raw_String)s).data;
|
||||
}
|
||||
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Slice)a).data;
|
||||
}
|
||||
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
|
||||
}
|
||||
|
||||
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data};
|
||||
|
||||
|
||||
@@ -0,0 +1,649 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||
|
||||
Proc_Inlining :: enum u32 {
|
||||
None = 0,
|
||||
Inline = 1,
|
||||
No_Inline = 2,
|
||||
}
|
||||
|
||||
Proc_Calling_Convention :: enum i32 {
|
||||
Invalid = 0,
|
||||
Odin,
|
||||
Contextless,
|
||||
C_Decl,
|
||||
Std_Call,
|
||||
Fast_Call,
|
||||
|
||||
Foreign_Block_Default = -1,
|
||||
}
|
||||
|
||||
Node_State_Flag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
}
|
||||
Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
||||
|
||||
|
||||
Comment_Group :: struct {
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
pos: tokenizer.Pos,
|
||||
end: tokenizer.Pos,
|
||||
derived: any,
|
||||
state_flags: Node_State_Flags,
|
||||
}
|
||||
|
||||
|
||||
Expr :: struct {
|
||||
using expr_base: Node,
|
||||
}
|
||||
Stmt :: struct {
|
||||
using stmt_base: Node,
|
||||
}
|
||||
Decl :: struct {
|
||||
using decl_base: Stmt,
|
||||
}
|
||||
|
||||
// Expressions
|
||||
|
||||
Bad_Expr :: struct {
|
||||
using node: Expr,
|
||||
}
|
||||
|
||||
Ident :: struct {
|
||||
using node: Expr,
|
||||
name: string,
|
||||
}
|
||||
|
||||
Implicit :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
}
|
||||
|
||||
|
||||
Undef :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
}
|
||||
|
||||
Basic_Lit :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
}
|
||||
|
||||
Basic_Directive :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
name: string,
|
||||
}
|
||||
|
||||
Ellipsis :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Proc_Lit :: struct {
|
||||
using node: Expr,
|
||||
type: ^Proc_Type,
|
||||
body: ^Stmt,
|
||||
tags: Proc_Tags,
|
||||
inlining: Proc_Inlining,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
}
|
||||
|
||||
Comp_Lit :: struct {
|
||||
using node: Expr,
|
||||
type: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
elems: []^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
Tag_Expr :: struct {
|
||||
using node: Expr,
|
||||
op: tokenizer.Token,
|
||||
name: string,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Unary_Expr :: struct {
|
||||
using node: Expr,
|
||||
op: tokenizer.Token,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Binary_Expr :: struct {
|
||||
using node: Expr,
|
||||
left: ^Expr,
|
||||
op: tokenizer.Token,
|
||||
right: ^Expr,
|
||||
}
|
||||
|
||||
Paren_Expr :: struct {
|
||||
using node: Expr,
|
||||
open: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Selector_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
Implicit_Selector_Expr :: struct {
|
||||
using node: Expr,
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
Index_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
index: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Deref_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
op: tokenizer.Token,
|
||||
}
|
||||
|
||||
Slice_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
low: ^Expr,
|
||||
interval: tokenizer.Token,
|
||||
high: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
inlining: Proc_Inlining,
|
||||
expr: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
args: []^Expr,
|
||||
ellipsis: tokenizer.Token,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Field_Value :: struct {
|
||||
using node: Expr,
|
||||
field: ^Expr,
|
||||
sep: tokenizer.Pos,
|
||||
value: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_Expr :: struct {
|
||||
using node: Expr,
|
||||
cond: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
x: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_If_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_When_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Type_Assertion :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
dot: tokenizer.Pos,
|
||||
open: tokenizer.Pos,
|
||||
type: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Type_Cast :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
open: tokenizer.Pos,
|
||||
type: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Auto_Cast :: struct {
|
||||
using node: Expr,
|
||||
op: tokenizer.Token,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Statements
|
||||
|
||||
Bad_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
}
|
||||
|
||||
Empty_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
semicolon: tokenizer.Pos, // Position of the following ';'
|
||||
}
|
||||
|
||||
Expr_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Tag_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
op: tokenizer.Token,
|
||||
name: string,
|
||||
stmt: ^Stmt,
|
||||
}
|
||||
|
||||
Assign_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
lhs: []^Expr,
|
||||
op: tokenizer.Token,
|
||||
rhs: []^Expr,
|
||||
}
|
||||
|
||||
|
||||
Block_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
stmts: []^Stmt,
|
||||
close: tokenizer.Pos,
|
||||
uses_do: bool,
|
||||
}
|
||||
|
||||
If_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
if_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
else_stmt: ^Stmt,
|
||||
}
|
||||
|
||||
When_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
when_pos: tokenizer.Pos,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
else_stmt: ^Stmt,
|
||||
}
|
||||
|
||||
Return_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
results: []^Expr,
|
||||
}
|
||||
|
||||
Defer_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
stmt: ^Stmt,
|
||||
}
|
||||
|
||||
For_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
post: ^Stmt,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
|
||||
Case_Clause :: struct {
|
||||
using node: Stmt,
|
||||
case_pos: tokenizer.Pos,
|
||||
list: []^Expr,
|
||||
terminator: tokenizer.Token,
|
||||
body: []^Stmt,
|
||||
}
|
||||
|
||||
Switch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
switch_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
partial: bool,
|
||||
}
|
||||
|
||||
Type_Switch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
switch_pos: tokenizer.Pos,
|
||||
tag: ^Stmt,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
partial: bool,
|
||||
}
|
||||
|
||||
Branch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
tok: tokenizer.Token,
|
||||
label: ^Ident,
|
||||
}
|
||||
|
||||
Using_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
list: []^Expr,
|
||||
}
|
||||
|
||||
|
||||
// Declarations
|
||||
|
||||
Bad_Decl :: struct {
|
||||
using node: Decl,
|
||||
}
|
||||
|
||||
Value_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
names: []^Expr,
|
||||
type: ^Expr,
|
||||
values: []^Expr,
|
||||
comment: ^Comment_Group,
|
||||
is_using: bool,
|
||||
is_mutable: bool,
|
||||
}
|
||||
|
||||
Package_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
token: tokenizer.Token,
|
||||
name: string,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
is_using: bool,
|
||||
import_tok: tokenizer.Token,
|
||||
name: tokenizer.Token,
|
||||
relpath: tokenizer.Token,
|
||||
fullpath: string,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
Foreign_Block_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
tok: tokenizer.Token,
|
||||
foreign_library: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
Foreign_Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
foreign_tok: tokenizer.Token,
|
||||
import_tok: tokenizer.Token,
|
||||
name: ^Ident,
|
||||
collection_name: string,
|
||||
fullpaths: []string,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Other things
|
||||
unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
val = expr;
|
||||
if expr == nil {
|
||||
return;
|
||||
}
|
||||
for {
|
||||
e, ok := val.derived.(Paren_Expr);
|
||||
if !ok do break;
|
||||
val = e.expr;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Field_Flag :: enum {
|
||||
Ellipsis,
|
||||
Using,
|
||||
No_Alias,
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
In,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
Default_Parameters,
|
||||
Typeid_Token,
|
||||
}
|
||||
|
||||
Field_Flags :: distinct bit_set[Field_Flag];
|
||||
|
||||
Field_Flags_Struct :: Field_Flags{
|
||||
.Using,
|
||||
.Tags,
|
||||
};
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
.Typeid_Token,
|
||||
};
|
||||
Field_Flags_Signature :: Field_Flags{
|
||||
.Ellipsis,
|
||||
.Using,
|
||||
.No_Alias,
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Default_Parameters,
|
||||
};
|
||||
|
||||
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
|
||||
Field_Flags_Signature_Results :: Field_Flags_Signature;
|
||||
|
||||
|
||||
Proc_Group :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
open: tokenizer.Pos,
|
||||
args: []^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Attribute :: struct {
|
||||
using node: Node,
|
||||
tok: tokenizer.Token_Kind,
|
||||
open: tokenizer.Pos,
|
||||
elems: []^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
using node: Node,
|
||||
docs: ^Comment_Group,
|
||||
names: []^Expr, // Could be polymorphic
|
||||
type: ^Expr,
|
||||
default_value: ^Expr,
|
||||
tag: tokenizer.Token,
|
||||
flags: Field_Flags,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
Field_List :: struct {
|
||||
using node: Node,
|
||||
open: tokenizer.Pos,
|
||||
list: []^Field,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
// Types
|
||||
Typeid_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
specialization: ^Expr,
|
||||
}
|
||||
|
||||
Helper_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Distinct_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Opaque_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Poly_Type :: struct {
|
||||
using node: Expr,
|
||||
dollar: tokenizer.Pos,
|
||||
type: ^Ident,
|
||||
specialization: ^Expr,
|
||||
}
|
||||
|
||||
Proc_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
calling_convention: Proc_Calling_Convention,
|
||||
params: ^Field_List,
|
||||
arrow: tokenizer.Pos,
|
||||
results: ^Field_List,
|
||||
tags: Proc_Tags,
|
||||
generic: bool,
|
||||
diverging: bool,
|
||||
}
|
||||
|
||||
Pointer_Type :: struct {
|
||||
using node: Expr,
|
||||
pointer: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Array_Type :: struct {
|
||||
using node: Expr,
|
||||
open: tokenizer.Pos,
|
||||
tag: ^Expr,
|
||||
len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types
|
||||
close: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Dynamic_Array_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
dynamic_pos: tokenizer.Pos,
|
||||
close: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Struct_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
}
|
||||
|
||||
Union_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
variants: []^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_maybe: bool,
|
||||
}
|
||||
|
||||
Enum_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
base_type: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Expr,
|
||||
close: tokenizer.Pos,
|
||||
|
||||
is_using: bool,
|
||||
}
|
||||
|
||||
Bit_Field_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
align: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Field_Value, // Field_Value with ':' rather than '='
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Bit_Set_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
open: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
underlying: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Map_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
key: ^Expr,
|
||||
value: ^Expr,
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:mem"
|
||||
import "core:fmt"
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
|
||||
n := mem.new(T);
|
||||
n.pos = pos;
|
||||
n.end = end;
|
||||
n.derived = n^;
|
||||
base: ^Node = n; // dummy check
|
||||
_ = base; // "Use" type to make -vet happy
|
||||
return n;
|
||||
}
|
||||
|
||||
clone :: proc{
|
||||
clone_node,
|
||||
clone_expr,
|
||||
clone_stmt,
|
||||
clone_decl,
|
||||
clone_array,
|
||||
clone_dynamic_array,
|
||||
};
|
||||
|
||||
clone_array :: proc(array: $A/[]^$T) -> A {
|
||||
if len(array) == 0 {
|
||||
return nil;
|
||||
}
|
||||
res := make(A, len(array));
|
||||
for elem, i in array {
|
||||
res[i] = auto_cast clone(elem);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
clone_dynamic_array :: proc(array: $A/[dynamic]^$T) -> A {
|
||||
if len(array) == 0 {
|
||||
return nil;
|
||||
}
|
||||
res := make(A, len(array));
|
||||
for elem, i in array {
|
||||
res[i] = auto_cast clone(elem);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
clone_expr :: proc(node: ^Expr) -> ^Expr {
|
||||
return cast(^Expr)clone_node(node);
|
||||
}
|
||||
clone_stmt :: proc(node: ^Stmt) -> ^Stmt {
|
||||
return cast(^Stmt)clone_node(node);
|
||||
}
|
||||
clone_decl :: proc(node: ^Decl) -> ^Decl {
|
||||
return cast(^Decl)clone_node(node);
|
||||
}
|
||||
clone_node :: proc(node: ^Node) -> ^Node {
|
||||
if node == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
size := size_of(Node);
|
||||
align := align_of(Node);
|
||||
ti := type_info_of(node.derived.id);
|
||||
if ti != nil {
|
||||
size = ti.size;
|
||||
align = ti.align;
|
||||
}
|
||||
|
||||
res := cast(^Node)mem.alloc(size, align);
|
||||
src: rawptr = node;
|
||||
if node.derived != nil {
|
||||
src = node.derived.data;
|
||||
}
|
||||
mem.copy(res, src, size);
|
||||
res.derived.data = rawptr(res);
|
||||
|
||||
switch n in node.derived {
|
||||
case Bad_Expr:
|
||||
case Ident:
|
||||
case Implicit:
|
||||
case Undef:
|
||||
case Basic_Lit:
|
||||
|
||||
case Ellipsis:
|
||||
r := cast(^Ellipsis)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Proc_Lit:
|
||||
r := cast(^Proc_Lit)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.body = clone(r.body);
|
||||
case Comp_Lit:
|
||||
r := cast(^Comp_Lit)res;
|
||||
r.type = clone(r.type);
|
||||
r.elems = clone(r.elems);
|
||||
|
||||
case Tag_Expr:
|
||||
r := cast(^Tag_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Unary_Expr:
|
||||
r := cast(^Unary_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Binary_Expr:
|
||||
r := cast(^Binary_Expr)res;
|
||||
r.left = clone(r.left);
|
||||
r.right = clone(r.right);
|
||||
case Paren_Expr:
|
||||
r := cast(^Paren_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Selector_Expr:
|
||||
r := cast(^Selector_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.field = auto_cast clone(r.field);
|
||||
case Index_Expr:
|
||||
r := cast(^Index_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.index = clone(r.index);
|
||||
case Deref_Expr:
|
||||
r := cast(^Deref_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Slice_Expr:
|
||||
r := cast(^Slice_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.low = clone(r.low);
|
||||
r.high = clone(r.high);
|
||||
case Call_Expr:
|
||||
r := cast(^Call_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.args = clone(r.args);
|
||||
case Field_Value:
|
||||
r := cast(^Field_Value)res;
|
||||
r.field = clone(r.field);
|
||||
r.value = clone(r.value);
|
||||
case Ternary_Expr:
|
||||
r := cast(^Ternary_Expr)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.x = clone(r.x);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_If_Expr:
|
||||
r := cast(^Ternary_If_Expr)res;
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_When_Expr:
|
||||
r := cast(^Ternary_When_Expr)res;
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Type_Assertion:
|
||||
r := cast(^Type_Assertion)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.type = clone(r.type);
|
||||
case Type_Cast:
|
||||
r := cast(^Type_Cast)res;
|
||||
r.type = clone(r.type);
|
||||
r.expr = clone(r.expr);
|
||||
case Auto_Cast:
|
||||
r := cast(^Auto_Cast)res;
|
||||
r.expr = clone(r.expr);
|
||||
|
||||
case Bad_Stmt:
|
||||
case Empty_Stmt:
|
||||
case Expr_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Tag_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
|
||||
case Assign_Stmt:
|
||||
r := cast(^Assign_Stmt)res;
|
||||
r.lhs = clone(r.lhs);
|
||||
r.rhs = clone(r.rhs);
|
||||
case Block_Stmt:
|
||||
r := cast(^Block_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.stmts = clone(r.stmts);
|
||||
case If_Stmt:
|
||||
r := cast(^If_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case When_Stmt:
|
||||
r := cast(^When_Stmt)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case Return_Stmt:
|
||||
r := cast(^Return_Stmt)res;
|
||||
r.results = clone(r.results);
|
||||
case Defer_Stmt:
|
||||
r := cast(^Defer_Stmt)res;
|
||||
r.stmt = clone(r.stmt);
|
||||
case For_Stmt:
|
||||
r := cast(^For_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.post = clone(r.post);
|
||||
r.body = clone(r.body);
|
||||
case Range_Stmt:
|
||||
r := cast(^Range_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.val0 = clone(r.val0);
|
||||
r.val1 = clone(r.val1);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Case_Clause:
|
||||
r := cast(^Case_Clause)res;
|
||||
r.list = clone(r.list);
|
||||
r.body = clone(r.body);
|
||||
case Switch_Stmt:
|
||||
r := cast(^Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
case Type_Switch_Stmt:
|
||||
r := cast(^Type_Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.tag = clone(r.tag);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Branch_Stmt:
|
||||
r := cast(^Branch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
case Using_Stmt:
|
||||
r := cast(^Using_Stmt)res;
|
||||
r.list = clone(r.list);
|
||||
case Bad_Decl:
|
||||
case Value_Decl:
|
||||
r := cast(^Value_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
r.values = clone(r.values);
|
||||
case Package_Decl:
|
||||
case Import_Decl:
|
||||
case Foreign_Block_Decl:
|
||||
r := cast(^Foreign_Block_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.foreign_library = clone(r.foreign_library);
|
||||
r.body = clone(r.body);
|
||||
case Foreign_Import_Decl:
|
||||
r := cast(^Foreign_Import_Decl)res;
|
||||
r.name = auto_cast clone(r.name);
|
||||
case Proc_Group:
|
||||
r := cast(^Proc_Group)res;
|
||||
r.args = clone(r.args);
|
||||
case Attribute:
|
||||
r := cast(^Attribute)res;
|
||||
r.elems = clone(r.elems);
|
||||
case Field:
|
||||
r := cast(^Field)res;
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
r.default_value = clone(r.default_value);
|
||||
case Field_List:
|
||||
r := cast(^Field_List)res;
|
||||
r.list = clone(r.list);
|
||||
case Typeid_Type:
|
||||
r := cast(^Typeid_Type)res;
|
||||
r.specialization = clone(r.specialization);
|
||||
case Helper_Type:
|
||||
r := cast(^Helper_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Distinct_Type:
|
||||
r := cast(^Distinct_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Opaque_Type:
|
||||
r := cast(^Opaque_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Poly_Type:
|
||||
r := cast(^Poly_Type)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.specialization = clone(r.specialization);
|
||||
case Proc_Type:
|
||||
r := cast(^Proc_Type)res;
|
||||
r.params = auto_cast clone(r.params);
|
||||
r.results = auto_cast clone(r.results);
|
||||
case Pointer_Type:
|
||||
r := cast(^Pointer_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Array_Type:
|
||||
r := cast(^Array_Type)res;
|
||||
r.len = clone(r.len);
|
||||
r.elem = clone(r.elem);
|
||||
case Dynamic_Array_Type:
|
||||
r := cast(^Dynamic_Array_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Struct_Type:
|
||||
r := cast(^Struct_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.fields = auto_cast clone(r.fields);
|
||||
case Union_Type:
|
||||
r := cast(^Union_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.variants = clone(r.variants);
|
||||
case Enum_Type:
|
||||
r := cast(^Enum_Type)res;
|
||||
r.base_type = clone(r.base_type);
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Field_Type:
|
||||
r := cast(^Bit_Field_Type)res;
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Set_Type:
|
||||
r := cast(^Bit_Set_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
r.underlying = clone(r.underlying);
|
||||
case Map_Type:
|
||||
r := cast(^Map_Type)res;
|
||||
r.key = clone(r.key);
|
||||
r.value = clone(r.value);
|
||||
|
||||
case:
|
||||
fmt.panicf("Unhandled node kind: %T", n);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
Package_Kind :: enum {
|
||||
Normal,
|
||||
Runtime,
|
||||
Init,
|
||||
}
|
||||
|
||||
Package :: struct {
|
||||
kind: Package_Kind,
|
||||
id: int,
|
||||
name: string,
|
||||
fullpath: string,
|
||||
files: []^File,
|
||||
|
||||
user_data: rawptr,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
id: int,
|
||||
pkg: ^Package,
|
||||
|
||||
fullpath: string,
|
||||
src: []byte,
|
||||
|
||||
pkg_decl: ^Package_Decl,
|
||||
pkg_token: tokenizer.Token,
|
||||
pkg_name: string,
|
||||
|
||||
decls: [dynamic]^Stmt,
|
||||
imports: [dynamic]^Import_Decl,
|
||||
directive_count: int,
|
||||
|
||||
comments: [dynamic]^Comment_Group,
|
||||
|
||||
syntax_warning_count: int,
|
||||
syntax_error_count: int,
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,335 @@
|
||||
package odin_tokenizer
|
||||
|
||||
import "core:strings"
|
||||
|
||||
Token :: struct {
|
||||
kind: Token_Kind,
|
||||
text: string,
|
||||
pos: Pos,
|
||||
}
|
||||
|
||||
Pos :: struct {
|
||||
file: string,
|
||||
offset: int, // starting at 0
|
||||
line: int, // starting at 1
|
||||
column: int, // starting at 1
|
||||
}
|
||||
|
||||
pos_compare :: proc(lhs, rhs: Pos) -> int {
|
||||
if lhs.offset != rhs.offset {
|
||||
return -1 if (lhs.offset < rhs.offset) else +1;
|
||||
}
|
||||
if lhs.line != rhs.line {
|
||||
return -1 if (lhs.line < rhs.line) else +1;
|
||||
}
|
||||
if lhs.column != rhs.column {
|
||||
return -1 if (lhs.column < rhs.column) else +1;
|
||||
}
|
||||
return strings.compare(lhs.file, rhs.file);
|
||||
}
|
||||
|
||||
Token_Kind :: enum u32 {
|
||||
Invalid,
|
||||
EOF,
|
||||
Comment,
|
||||
|
||||
B_Literal_Begin,
|
||||
Ident,
|
||||
Integer,
|
||||
Float,
|
||||
Imag,
|
||||
Rune,
|
||||
String,
|
||||
B_Literal_End,
|
||||
|
||||
B_Operator_Begin,
|
||||
Eq,
|
||||
Not,
|
||||
Hash,
|
||||
At,
|
||||
Dollar,
|
||||
Pointer,
|
||||
Question,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Quo,
|
||||
Mod,
|
||||
Mod_Mod,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
And_Not,
|
||||
Shl,
|
||||
Shr,
|
||||
|
||||
Cmp_And,
|
||||
Cmp_Or,
|
||||
|
||||
B_Assign_Op_Begin,
|
||||
Add_Eq,
|
||||
Sub_Eq,
|
||||
Mul_Eq,
|
||||
Quo_Eq,
|
||||
Mod_Eq,
|
||||
Mod_Mod_Eq,
|
||||
And_Eq,
|
||||
Or_Eq,
|
||||
Xor_Eq,
|
||||
And_Not_Eq,
|
||||
Shl_Eq,
|
||||
Shr_Eq,
|
||||
Cmp_And_Eq,
|
||||
Cmp_Or_Eq,
|
||||
B_Assign_Op_End,
|
||||
|
||||
Arrow_Right,
|
||||
Arrow_Left,
|
||||
Double_Arrow_Right,
|
||||
Undef,
|
||||
|
||||
B_Comparison_Begin,
|
||||
Cmp_Eq,
|
||||
Not_Eq,
|
||||
Lt,
|
||||
Gt,
|
||||
Lt_Eq,
|
||||
Gt_Eq,
|
||||
B_Comparison_End,
|
||||
|
||||
Open_Paren,
|
||||
Close_Paren,
|
||||
Open_Bracket,
|
||||
Close_Bracket,
|
||||
Open_Brace,
|
||||
Close_Brace,
|
||||
Colon,
|
||||
Semicolon,
|
||||
Period,
|
||||
Comma,
|
||||
Ellipsis,
|
||||
Range_Half,
|
||||
Back_Slash,
|
||||
B_Operator_End,
|
||||
|
||||
B_Keyword_Begin,
|
||||
Import,
|
||||
Foreign,
|
||||
Package,
|
||||
Typeid,
|
||||
When,
|
||||
Where,
|
||||
If,
|
||||
Else,
|
||||
For,
|
||||
Switch,
|
||||
In,
|
||||
Not_In,
|
||||
Do,
|
||||
Case,
|
||||
Break,
|
||||
Continue,
|
||||
Fallthrough,
|
||||
Defer,
|
||||
Return,
|
||||
Proc,
|
||||
Macro,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Bit_Field,
|
||||
Bit_Set,
|
||||
Map,
|
||||
Dynamic,
|
||||
Auto_Cast,
|
||||
Cast,
|
||||
Transmute,
|
||||
Distinct,
|
||||
Opaque,
|
||||
Using,
|
||||
Inline,
|
||||
No_Inline,
|
||||
Context,
|
||||
Size_Of,
|
||||
Align_Of,
|
||||
Offset_Of,
|
||||
Type_Of,
|
||||
Const,
|
||||
B_Keyword_End,
|
||||
|
||||
COUNT,
|
||||
|
||||
B_Custom_Keyword_Begin = COUNT+1,
|
||||
// ... Custom keywords
|
||||
};
|
||||
|
||||
tokens := [Token_Kind.COUNT]string {
|
||||
"Invalid",
|
||||
"EOF",
|
||||
"Comment",
|
||||
|
||||
"",
|
||||
"identifier",
|
||||
"integer",
|
||||
"float",
|
||||
"imaginary",
|
||||
"rune",
|
||||
"string",
|
||||
"",
|
||||
|
||||
"",
|
||||
"=",
|
||||
"!",
|
||||
"#",
|
||||
"@",
|
||||
"$",
|
||||
"^",
|
||||
"?",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
"%%",
|
||||
"&",
|
||||
"|",
|
||||
"~",
|
||||
"&~",
|
||||
"<<",
|
||||
">>",
|
||||
|
||||
"&&",
|
||||
"||",
|
||||
|
||||
"",
|
||||
"+=",
|
||||
"-=",
|
||||
"*=",
|
||||
"/=",
|
||||
"%=",
|
||||
"%%=",
|
||||
"&=",
|
||||
"|=",
|
||||
"~=",
|
||||
"&~=",
|
||||
"<<=",
|
||||
">>=",
|
||||
"&&=",
|
||||
"||=",
|
||||
"",
|
||||
|
||||
"->",
|
||||
"<-",
|
||||
"=>",
|
||||
"---",
|
||||
|
||||
"",
|
||||
"==",
|
||||
"!=",
|
||||
"<",
|
||||
">",
|
||||
"<=",
|
||||
">=",
|
||||
"",
|
||||
|
||||
"(",
|
||||
")",
|
||||
"[",
|
||||
"]",
|
||||
"{",
|
||||
"}",
|
||||
":",
|
||||
";",
|
||||
".",
|
||||
",",
|
||||
"..",
|
||||
"..<",
|
||||
"\\",
|
||||
"",
|
||||
|
||||
"",
|
||||
"import",
|
||||
"foreign",
|
||||
"package",
|
||||
"typeid",
|
||||
"when",
|
||||
"where",
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"switch",
|
||||
"in",
|
||||
"not_in",
|
||||
"do",
|
||||
"case",
|
||||
"break",
|
||||
"continue",
|
||||
"fallthrough",
|
||||
"defer",
|
||||
"return",
|
||||
"proc",
|
||||
"macro",
|
||||
"struct",
|
||||
"union",
|
||||
"enum",
|
||||
"bit_field",
|
||||
"bit_set",
|
||||
"map",
|
||||
"dynamic",
|
||||
"auto_cast",
|
||||
"cast",
|
||||
"transmute",
|
||||
"distinct",
|
||||
"opaque",
|
||||
"using",
|
||||
"inline",
|
||||
"no_inline",
|
||||
"context",
|
||||
"size_of",
|
||||
"align_of",
|
||||
"offset_of",
|
||||
"type_of",
|
||||
"const",
|
||||
"",
|
||||
};
|
||||
|
||||
custom_keyword_tokens: []string;
|
||||
|
||||
to_string :: proc(kind: Token_Kind) -> string {
|
||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||
return tokens[kind];
|
||||
}
|
||||
if Token_Kind.B_Custom_Keyword_Begin < kind {
|
||||
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin));
|
||||
if n < len(custom_keyword_tokens) {
|
||||
return custom_keyword_tokens[n];
|
||||
}
|
||||
}
|
||||
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
is_literal :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
|
||||
}
|
||||
is_operator :: proc(kind: Token_Kind) -> bool {
|
||||
#partial switch kind {
|
||||
case .B_Operator_Begin .. .B_Operator_End:
|
||||
return true;
|
||||
case .In, .Not_In:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq;
|
||||
}
|
||||
is_keyword :: proc(kind: Token_Kind) -> bool {
|
||||
switch {
|
||||
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
|
||||
return true;
|
||||
case Token_Kind.B_Custom_Keyword_Begin < kind:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
package odin_tokenizer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
path: string,
|
||||
src: []byte,
|
||||
err: Error_Handler,
|
||||
|
||||
// Tokenizing state
|
||||
ch: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
|
||||
// Mutable data
|
||||
error_count: int,
|
||||
}
|
||||
|
||||
init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) {
|
||||
t.src = src;
|
||||
t.err = err;
|
||||
t.ch = ' ';
|
||||
t.offset = 0;
|
||||
t.read_offset = 0;
|
||||
t.line_offset = 0;
|
||||
t.line_count = len(src) > 0 ? 1 : 0;
|
||||
t.error_count = 0;
|
||||
t.path = path;
|
||||
|
||||
advance_rune(t);
|
||||
if t.ch == utf8.RUNE_BOM {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
|
||||
line := t.line_count;
|
||||
column := offset - t.line_offset + 1;
|
||||
|
||||
return Pos {
|
||||
file = t.path,
|
||||
offset = offset,
|
||||
line = line,
|
||||
column = column,
|
||||
};
|
||||
}
|
||||
|
||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintf("\n");
|
||||
}
|
||||
|
||||
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset);
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args);
|
||||
}
|
||||
t.error_count += 1;
|
||||
}
|
||||
|
||||
advance_rune :: proc(using t: ^Tokenizer) {
|
||||
if read_offset < len(src) {
|
||||
offset = read_offset;
|
||||
if ch == '\n' {
|
||||
line_offset = offset;
|
||||
line_count += 1;
|
||||
}
|
||||
r, w := rune(src[read_offset]), 1;
|
||||
switch {
|
||||
case r == 0:
|
||||
error(t, t.offset, "illegal character NUL");
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(src[read_offset:]);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
error(t, t.offset, "illegal UTF-8 encoding");
|
||||
} else if r == utf8.RUNE_BOM && offset > 0 {
|
||||
error(t, t.offset, "illegal byte order mark");
|
||||
}
|
||||
}
|
||||
read_offset += w;
|
||||
ch = r;
|
||||
} else {
|
||||
offset = len(src);
|
||||
if ch == '\n' {
|
||||
line_offset = offset;
|
||||
line_count += 1;
|
||||
}
|
||||
ch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
||||
if t.read_offset+offset < len(t.src) {
|
||||
return t.src[t.read_offset+offset];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for t.ch == ' ' ||
|
||||
t.ch == '\t' ||
|
||||
t.ch == '\n' ||
|
||||
t.ch == '\r' {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
is_letter :: proc(r: rune) -> bool {
|
||||
if r < utf8.RUNE_SELF {
|
||||
switch r {
|
||||
case '_':
|
||||
return true;
|
||||
case 'A'..'Z', 'a'..'z':
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO(bill): Add unicode lookup tables
|
||||
return false;
|
||||
}
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
// TODO(bill): Add unicode lookup tables
|
||||
return '0' <= r && r <= '9';
|
||||
}
|
||||
|
||||
|
||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
next := -1;
|
||||
general: {
|
||||
if t.ch == '/' || t.ch == '!' { // // #! comments
|
||||
advance_rune(t);
|
||||
for t.ch != '\n' && t.ch >= 0 {
|
||||
advance_rune(t);
|
||||
}
|
||||
|
||||
next = t.offset;
|
||||
if t.ch == '\n' {
|
||||
next += 1;
|
||||
}
|
||||
break general;
|
||||
}
|
||||
|
||||
/* style comment */
|
||||
advance_rune(t);
|
||||
for t.ch >= 0 {
|
||||
ch := t.ch;
|
||||
advance_rune(t);
|
||||
if ch == '*' && t.ch == '/' {
|
||||
advance_rune(t);
|
||||
next = t.offset;
|
||||
break general;
|
||||
}
|
||||
}
|
||||
|
||||
error(t, offset, "comment not terminated");
|
||||
}
|
||||
|
||||
lit := t.src[offset : t.offset];
|
||||
|
||||
// NOTE(bill): Strip CR for line comments
|
||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
||||
lit = lit[:len(lit)-1];
|
||||
}
|
||||
|
||||
|
||||
return string(lit);
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset;
|
||||
|
||||
for is_letter(t.ch) || is_digit(t.ch) {
|
||||
advance_rune(t);
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
error(t, offset, "string literal was not terminated");
|
||||
break;
|
||||
}
|
||||
advance_rune(t);
|
||||
if ch == '"' {
|
||||
break;
|
||||
}
|
||||
if ch == '\\' {
|
||||
scan_escape(t);
|
||||
}
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_raw_string :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
error(t, offset, "raw string literal was not terminated");
|
||||
break;
|
||||
}
|
||||
advance_rune(t);
|
||||
if ch == '`' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
digit_val :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
return int(r-'0');
|
||||
case 'A'..'F':
|
||||
return int(r-'A' + 10);
|
||||
case 'a'..'f':
|
||||
return int(r-'a' + 10);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
offset := t.offset;
|
||||
|
||||
n: int;
|
||||
base, max: u32;
|
||||
switch t.ch {
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', '\\', '\'', '\"':
|
||||
advance_rune(t);
|
||||
return true;
|
||||
|
||||
case '0'..'7':
|
||||
n, base, max = 3, 8, 255;
|
||||
case 'x':
|
||||
advance_rune(t);
|
||||
n, base, max = 2, 16, 255;
|
||||
case 'u':
|
||||
advance_rune(t);
|
||||
n, base, max = 4, 16, utf8.MAX_RUNE;
|
||||
case 'U':
|
||||
advance_rune(t);
|
||||
n, base, max = 8, 16, utf8.MAX_RUNE;
|
||||
case:
|
||||
if t.ch < 0 {
|
||||
error(t, offset, "escape sequence was not terminated");
|
||||
} else {
|
||||
error(t, offset, "unknown escape sequence");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
x: u32;
|
||||
for n > 0 {
|
||||
d := u32(digit_val(t.ch));
|
||||
for d >= base {
|
||||
if t.ch < 0 {
|
||||
error(t, t.offset, "escape sequence was not terminated");
|
||||
} else {
|
||||
error(t, t.offset, "illegal character %d in escape sequence", t.ch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
x = x*base + d;
|
||||
advance_rune(t);
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
error(t, offset, "escape sequence is an invalid Unicode code point");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
valid := true;
|
||||
n := 0;
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
if valid {
|
||||
error(t, offset, "rune literal not terminated");
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
advance_rune(t);
|
||||
if ch == '\'' {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
if ch == '\\' {
|
||||
if !scan_escape(t) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if valid && n != 1 {
|
||||
error(t, offset, "illegal rune literal");
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_val(t.ch) < base || t.ch == '_' {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer, kind: ^Token_Kind) {
|
||||
if t.ch == 'e' || t.ch == 'E' {
|
||||
kind^ = .Float;
|
||||
advance_rune(t);
|
||||
if t.ch == '-' || t.ch == '+' {
|
||||
advance_rune(t);
|
||||
}
|
||||
if digit_val(t.ch) < 10 {
|
||||
scan_mantissa(t, 10);
|
||||
} else {
|
||||
error(t, t.offset, "illegal floating-point exponent");
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): This needs to be here for sanity's sake
|
||||
switch t.ch {
|
||||
case 'i', 'j', 'k':
|
||||
kind^ = .Imag;
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer, kind: ^Token_Kind) -> (early_exit: bool) {
|
||||
if t.ch == '.' && peek_byte(t) == '.' {
|
||||
return true;
|
||||
}
|
||||
if t.ch == '.' {
|
||||
kind^ = .Float;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
offset := t.offset;
|
||||
kind := Token_Kind.Integer;
|
||||
seen_point := seen_decimal_point;
|
||||
|
||||
if seen_point {
|
||||
offset -= 1;
|
||||
kind = .Float;
|
||||
scan_mantissa(t, 10);
|
||||
scan_exponent(t, &kind);
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, base);
|
||||
if t.offset - prev <= 1 {
|
||||
kind^ = .Invalid;
|
||||
error(t, t.offset, msg);
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune(t);
|
||||
switch t.ch {
|
||||
case 'b': int_base(t, &kind, 2, "illegal binary integer");
|
||||
case 'o': int_base(t, &kind, 8, "illegal octal integer");
|
||||
case 'd': int_base(t, &kind, 10, "illegal decimal integer");
|
||||
case 'z': int_base(t, &kind, 12, "illegal dozenal integer");
|
||||
case 'x': int_base(t, &kind, 16, "illegal hexadecimal integer");
|
||||
case 'h':
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 16);
|
||||
if t.offset - prev <= 1 {
|
||||
kind = .Invalid;
|
||||
error(t, t.offset, "illegal hexadecimal floating-point number");
|
||||
} else {
|
||||
sub := t.src[prev+1 : t.offset];
|
||||
digit_count := 0;
|
||||
for d in sub {
|
||||
if d != '_' {
|
||||
digit_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch digit_count {
|
||||
case 8, 16: break;
|
||||
case:
|
||||
error(t, t.offset, "invalid hexadecimal floating-point number, expected 8 or 16 digits, got %d", digit_count);
|
||||
}
|
||||
}
|
||||
|
||||
case:
|
||||
seen_point = false;
|
||||
scan_mantissa(t, 10);
|
||||
if t.ch == '.' {
|
||||
seen_point = true;
|
||||
if scan_fraction(t, &kind) {
|
||||
return kind, string(t.src[offset : t.offset]);
|
||||
}
|
||||
}
|
||||
scan_exponent(t, &kind);
|
||||
return kind, string(t.src[offset : t.offset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
if scan_fraction(t, &kind) {
|
||||
return kind, string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_exponent(t, &kind);
|
||||
|
||||
return kind, string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
}
|
||||
return tok0;
|
||||
}
|
||||
switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
}
|
||||
if t.ch == ch2 {
|
||||
advance_rune(t);
|
||||
return tok2;
|
||||
}
|
||||
return tok0;
|
||||
}
|
||||
switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
}
|
||||
if t.ch == ch2 {
|
||||
advance_rune(t);
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok3;
|
||||
}
|
||||
return tok2;
|
||||
}
|
||||
return tok0;
|
||||
}
|
||||
|
||||
|
||||
skip_whitespace(t);
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
kind: Token_Kind;
|
||||
lit: string;
|
||||
pos := offset_to_pos(t, offset);
|
||||
|
||||
switch ch := t.ch; true {
|
||||
case is_letter(ch):
|
||||
lit = scan_identifier(t);
|
||||
kind = .Ident;
|
||||
check_keyword: if len(lit) > 1 {
|
||||
// TODO(bill): Maybe have a hash table lookup rather than this linear search
|
||||
for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End {
|
||||
if lit == tokens[i] {
|
||||
kind = Token_Kind(i);
|
||||
break check_keyword;
|
||||
}
|
||||
}
|
||||
for keyword, i in custom_keyword_tokens {
|
||||
if lit == keyword {
|
||||
kind = Token_Kind(i+1) + .B_Custom_Keyword_Begin;
|
||||
break check_keyword;
|
||||
}
|
||||
}
|
||||
if kind == .Ident && lit == "notin" {
|
||||
kind = .Not_In;
|
||||
}
|
||||
}
|
||||
case '0' <= ch && ch <= '9':
|
||||
kind, lit = scan_number(t, false);
|
||||
case:
|
||||
advance_rune(t);
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = .EOF;
|
||||
case '"':
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case '\'':
|
||||
kind = .Rune;
|
||||
lit = scan_rune(t);
|
||||
case '`':
|
||||
kind = .String;
|
||||
lit = scan_raw_string(t);
|
||||
case '=':
|
||||
if t.ch == '>' {
|
||||
advance_rune(t);
|
||||
kind = .Double_Arrow_Right;
|
||||
} else {
|
||||
kind = switch2(t, .Eq, .Cmp_Eq);
|
||||
}
|
||||
case '!': kind = switch2(t, .Not, .Not_Eq);
|
||||
case '#':
|
||||
kind = .Hash;
|
||||
if t.ch == '!' {
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
}
|
||||
case '?': kind = .Question;
|
||||
case '@': kind = .At;
|
||||
case '$': kind = .Dollar;
|
||||
case '^': kind = .Pointer;
|
||||
case '+': kind = switch2(t, .Add, .Add_Eq);
|
||||
case '-':
|
||||
if t.ch == '>' {
|
||||
advance_rune(t);
|
||||
kind = .Arrow_Right;
|
||||
} else if t.ch == '-' && peek_byte(t) == '-' {
|
||||
advance_rune(t);
|
||||
advance_rune(t);
|
||||
kind = .Undef;
|
||||
} else {
|
||||
kind = switch2(t, .Sub, .Sub_Eq);
|
||||
}
|
||||
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
} else {
|
||||
kind = switch2(t, .Quo, .Quo_Eq);
|
||||
}
|
||||
case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq);
|
||||
case '&':
|
||||
if t.ch == '~' {
|
||||
advance_rune(t);
|
||||
kind = switch2(t, .And_Not, .And_Not_Eq);
|
||||
} else {
|
||||
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
|
||||
}
|
||||
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
|
||||
case '~': kind = .Xor;
|
||||
case '<':
|
||||
if t.ch == '-' {
|
||||
advance_rune(t);
|
||||
kind = .Arrow_Left;
|
||||
} else {
|
||||
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
|
||||
}
|
||||
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
|
||||
|
||||
case '.':
|
||||
if '0' <= t.ch && t.ch <= '9' {
|
||||
kind, lit = scan_number(t, true);
|
||||
} else {
|
||||
kind = .Period;
|
||||
if t.ch == '.' {
|
||||
advance_rune(t);
|
||||
kind = .Ellipsis;
|
||||
if t.ch == '<' {
|
||||
advance_rune(t);
|
||||
kind = .Range_Half;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ':': kind = .Colon;
|
||||
case ',': kind = .Comma;
|
||||
case ';': kind = .Semicolon;
|
||||
case '(': kind = .Open_Paren;
|
||||
case ')': kind = .Close_Paren;
|
||||
case '[': kind = .Open_Bracket;
|
||||
case ']': kind = .Close_Bracket;
|
||||
case '{': kind = .Open_Brace;
|
||||
case '}': kind = .Close_Brace;
|
||||
|
||||
case '\\': kind = .Back_Slash;
|
||||
|
||||
case:
|
||||
if ch != utf8.RUNE_BOM {
|
||||
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||
}
|
||||
kind = .Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset]);
|
||||
}
|
||||
return Token{kind, lit, pos};
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library lib "gl" when ODIN_OS == "linux";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#load "opengl_constants.odin";
|
||||
|
||||
Clear :: proc(mask: u32) #foreign lib "glClear";
|
||||
ClearColor :: proc(r, g, b, a: f32) #foreign lib "glClearColor";
|
||||
Begin :: proc(mode: i32) #foreign lib "glBegin";
|
||||
End :: proc() #foreign lib "glEnd";
|
||||
Finish :: proc() #foreign lib "glFinish";
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign lib "glBlendFunc";
|
||||
Enable :: proc(cap: i32) #foreign lib "glEnable";
|
||||
Disable :: proc(cap: i32) #foreign lib "glDisable";
|
||||
GenTextures :: proc(count: i32, result: ^u32) #foreign lib "glGenTextures";
|
||||
DeleteTextures:: proc(count: i32, result: ^u32) #foreign lib "glDeleteTextures";
|
||||
TexParameteri :: proc(target, pname, param: i32) #foreign lib "glTexParameteri";
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign lib "glTexParameterf";
|
||||
BindTexture :: proc(target: i32, texture: u32) #foreign lib "glBindTexture";
|
||||
LoadIdentity :: proc() #foreign lib "glLoadIdentity";
|
||||
Viewport :: proc(x, y, width, height: i32) #foreign lib "glViewport";
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho";
|
||||
Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f";
|
||||
Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f";
|
||||
GetError :: proc() -> i32 #foreign lib "glGetError";
|
||||
GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString";
|
||||
GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv";
|
||||
TexCoord2f :: proc(x, y: f32) #foreign lib "glTexCoord2f";
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
|
||||
|
||||
|
||||
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
|
||||
|
||||
_libgl := win32.LoadLibraryA(_string_data("opengl32.dll\x00"));
|
||||
|
||||
GetProcAddress :: proc(name: string) -> proc() #cc_c {
|
||||
if name[len(name)-1] == 0 {
|
||||
name = name[0..<len(name)-1];
|
||||
}
|
||||
// NOTE(bill): null terminated
|
||||
assert((&name[0] + len(name))^ == 0);
|
||||
res := wgl.GetProcAddress(&name[0]);
|
||||
if res == nil {
|
||||
res = win32.GetProcAddress(_libgl, &name[0]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
|
||||
BindVertexArray: proc(buffer: u32) #cc_c;
|
||||
BindSampler: proc(position: i32, sampler: u32) #cc_c;
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
|
||||
|
||||
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||
|
||||
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||
UnmapBuffer: proc(target: i32) #cc_c;
|
||||
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||
|
||||
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
|
||||
ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c;
|
||||
CompileShader: proc(shader: u32) #cc_c;
|
||||
CreateProgram: proc() -> u32 #cc_c;
|
||||
AttachShader: proc(program, shader: u32) #cc_c;
|
||||
DetachShader: proc(program, shader: u32) #cc_c;
|
||||
DeleteShader: proc(shader: u32) #cc_c;
|
||||
LinkProgram: proc(program: u32) #cc_c;
|
||||
UseProgram: proc(program: u32) #cc_c;
|
||||
DeleteProgram: proc(program: u32) #cc_c;
|
||||
|
||||
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
|
||||
ActiveTexture: proc(texture: i32) #cc_c;
|
||||
GenerateMipmap: proc(target: i32) #cc_c;
|
||||
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||
|
||||
|
||||
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||
|
||||
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
|
||||
|
||||
init :: proc() {
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline {
|
||||
x := ^(proc() #cc_c)(p);
|
||||
x^ = GetProcAddress(name);
|
||||
}
|
||||
|
||||
set_proc_address(&GenBuffers, "glGenBuffers\x00");
|
||||
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
|
||||
set_proc_address(&GenSamplers, "glGenSamplers\x00");
|
||||
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
|
||||
set_proc_address(&BindBuffer, "glBindBuffer\x00");
|
||||
set_proc_address(&BindSampler, "glBindSampler\x00");
|
||||
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
|
||||
set_proc_address(&BufferData, "glBufferData\x00");
|
||||
set_proc_address(&BufferSubData, "glBufferSubData\x00");
|
||||
|
||||
set_proc_address(&DrawArrays, "glDrawArrays\x00");
|
||||
set_proc_address(&DrawElements, "glDrawElements\x00");
|
||||
|
||||
set_proc_address(&MapBuffer, "glMapBuffer\x00");
|
||||
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
|
||||
|
||||
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
|
||||
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
|
||||
|
||||
set_proc_address(&CreateShader, "glCreateShader\x00");
|
||||
set_proc_address(&ShaderSource, "glShaderSource\x00");
|
||||
set_proc_address(&CompileShader, "glCompileShader\x00");
|
||||
set_proc_address(&CreateProgram, "glCreateProgram\x00");
|
||||
set_proc_address(&AttachShader, "glAttachShader\x00");
|
||||
set_proc_address(&DetachShader, "glDetachShader\x00");
|
||||
set_proc_address(&DeleteShader, "glDeleteShader\x00");
|
||||
set_proc_address(&LinkProgram, "glLinkProgram\x00");
|
||||
set_proc_address(&UseProgram, "glUseProgram\x00");
|
||||
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
|
||||
|
||||
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
|
||||
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
|
||||
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
|
||||
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
|
||||
|
||||
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
|
||||
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
|
||||
|
||||
set_proc_address(&Uniform1i, "glUniform1i\x00");
|
||||
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
|
||||
|
||||
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
|
||||
|
||||
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
|
||||
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
|
||||
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
|
||||
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
|
||||
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
|
||||
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
#load "os_windows.odin" when ODIN_OS == "windows";
|
||||
#load "os_x.odin" when ODIN_OS == "osx";
|
||||
#load "os_linux.odin" when ODIN_OS == "linux";
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
package os
|
||||
|
||||
import "core:mem"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
OS :: ODIN_OS;
|
||||
ARCH :: ODIN_ARCH;
|
||||
ENDIAN :: ODIN_ENDIAN;
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, transmute([]byte)str);
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
||||
return write(fd, []byte{b});
|
||||
}
|
||||
|
||||
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
return write_byte(fd, byte(r));
|
||||
}
|
||||
|
||||
b, n := utf8.encode_rune(r);
|
||||
return write(fd, b[:n]);
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) {
|
||||
write_byte(fd, '\'');
|
||||
|
||||
switch r {
|
||||
case '\a': write_string(fd, "\\a");
|
||||
case '\b': write_string(fd, "\\b");
|
||||
case '\e': write_string(fd, "\\e");
|
||||
case '\f': write_string(fd, "\\f");
|
||||
case '\n': write_string(fd, "\\n");
|
||||
case '\r': write_string(fd, "\\r");
|
||||
case '\t': write_string(fd, "\\t");
|
||||
case '\v': write_string(fd, "\\v");
|
||||
case:
|
||||
if r < 32 {
|
||||
write_string(fd, "\\x");
|
||||
b: [2]byte;
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil);
|
||||
switch len(s) {
|
||||
case 0: write_string(fd, "00");
|
||||
case 1: write_rune(fd, '0');
|
||||
case 2: write_string(fd, s);
|
||||
}
|
||||
} else {
|
||||
write_rune(fd, r);
|
||||
}
|
||||
}
|
||||
write_byte(fd, '\'');
|
||||
}
|
||||
|
||||
|
||||
file_size_from_path :: proc(path: string) -> i64 {
|
||||
fd, err := open(path, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return -1;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return -1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data = make([]byte, int(length));
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data);
|
||||
if read_err != ERROR_NONE {
|
||||
delete(data);
|
||||
return nil, false;
|
||||
}
|
||||
return data[:bytes_read], true;
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
|
||||
flags: int = O_WRONLY|O_CREATE;
|
||||
if truncate {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
|
||||
mode: int = 0;
|
||||
when OS == "linux" || OS == "darwin" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, err := open(name, flags, mode);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
_, write_err := write(fd, data);
|
||||
return write_err == 0;
|
||||
}
|
||||
|
||||
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return write(fd, s);
|
||||
}
|
||||
|
||||
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return read(fd, s);
|
||||
}
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
allocated_mem: rawptr;
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^;
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr));
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
|
||||
|
||||
ptr := uintptr(aligned_mem);
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
if p != nil {
|
||||
heap_free(mem.ptr_offset((^rawptr)(p), -1)^);
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
if p == nil do return nil;
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment);
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory);
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(tetra): Do nothing.
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment);
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
heap_allocator :: proc() -> mem.Allocator {
|
||||
return mem.Allocator{
|
||||
procedure = heap_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
package os
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
foreign import pthread "system:pthread"
|
||||
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import "core:c"
|
||||
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct int;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
EPERM: Errno : 1; /* Operation not permitted */
|
||||
ENOENT: Errno : 2; /* No such file or directory */
|
||||
ESRCH: Errno : 3; /* No such process */
|
||||
EINTR: Errno : 4; /* Interrupted system call */
|
||||
EIO: Errno : 5; /* Input/output error */
|
||||
ENXIO: Errno : 6; /* Device not configured */
|
||||
E2BIG: Errno : 7; /* Argument list too long */
|
||||
ENOEXEC: Errno : 8; /* Exec format error */
|
||||
EBADF: Errno : 9; /* Bad file descriptor */
|
||||
ECHILD: Errno : 10; /* No child processes */
|
||||
EDEADLK: Errno : 11; /* Resource deadlock avoided */
|
||||
ENOMEM: Errno : 12; /* Cannot allocate memory */
|
||||
EACCES: Errno : 13; /* Permission denied */
|
||||
EFAULT: Errno : 14; /* Bad address */
|
||||
ENOTBLK: Errno : 15; /* Block device required */
|
||||
EBUSY: Errno : 16; /* Device / Resource busy */
|
||||
EEXIST: Errno : 17; /* File exists */
|
||||
EXDEV: Errno : 18; /* Cross-device link */
|
||||
ENODEV: Errno : 19; /* Operation not supported by device */
|
||||
ENOTDIR: Errno : 20; /* Not a directory */
|
||||
EISDIR: Errno : 21; /* Is a directory */
|
||||
EINVAL: Errno : 22; /* Invalid argument */
|
||||
ENFILE: Errno : 23; /* Too many open files in system */
|
||||
EMFILE: Errno : 24; /* Too many open files */
|
||||
ENOTTY: Errno : 25; /* Inappropriate ioctl for device */
|
||||
ETXTBSY: Errno : 26; /* Text file busy */
|
||||
EFBIG: Errno : 27; /* File too large */
|
||||
ENOSPC: Errno : 28; /* No space left on device */
|
||||
ESPIPE: Errno : 29; /* Illegal seek */
|
||||
EROFS: Errno : 30; /* Read-only file system */
|
||||
EMLINK: Errno : 31; /* Too many links */
|
||||
EPIPE: Errno : 32; /* Broken pipe */
|
||||
|
||||
/* math software */
|
||||
EDOM: Errno : 33; /* Numerical argument out of domain */
|
||||
ERANGE: Errno : 34; /* Result too large */
|
||||
|
||||
/* non-blocking and interrupt i/o */
|
||||
EAGAIN: Errno : 35; /* Resource temporarily unavailable */
|
||||
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
|
||||
EINPROGRESS: Errno : 36; /* Operation now in progress */
|
||||
EALREADY: Errno : 37; /* Operation already in progress */
|
||||
|
||||
/* ipc/network software -- argument errors */
|
||||
ENOTSOCK: Errno : 38; /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 39; /* Destination address required */
|
||||
EMSGSIZE: Errno : 40; /* Message too long */
|
||||
EPROTOTYPE: Errno : 41; /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 42; /* Protocol not available */
|
||||
EPROTONOSUPPORT: Errno : 43; /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT: Errno : 44; /* Socket type not supported */
|
||||
ENOTSUP: Errno : 45; /* Operation not supported */
|
||||
EPFNOSUPPORT: Errno : 46; /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 47; /* Address family not supported by protocol family */
|
||||
EADDRINUSE: Errno : 48; /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 49; /* Can't assign requested address */
|
||||
|
||||
/* ipc/network software -- operational errors */
|
||||
ENETDOWN: Errno : 50; /* Network is down */
|
||||
ENETUNREACH: Errno : 51; /* Network is unreachable */
|
||||
ENETRESET: Errno : 52; /* Network dropped connection on reset */
|
||||
ECONNABORTED: Errno : 53; /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 54; /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 55; /* No buffer space available */
|
||||
EISCONN: Errno : 56; /* Socket is already connected */
|
||||
ENOTCONN: Errno : 57; /* Socket is not connected */
|
||||
ESHUTDOWN: Errno : 58; /* Can't send after socket shutdown */
|
||||
ETOOMANYREFS: Errno : 59; /* Too many references: can't splice */
|
||||
ETIMEDOUT: Errno : 60; /* Operation timed out */
|
||||
ECONNREFUSED: Errno : 61; /* Connection refused */
|
||||
|
||||
ELOOP: Errno : 62; /* Too many levels of symbolic links */
|
||||
ENAMETOOLONG: Errno : 63; /* File name too long */
|
||||
|
||||
/* should be rearranged */
|
||||
EHOSTDOWN: Errno : 64; /* Host is down */
|
||||
EHOSTUNREACH: Errno : 65; /* No route to host */
|
||||
ENOTEMPTY: Errno : 66; /* Directory not empty */
|
||||
|
||||
/* quotas & mush */
|
||||
EPROCLIM: Errno : 67; /* Too many processes */
|
||||
EUSERS: Errno : 68; /* Too many users */
|
||||
EDQUOT: Errno : 69; /* Disc quota exceeded */
|
||||
|
||||
/* Network File System */
|
||||
ESTALE: Errno : 70; /* Stale NFS file handle */
|
||||
EREMOTE: Errno : 71; /* Too many levels of remote in path */
|
||||
EBADRPC: Errno : 72; /* RPC struct is bad */
|
||||
ERPCMISMATCH: Errno : 73; /* RPC version wrong */
|
||||
EPROGUNAVAIL: Errno : 74; /* RPC prog. not avail */
|
||||
EPROGMISMATCH: Errno : 75; /* Program version wrong */
|
||||
EPROCUNAVAIL: Errno : 76; /* Bad procedure for program */
|
||||
|
||||
ENOLCK: Errno : 77; /* No locks available */
|
||||
ENOSYS: Errno : 78; /* Function not implemented */
|
||||
|
||||
EFTYPE: Errno : 79; /* Inappropriate file type or format */
|
||||
EAUTH: Errno : 80; /* Authentication error */
|
||||
ENEEDAUTH: Errno : 81; /* Need authenticator */
|
||||
|
||||
/* Intelligent device errors */
|
||||
EPWROFF: Errno : 82; /* Device power is off */
|
||||
EDEVERR: Errno : 83; /* Device error, e.g. paper out */
|
||||
EOVERFLOW: Errno : 84; /* Value too large to be stored in data type */
|
||||
|
||||
/* Program loading errors */
|
||||
EBADEXEC: Errno : 85; /* Bad executable */
|
||||
EBADARCH: Errno : 86; /* Bad CPU type in executable */
|
||||
ESHLIBVERS: Errno : 87; /* Shared library version mismatch */
|
||||
EBADMACHO: Errno : 88; /* Malformed Macho file */
|
||||
|
||||
ECANCELED: Errno : 89; /* Operation canceled */
|
||||
|
||||
EIDRM: Errno : 90; /* Identifier removed */
|
||||
ENOMSG: Errno : 91; /* No message of desired type */
|
||||
EILSEQ: Errno : 92; /* Illegal byte sequence */
|
||||
ENOATTR: Errno : 93; /* Attribute not found */
|
||||
|
||||
EBADMSG: Errno : 94; /* Bad message */
|
||||
EMULTIHOP: Errno : 95; /* Reserved */
|
||||
ENODATA: Errno : 96; /* No message available on STREAM */
|
||||
ENOLINK: Errno : 97; /* Reserved */
|
||||
ENOSR: Errno : 98; /* No STREAM resources */
|
||||
ENOSTR: Errno : 99; /* Not a STREAM */
|
||||
EPROTO: Errno : 100; /* Protocol error */
|
||||
ETIME: Errno : 101; /* STREAM ioctl timeout */
|
||||
|
||||
ENOPOLICY: Errno : 103; /* No such policy registered */
|
||||
|
||||
ENOTRECOVERABLE: Errno : 104; /* State not recoverable */
|
||||
EOWNERDEAD: Errno : 105; /* Previous owner died */
|
||||
|
||||
EQFULL: Errno : 106; /* Interface output queue is full */
|
||||
ELAST: Errno : 106; /* Must be equal largest errno */
|
||||
|
||||
O_RDONLY :: 0x0000;
|
||||
O_WRONLY :: 0x0001;
|
||||
O_RDWR :: 0x0002;
|
||||
O_CREATE :: 0x0200;
|
||||
O_EXCL :: 0x0800;
|
||||
O_NOCTTY :: 0;
|
||||
O_TRUNC :: 0x0400;
|
||||
O_NONBLOCK :: 0x0004;
|
||||
O_APPEND :: 0x0008;
|
||||
O_SYNC :: 0x0080;
|
||||
O_ASYNC :: 0x0040;
|
||||
O_CLOEXEC :: 0x1000000;
|
||||
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
Stat :: struct {
|
||||
device_id: i32, // ID of device containing file
|
||||
mode: u16, // Mode of the file
|
||||
nlink: u16, // Number of hard links
|
||||
serial: u64, // File serial number
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
rdev: i32, // Device ID, if device
|
||||
|
||||
last_access: _File_Time, // Time of last access
|
||||
modified: _File_Time, // Time of last modification
|
||||
status_change: _File_Time, // Time of last status change
|
||||
created: _File_Time, // Time of creation
|
||||
|
||||
size: i64, // Size of the file, in bytes
|
||||
blocks: i64, // Number of blocks allocated for the file
|
||||
block_size: i32, // Optimal blocksize for I/O
|
||||
flags: u32, // User-defined flags for the file
|
||||
gen_num: u32, // File generation number ..?
|
||||
_spare: i32, // RESERVED
|
||||
_reserve1,
|
||||
_reserve2: i64, // RESERVED
|
||||
};
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0o170000; // Type of file mask
|
||||
S_IFIFO :: 0o010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0o020000; // Character special
|
||||
S_IFDIR :: 0o040000; // Directory
|
||||
S_IFBLK :: 0o060000; // Block special
|
||||
S_IFREG :: 0o100000; // Regular
|
||||
S_IFLNK :: 0o120000; // Symbolic link
|
||||
S_IFSOCK :: 0o140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0o0700; // RWX mask for owner
|
||||
S_IRUSR :: 0o0400; // R for owner
|
||||
S_IWUSR :: 0o0200; // W for owner
|
||||
S_IXUSR :: 0o0100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0o0070; // RWX mask for group
|
||||
S_IRGRP :: 0o0040; // R for group
|
||||
S_IWGRP :: 0o0020; // W for group
|
||||
S_IXGRP :: 0o0010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0o0007; // RWX mask for other
|
||||
S_IROTH :: 0o0004; // R for other
|
||||
S_IWOTH :: 0o0002; // W for other
|
||||
S_IXOTH :: 0o0001; // X for other
|
||||
|
||||
S_ISUID :: 0o4000; // Set user id on execution
|
||||
S_ISGID :: 0o2000; // Set group id on execution
|
||||
S_ISVTX :: 0o1000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
|
||||
S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
|
||||
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
|
||||
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
|
||||
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
|
||||
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
|
||||
S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__error") __error :: proc() -> ^int ---;
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
|
||||
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
|
||||
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
|
||||
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
|
||||
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) ---;
|
||||
}
|
||||
|
||||
foreign dl {
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||
}
|
||||
|
||||
get_last_error :: proc() -> int {
|
||||
return __error()^;
|
||||
}
|
||||
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
handle := _unix_open(cstr, i32(flags), u16(mode));
|
||||
delete(cstr);
|
||||
if handle == -1 {
|
||||
return INVALID_HANDLE, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
if len(data) == 0 {
|
||||
return 0, 0;
|
||||
}
|
||||
bytes_written := _unix_write(fd, raw_data(data), 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, raw_data(data), len(data));
|
||||
if bytes_read == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_read, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
final_offset := i64(_unix_lseek(fd, int(offset), whence));
|
||||
if final_offset == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return i64(size), err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/';
|
||||
}
|
||||
|
||||
stat :: inline proc(path: string) -> (Stat, bool) {
|
||||
s: Stat;
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
defer delete(cstr);
|
||||
ret_int := _unix_stat(cstr, &s);
|
||||
return s, ret_int==0;
|
||||
}
|
||||
|
||||
access :: inline proc(path: string, mask: int) -> bool {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
defer delete(cstr);
|
||||
return _unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
heap_alloc :: inline proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return _unix_calloc(1, size);
|
||||
}
|
||||
heap_resize :: inline proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
heap_free :: inline proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name);
|
||||
defer delete(path_str);
|
||||
cstr := _unix_getenv(path_str);
|
||||
if cstr == nil {
|
||||
return "", false;
|
||||
}
|
||||
return string(cstr), true;
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
page_size := get_page_size(); // NOTE(tetra): See note in os_linux.odin/get_current_directory.
|
||||
buf := make([dynamic]u8, page_size);
|
||||
for {
|
||||
cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)));
|
||||
if cwd != nil {
|
||||
return string(cwd);
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
return "";
|
||||
}
|
||||
resize(&buf, len(buf)+page_size);
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
||||
res := _unix_chdir(cstr);
|
||||
if res == -1 do return Errno(get_last_error());
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
exit :: inline proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
tid: u64;
|
||||
// NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
|
||||
// For older versions there is `syscall(SYS_thread_selfid)`, but not really
|
||||
// the same thing apparently.
|
||||
foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int ---; }
|
||||
pthread_threadid_np(nil, &tid);
|
||||
return int(tid);
|
||||
}
|
||||
|
||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||
cstr := strings.clone_to_cstring(filename);
|
||||
defer delete(cstr);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil);
|
||||
cstr := strings.clone_to_cstring(symbol);
|
||||
defer delete(cstr);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: inline proc(handle: rawptr) -> bool {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
page_size = int(_unix_getpagesize());
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
res[i] = string(arg);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
package os
|
||||
|
||||
Handle :: distinct i32;
|
||||
Errno :: distinct i32;
|
||||
|
||||
ERROR_NONE :: Errno(0);
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
stdout: Handle;
|
||||
stderr: Handle;
|
||||
stdin: Handle;
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return nil;
|
||||
}
|
||||
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 nil;
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil do return;
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
package os
|
||||
|
||||
foreign import dl "system:dl"
|
||||
foreign import libc "system:c"
|
||||
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import "core:c"
|
||||
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct i32;
|
||||
Syscall :: distinct int;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
EPERM: Errno : 1;
|
||||
ENOENT: Errno : 2;
|
||||
ESRCH: Errno : 3;
|
||||
EINTR: Errno : 4;
|
||||
EIO: Errno : 5;
|
||||
ENXIO: Errno : 6;
|
||||
EBADF: Errno : 9;
|
||||
EAGAIN: Errno : 11;
|
||||
ENOMEM: Errno : 12;
|
||||
EACCES: Errno : 13;
|
||||
EFAULT: Errno : 14;
|
||||
EEXIST: Errno : 17;
|
||||
ENODEV: Errno : 19;
|
||||
ENOTDIR: Errno : 20;
|
||||
EISDIR: Errno : 21;
|
||||
EINVAL: Errno : 22;
|
||||
ENFILE: Errno : 23;
|
||||
EMFILE: Errno : 24;
|
||||
ETXTBSY: Errno : 26;
|
||||
EFBIG: Errno : 27;
|
||||
ENOSPC: Errno : 28;
|
||||
ESPIPE: Errno : 29;
|
||||
EROFS: Errno : 30;
|
||||
EPIPE: Errno : 32;
|
||||
|
||||
ERANGE: Errno : 34; /* Result too large */
|
||||
EDEADLK: Errno : 35; /* Resource deadlock would occur */
|
||||
ENAMETOOLONG: Errno : 36; /* File name too long */
|
||||
ENOLCK: Errno : 37; /* No record locks available */
|
||||
|
||||
ENOSYS: Errno : 38; /* Invalid system call number */
|
||||
|
||||
ENOTEMPTY: Errno : 39; /* Directory not empty */
|
||||
ELOOP: Errno : 40; /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
|
||||
ENOMSG: Errno : 42; /* No message of desired type */
|
||||
EIDRM: Errno : 43; /* Identifier removed */
|
||||
ECHRNG: Errno : 44; /* Channel number out of range */
|
||||
EL2NSYNC: Errno : 45; /* Level 2 not synchronized */
|
||||
EL3HLT: Errno : 46; /* Level 3 halted */
|
||||
EL3RST: Errno : 47; /* Level 3 reset */
|
||||
ELNRNG: Errno : 48; /* Link number out of range */
|
||||
EUNATCH: Errno : 49; /* Protocol driver not attached */
|
||||
ENOCSI: Errno : 50; /* No CSI structure available */
|
||||
EL2HLT: Errno : 51; /* Level 2 halted */
|
||||
EBADE: Errno : 52; /* Invalid exchange */
|
||||
EBADR: Errno : 53; /* Invalid request descriptor */
|
||||
EXFULL: Errno : 54; /* Exchange full */
|
||||
ENOANO: Errno : 55; /* No anode */
|
||||
EBADRQC: Errno : 56; /* Invalid request code */
|
||||
EBADSLT: Errno : 57; /* Invalid slot */
|
||||
EDEADLOCK: Errno : EDEADLK;
|
||||
EBFONT: Errno : 59; /* Bad font file format */
|
||||
ENOSTR: Errno : 60; /* Device not a stream */
|
||||
ENODATA: Errno : 61; /* No data available */
|
||||
ETIME: Errno : 62; /* Timer expired */
|
||||
ENOSR: Errno : 63; /* Out of streams resources */
|
||||
ENONET: Errno : 64; /* Machine is not on the network */
|
||||
ENOPKG: Errno : 65; /* Package not installed */
|
||||
EREMOTE: Errno : 66; /* Object is remote */
|
||||
ENOLINK: Errno : 67; /* Link has been severed */
|
||||
EADV: Errno : 68; /* Advertise error */
|
||||
ESRMNT: Errno : 69; /* Srmount error */
|
||||
ECOMM: Errno : 70; /* Communication error on send */
|
||||
EPROTO: Errno : 71; /* Protocol error */
|
||||
EMULTIHOP: Errno : 72; /* Multihop attempted */
|
||||
EDOTDOT: Errno : 73; /* RFS specific error */
|
||||
EBADMSG: Errno : 74; /* Not a data message */
|
||||
EOVERFLOW: Errno : 75; /* Value too large for defined data type */
|
||||
ENOTUNIQ: Errno : 76; /* Name not unique on network */
|
||||
EBADFD: Errno : 77; /* File descriptor in bad state */
|
||||
EREMCHG: Errno : 78; /* Remote address changed */
|
||||
ELIBACC: Errno : 79; /* Can not access a needed shared library */
|
||||
ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */
|
||||
ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */
|
||||
ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */
|
||||
EILSEQ: Errno : 84; /* Illegal byte sequence */
|
||||
ERESTART: Errno : 85; /* Interrupted system call should be restarted */
|
||||
ESTRPIPE: Errno : 86; /* Streams pipe error */
|
||||
EUSERS: Errno : 87; /* Too many users */
|
||||
ENOTSOCK: Errno : 88; /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 89; /* Destination address required */
|
||||
EMSGSIZE: Errno : 90; /* Message too long */
|
||||
EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 92; /* Protocol not available */
|
||||
EPROTONOSUPPORT:Errno : 93; /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT:Errno : 94; /* Socket type not supported */
|
||||
EOPNOTSUPP: Errno : 95; /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT: Errno : 96; /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 97; /* Address family not supported by protocol */
|
||||
EADDRINUSE: Errno : 98; /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 99; /* Cannot assign requested address */
|
||||
ENETDOWN: Errno : 100; /* Network is down */
|
||||
ENETUNREACH: Errno : 101; /* Network is unreachable */
|
||||
ENETRESET: Errno : 102; /* Network dropped connection because of reset */
|
||||
ECONNABORTED: Errno : 103; /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 104; /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 105; /* No buffer space available */
|
||||
EISCONN: Errno : 106; /* Transport endpoint is already connected */
|
||||
ENOTCONN: Errno : 107; /* Transport endpoint is not connected */
|
||||
ESHUTDOWN: Errno : 108; /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS: Errno : 109; /* Too many references: cannot splice */
|
||||
ETIMEDOUT: Errno : 110; /* Connection timed out */
|
||||
ECONNREFUSED: Errno : 111; /* Connection refused */
|
||||
EHOSTDOWN: Errno : 112; /* Host is down */
|
||||
EHOSTUNREACH: Errno : 113; /* No route to host */
|
||||
EALREADY: Errno : 114; /* Operation already in progress */
|
||||
EINPROGRESS: Errno : 115; /* Operation now in progress */
|
||||
ESTALE: Errno : 116; /* Stale file handle */
|
||||
EUCLEAN: Errno : 117; /* Structure needs cleaning */
|
||||
ENOTNAM: Errno : 118; /* Not a XENIX named type file */
|
||||
ENAVAIL: Errno : 119; /* No XENIX semaphores available */
|
||||
EISNAM: Errno : 120; /* Is a named type file */
|
||||
EREMOTEIO: Errno : 121; /* Remote I/O error */
|
||||
EDQUOT: Errno : 122; /* Quota exceeded */
|
||||
|
||||
ENOMEDIUM: Errno : 123; /* No medium found */
|
||||
EMEDIUMTYPE: Errno : 124; /* Wrong medium type */
|
||||
ECANCELED: Errno : 125; /* Operation Canceled */
|
||||
ENOKEY: Errno : 126; /* Required key not available */
|
||||
EKEYEXPIRED: Errno : 127; /* Key has expired */
|
||||
EKEYREVOKED: Errno : 128; /* Key has been revoked */
|
||||
EKEYREJECTED: Errno : 129; /* Key was rejected by service */
|
||||
|
||||
/* for robust mutexes */
|
||||
EOWNERDEAD: Errno : 130; /* Owner died */
|
||||
ENOTRECOVERABLE: Errno : 131; /* State not recoverable */
|
||||
|
||||
ERFKILL: Errno : 132; /* Operation not possible due to RF-kill */
|
||||
|
||||
EHWPOISON: Errno : 133; /* Memory page has hardware error */
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001;
|
||||
RTLD_NOW :: 0x002;
|
||||
RTLD_BINDING_MASK :: 0x3;
|
||||
RTLD_GLOBAL :: 0x100;
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
Stat :: struct {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u64, // Number of hard links
|
||||
mode: u32, // Mode of the file
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
_padding: i32, // 32 bits of padding
|
||||
rdev: u64, // Device ID, if device
|
||||
size: i64, // Size of the file, in bytes
|
||||
block_size: i64, // Optimal bllocksize for I/O
|
||||
blocks: i64, // Number of 512-byte blocks allocated
|
||||
|
||||
last_access: File_Time, // Time of last access
|
||||
status_change: File_Time, // Time of last status change
|
||||
modified: File_Time, // Time of last modification
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
};
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0o170000; // Type of file mask
|
||||
S_IFIFO :: 0o010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0o020000; // Character special
|
||||
S_IFDIR :: 0o040000; // Directory
|
||||
S_IFBLK :: 0o060000; // Block special
|
||||
S_IFREG :: 0o100000; // Regular
|
||||
S_IFLNK :: 0o120000; // Symbolic link
|
||||
S_IFSOCK :: 0o140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0o0700; // RWX mask for owner
|
||||
S_IRUSR :: 0o0400; // R for owner
|
||||
S_IWUSR :: 0o0200; // W for owner
|
||||
S_IXUSR :: 0o0100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0o0070; // RWX mask for group
|
||||
S_IRGRP :: 0o0040; // R for group
|
||||
S_IWGRP :: 0o0020; // W for group
|
||||
S_IXGRP :: 0o0010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0o0007; // RWX mask for other
|
||||
S_IROTH :: 0o0004; // R for other
|
||||
S_IWOTH :: 0o0002; // W for other
|
||||
S_IXOTH :: 0o0001; // X for other
|
||||
|
||||
S_ISUID :: 0o4000; // Set user id on execution
|
||||
S_ISGID :: 0o2000; // Set group id on execution
|
||||
S_ISVTX :: 0o1000; // Directory restrcted delete
|
||||
|
||||
|
||||
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
|
||||
S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
|
||||
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
|
||||
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
|
||||
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
|
||||
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
|
||||
S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
|
||||
|
||||
F_OK :: 0; // Test for file existance
|
||||
X_OK :: 1; // Test for execute permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
R_OK :: 4; // Test for read permission
|
||||
|
||||
SYS_GETTID: Syscall : 186;
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---;
|
||||
@(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^Stat) -> c.int ---;
|
||||
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> c.int ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---;
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---;
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---;
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
|
||||
}
|
||||
foreign dl {
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||
}
|
||||
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/';
|
||||
}
|
||||
|
||||
get_last_error :: proc() -> int {
|
||||
return __errno_location()^;
|
||||
}
|
||||
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode));
|
||||
delete(cstr);
|
||||
if handle == -1 {
|
||||
return INVALID_HANDLE, Errno(get_last_error());
|
||||
}
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
result := _unix_close(fd);
|
||||
if result == -1 {
|
||||
return Errno(get_last_error());
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
|
||||
if bytes_read == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
return int(bytes_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
|
||||
if bytes_written == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
return int(bytes_written), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, c.int(whence));
|
||||
if res == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
}
|
||||
return res, ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
s, err := fstat(fd);
|
||||
if err != ERROR_NONE {
|
||||
return -1, err;
|
||||
}
|
||||
return s.size, ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
|
||||
stdin: Handle = 0;
|
||||
stdout: Handle = 1;
|
||||
stderr: Handle = 2;
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
s, err := fstat(fd);
|
||||
if err != ERROR_NONE {
|
||||
return 0, err;
|
||||
}
|
||||
return File_Time(s.modified), ERROR_NONE;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||
s, err := stat(name);
|
||||
if err != ERROR_NONE {
|
||||
return 0, err;
|
||||
}
|
||||
return File_Time(s.modified), ERROR_NONE;
|
||||
}
|
||||
|
||||
stat :: inline proc(path: string) -> (Stat, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
defer delete(cstr);
|
||||
|
||||
s: Stat;
|
||||
result := _unix_stat(cstr, &s);
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error());
|
||||
}
|
||||
return s, ERROR_NONE;
|
||||
}
|
||||
|
||||
fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
|
||||
s: Stat;
|
||||
result := _unix_fstat(fd, &s);
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error());
|
||||
}
|
||||
return s, ERROR_NONE;
|
||||
}
|
||||
|
||||
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
||||
cstr := strings.clone_to_cstring(path);
|
||||
defer delete(cstr);
|
||||
result := _unix_access(cstr, c.int(mask));
|
||||
if result == -1 {
|
||||
return false, Errno(get_last_error());
|
||||
}
|
||||
return true, ERROR_NONE;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0);
|
||||
return _unix_calloc(1, c.size_t(size));
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, c.size_t(new_size));
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name);
|
||||
defer delete(path_str);
|
||||
cstr := _unix_getenv(path_str);
|
||||
if cstr == nil {
|
||||
return "", false;
|
||||
}
|
||||
return string(cstr), true;
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
// The largest value I could find was 4096, so might as well use the page size.
|
||||
page_size := get_page_size();
|
||||
buf := make([dynamic]u8, page_size);
|
||||
for {
|
||||
cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
|
||||
if cwd != nil {
|
||||
return string(cwd);
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
return "";
|
||||
}
|
||||
resize(&buf, len(buf)+page_size);
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
||||
res := _unix_chdir(cstr);
|
||||
if res == -1 do return Errno(get_last_error());
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
_unix_exit(c.int(code));
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return syscall(SYS_GETTID);
|
||||
}
|
||||
|
||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||
cstr := strings.clone_to_cstring(filename);
|
||||
defer delete(cstr);
|
||||
handle := _unix_dlopen(cstr, c.int(flags));
|
||||
return handle;
|
||||
}
|
||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil);
|
||||
cstr := strings.clone_to_cstring(symbol);
|
||||
defer delete(cstr);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: inline proc(handle: rawptr) -> bool {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
page_size = int(_unix_getpagesize());
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
res[i] = string(arg);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
// +build windows
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:intrinsics"
|
||||
|
||||
Handle :: distinct uintptr;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct int;
|
||||
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_INVALID_HANDLE: Errno : 6;
|
||||
ERROR_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();
|
||||
|
||||
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/' || r == '\\';
|
||||
}
|
||||
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
|
||||
access: u32;
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= win32.FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_APPEND_DATA;
|
||||
}
|
||||
if mode&O_CREATE != 0 {
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
|
||||
sa: ^win32.SECURITY_ATTRIBUTES = nil;
|
||||
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
switch {
|
||||
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREATE == O_CREATE:
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path);
|
||||
handle := Handle(win32.CreateFileW(auto_cast wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE do return handle, ERROR_NONE;
|
||||
|
||||
err := Errno(win32.GetLastError());
|
||||
return INVALID_HANDLE, err;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
||||
return Errno(win32.GetLastError());
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_write_length: win32.DWORD;
|
||||
total_write: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write;
|
||||
MAX :: 1<<31-1;
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX));
|
||||
|
||||
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil);
|
||||
if single_write_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError());
|
||||
return int(total_write), err;
|
||||
}
|
||||
total_write += i64(single_write_length);
|
||||
}
|
||||
return int(total_write), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_read_length: win32.DWORD;
|
||||
total_read: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
MAX :: 1<<32-1;
|
||||
to_read := win32.DWORD(min(u32(remaining), MAX));
|
||||
|
||||
e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError());
|
||||
return int(total_read), err;
|
||||
}
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
return int(total_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
w: u32;
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN;
|
||||
case 1: w = win32.FILE_CURRENT;
|
||||
case 2: w = win32.FILE_END;
|
||||
}
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.GetFileType(win32.HANDLE(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE do return 0, ERROR_FILE_IS_PIPE;
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.GetLastError());
|
||||
return 0, err;
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
length: win32.LARGE_INTEGER;
|
||||
err: Errno;
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = Errno(win32.GetLastError());
|
||||
}
|
||||
return i64(length), err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE));
|
||||
stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE));
|
||||
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE));
|
||||
|
||||
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h));
|
||||
when size_of(uintptr) == 8 {
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION;
|
||||
if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) {
|
||||
return 0, Errno(win32.GetLastError());
|
||||
}
|
||||
lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime);
|
||||
hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime);
|
||||
return lo | hi << 32, ERROR_NONE;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||
data: win32.WIN32_FILE_ATTRIBUTE_DATA;
|
||||
|
||||
wide_path := win32.utf8_to_wstring(name);
|
||||
if !win32.GetFileAttributesExW(auto_cast wide_path, win32.GetFileExInfoStandard, &data) {
|
||||
return 0, Errno(win32.GetLastError());
|
||||
}
|
||||
|
||||
l := File_Time(data.ftLastWriteTime.dwLowDateTime);
|
||||
h := File_Time(data.ftLastWriteTime.dwHighDateTime);
|
||||
return l | h << 32, ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(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.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size));
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil do return;
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
info: win32.SYSTEM_INFO;
|
||||
win32.GetSystemInfo(&info);
|
||||
page_size = int(info.dwPageSize);
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName;
|
||||
// The current directory is stored as a global variable in the process.
|
||||
@private cwd_gate := false;
|
||||
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
for intrinsics.atomic_xchg(&cwd_gate, true) {}
|
||||
|
||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil);
|
||||
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
|
||||
|
||||
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), auto_cast &dir_buf_wstr[0]);
|
||||
assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL.
|
||||
|
||||
intrinsics.atomic_store(&cwd_gate, false);
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator);
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
wstr := win32.utf8_to_wstring(path);
|
||||
|
||||
for intrinsics.atomic_xchg(&cwd_gate, true) {}
|
||||
defer intrinsics.atomic_store(&cwd_gate, false);
|
||||
|
||||
res := win32.SetCurrentDirectoryW(auto_cast wstr);
|
||||
if !res do return Errno(win32.GetLastError());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
win32.ExitProcess(win32.DWORD(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return int(win32.GetCurrentThreadId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count);
|
||||
arg_list := make([]string, int(arg_count));
|
||||
for _, i in arg_list {
|
||||
wc_str := (^win32.wstring)(uintptr(arg_list_ptr) + size_of(win32.wstring)*uintptr(i))^;
|
||||
olen := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1,
|
||||
nil, 0, nil, nil);
|
||||
|
||||
buf := make([]byte, int(olen));
|
||||
n := win32.WideCharToMultiByte(win32.CP_UTF8, 0, wc_str, -1,
|
||||
raw_data(buf), olen, nil, nil);
|
||||
if n > 0 {
|
||||
n -= 1;
|
||||
}
|
||||
arg_list[i] = string(buf[:n]);
|
||||
}
|
||||
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
get_windows_version_ansi :: proc() -> win32.OSVERSIONINFOEXW {
|
||||
osvi : win32.OSVERSIONINFOEXW;
|
||||
osvi.os_version_info_size = size_of(win32.OSVERSIONINFOEXW);
|
||||
win32.GetVersionExW(&osvi);
|
||||
return osvi;
|
||||
}
|
||||
|
||||
is_windows_xp :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 5 && osvi.minor_version == 1);
|
||||
}
|
||||
|
||||
is_windows_vista :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 6 && osvi.minor_version == 0);
|
||||
}
|
||||
|
||||
is_windows_7 :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 6 && osvi.minor_version == 1);
|
||||
}
|
||||
|
||||
is_windows_8 :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 6 && osvi.minor_version == 2);
|
||||
}
|
||||
|
||||
is_windows_8_1 :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 6 && osvi.minor_version == 3);
|
||||
}
|
||||
|
||||
is_windows_10 :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 10 && osvi.minor_version == 0);
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
File_Time :: u64;
|
||||
Errno :: i32;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001;
|
||||
RTLD_NOW :: 0x002;
|
||||
RTLD_BINDING_MASK :: 0x3;
|
||||
RTLD_GLOBAL :: 0x100;
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
_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;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
_unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
_unix_close :: proc(fd: Handle) -> i32 #foreign libc "close";
|
||||
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "read";
|
||||
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "write";
|
||||
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #foreign libc "lseek64";
|
||||
_unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #foreign libc "stat";
|
||||
_unix_access :: proc(path: ^u8, mask: int) -> i32 #foreign libc "access";
|
||||
|
||||
_unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
_unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
_unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
_unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
_unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
_unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := _unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_read(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_write(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, i32(whence));
|
||||
return res, 0;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
fd: Handle;
|
||||
err: Errno;
|
||||
size: i64;
|
||||
|
||||
fd, err = open_simple(name, O_RDONLY);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to open file.");
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
// We have a file
|
||||
size, err = seek(fd, 0, SEEK_END);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to end of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
_, err = seek(fd, 0, SEEK_SET);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to beginning of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
// We have a file size!
|
||||
|
||||
data := make([]u8, size+1);
|
||||
if data == nil {
|
||||
fmt.println("Failed to allocate file buffer.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
read(fd, data);
|
||||
data[size] = 0;
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return _unix_malloc(size);
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = _unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(_unix_dlerror());
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
// TODO(bill):
|
||||
return nil;
|
||||
}
|
||||
@@ -1,353 +0,0 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import fmt "fmt.odin";
|
||||
|
||||
Handle :: int;
|
||||
File_Time :: u64;
|
||||
Errno :: int;
|
||||
|
||||
INVALID_HANDLE: Handle : -1;
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_NO_MORE_FILES: Errno : 18;
|
||||
ERROR_HANDLE_EOF: Errno : 38;
|
||||
ERROR_NETNAME_DELETED: Errno : 64;
|
||||
ERROR_FILE_EXISTS: Errno : 80;
|
||||
ERROR_BROKEN_PIPE: Errno : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
ERROR_MORE_DATA: Errno : 234;
|
||||
ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
ERROR_IO_PENDING: Errno : 997;
|
||||
ERROR_NOT_FOUND: Errno : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
WSAEACCES: Errno : 10013;
|
||||
WSAECONNRESET: Errno : 10054;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
access: u32;
|
||||
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= win32.FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_APPEND_DATA;
|
||||
}
|
||||
|
||||
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
|
||||
sa: ^win32.Security_Attributes = nil;
|
||||
sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
match {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
default:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
|
||||
buf: [300]byte;
|
||||
copy(buf[..], []byte(path));
|
||||
|
||||
handle := Handle(win32.CreateFileA(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
err := win32.GetLastError();
|
||||
return INVALID_HANDLE, Errno(err);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.CloseHandle(win32.Handle(fd));
|
||||
}
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, []byte(str));
|
||||
}
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_written: i32;
|
||||
e := win32.WriteFile(win32.Handle(fd), &data[0], i32(len(data)), &bytes_written, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return int(bytes_written), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_read: i32;
|
||||
e := win32.ReadFile(win32.Handle(fd), &data[0], u32(len(data)), &bytes_read, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return int(bytes_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.GetFileType(win32.Handle(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
dw_ptr := win32.SetFilePointer(win32.Handle(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := win32.GetLastError();
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
// 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.GetStdHandle(i32(h));
|
||||
win32.SetHandleInformation(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.GetFileInformationByHandle(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]byte;
|
||||
|
||||
assert(len(buf) > len(name));
|
||||
|
||||
copy(buf[..], []byte(name));
|
||||
|
||||
if win32.GetFileAttributesExA(&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;
|
||||
}
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte;
|
||||
copy(buf[..], []byte(name));
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if ok := win32.GetFileSizeEx(win32.Handle(fd), &length) != 0; !ok {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data := make([]byte, length);
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = u32(remaining);
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size);
|
||||
}
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.ExitProcess(u32(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
return int(win32.GetCurrentThreadId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
|
||||
wstr_len := 0;
|
||||
for (wstr+wstr_len)^ != 0 {
|
||||
wstr_len++;
|
||||
}
|
||||
len := 2*wstr_len-1;
|
||||
buf := make([]byte, len+1);
|
||||
str := slice_ptr(wstr, wstr_len+1);
|
||||
|
||||
i, j := 0, 0;
|
||||
for str[j] != 0 {
|
||||
match {
|
||||
case str[j] < 0x80:
|
||||
if i+1 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = byte(str[j]); i++;
|
||||
j++;
|
||||
case str[j] < 0x800:
|
||||
if i+2 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = byte(0xc0 + (str[j]>>6)); i++;
|
||||
buf[i] = byte(0x80 + (str[j]&0x3f)); i++;
|
||||
j++;
|
||||
case 0xd800 <= str[j] && str[j] < 0xdc00:
|
||||
if i+4 > len {
|
||||
return "";
|
||||
}
|
||||
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
|
||||
buf[i] = byte(0xf0 + (c >> 18)); i++;
|
||||
buf[i] = byte(0x80 + ((c >> 12) & 0x3f)); i++;
|
||||
buf[i] = byte(0x80 + ((c >> 6) & 0x3f)); i++;
|
||||
buf[i] = byte(0x80 + ((c ) & 0x3f)); i++;
|
||||
j += 2;
|
||||
case 0xdc00 <= str[j] && str[j] < 0xe000:
|
||||
return "";
|
||||
default:
|
||||
if i+3 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = 0xe0 + byte (str[j] >> 12); i++;
|
||||
buf[i] = 0x80 + byte((str[j] >> 6) & 0x3f); i++;
|
||||
buf[i] = 0x80 + byte((str[j] ) & 0x3f); i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[0..<i]);
|
||||
}
|
||||
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count);
|
||||
arg_list := make([]string, arg_count);
|
||||
for _, i in arg_list {
|
||||
arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
|
||||
}
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
|
||||
-312
@@ -1,312 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
File_Time :: u64;
|
||||
Errno :: int;
|
||||
|
||||
// TODO(zangent): Find out how to make this work on x64 and x32.
|
||||
AddressSize :: i64;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
args: [dynamic]string;
|
||||
|
||||
FileTime :: struct #ordered {
|
||||
seconds: i64,
|
||||
nanoseconds: i64
|
||||
}
|
||||
|
||||
Stat :: struct #ordered {
|
||||
device_id : i32, // ID of device containing file
|
||||
mode : u16, // Mode of the file
|
||||
nlink : u16, // Number of hard links
|
||||
serial : u64, // File serial number
|
||||
uid : u32, // User ID of the file's owner
|
||||
gid : u32, // Group ID of the file's group
|
||||
rdev : i32, // Device ID, if device
|
||||
|
||||
last_access : FileTime, // Time of last access
|
||||
modified : FileTime, // Time of last modification
|
||||
status_change : FileTime, // Time of last status change
|
||||
created : FileTime, // Time of creation
|
||||
|
||||
size : i64, // Size of the file, in bytes
|
||||
blocks : i64, // Number of blocks allocated for the file
|
||||
block_size: i32, // Optimal blocksize for I/O
|
||||
flags : u32, // User-defined flags for the file
|
||||
gen_num : u32, // File generation number ...?
|
||||
_spare : i32, // RESERVED
|
||||
_reserve1,
|
||||
_reserve2 : i64, // RESERVED
|
||||
};
|
||||
|
||||
// File type
|
||||
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
|
||||
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
unix_close :: proc(handle: Handle) #foreign libc "close";
|
||||
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read";
|
||||
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write";
|
||||
unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek";
|
||||
unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat";
|
||||
unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access";
|
||||
|
||||
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_written := unix_write(fd, &data[0], len(data));
|
||||
if(bytes_written == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_written, 0;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_read := unix_read(fd, &data[0], len(data));
|
||||
if(bytes_read == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_read, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
final_offset := unix_lseek(fd, offset, whence);
|
||||
if(final_offset == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
|
||||
handle, err := open_simple(name, O_RDONLY);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to open file.");
|
||||
return nil, false;
|
||||
}
|
||||
defer(close(handle));
|
||||
|
||||
// We have a file!
|
||||
|
||||
size: AddressSize;
|
||||
size, err = seek(handle, 0, SEEK_END);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to end of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
_, err = seek(handle, 0, SEEK_SET);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to beginning of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
// We have a file size!
|
||||
|
||||
data := make([]u8, size+1);
|
||||
if data == nil {
|
||||
fmt.println("Failed to allocate file buffer.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
read(handle, data);
|
||||
data[size] = 0;
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr #inline {
|
||||
assert(size > 0);
|
||||
return unix_malloc(size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
|
||||
return unix_realloc(ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) #inline {
|
||||
unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) #inline {
|
||||
unix_exit(code);
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return cast(int) unix_gettid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(unix_dlerror());
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package path
|
||||
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
// returns everything preceding the last path element
|
||||
dir :: proc(path: string, new := false, allocator := context.allocator) -> string {
|
||||
if path == "" do return "";
|
||||
|
||||
for i := len(path) - 1; i >= 0; i -= 1 {
|
||||
if path[i] == '/' || path[i] == '\\' {
|
||||
if path[:i] == "" {
|
||||
// path is root
|
||||
return new ? strings.clone(SEPARATOR_STRING, allocator) : SEPARATOR_STRING;
|
||||
} else {
|
||||
return new ? strings.clone(path[:i], allocator) : path[:i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// path doesn't contain any folder structure
|
||||
return "";
|
||||
}
|
||||
|
||||
// returns the final path element
|
||||
base :: proc(path: string, new := false, allocator := context.allocator) -> string {
|
||||
if path == "" do return "";
|
||||
|
||||
end := len(path) - 1;
|
||||
|
||||
for i := end; i >= 0; i -= 1 {
|
||||
switch path[i] {
|
||||
case '/', '\\':
|
||||
if i != end {
|
||||
return new ? strings.clone(path[i+1:], allocator) : path[i+1:];
|
||||
} else {
|
||||
end = i; // we don't want trailing slashes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// path doesn't contain any folder structure, return entire path
|
||||
return new ? strings.clone(path, allocator) : path;
|
||||
}
|
||||
|
||||
// returns the final path element, excluding the file extension if there is one
|
||||
name :: proc(path: string, new := false, allocator := context.allocator) -> string {
|
||||
if path == "" do return "";
|
||||
|
||||
end := len(path) - 1;
|
||||
dot := end;
|
||||
|
||||
for i := end; i >= 0; i -= 1 {
|
||||
switch path[i] {
|
||||
case '.': dot = (dot == end ? i : dot);
|
||||
case '/', '\\': return new ? strings.clone(path[i+1:dot], allocator) : path[i+1:dot];
|
||||
}
|
||||
}
|
||||
|
||||
// path doesn't contain any folder structure or file extensions; assumed to be a valid file name
|
||||
return new ? strings.clone(path, allocator) : path;
|
||||
}
|
||||
|
||||
// returns the file extension, if there is one
|
||||
ext :: proc(path: string, new := false, allocator := context.allocator) -> string {
|
||||
if path == "" do return "";
|
||||
|
||||
for i := len(path)-1; i >= 0; i -= 1 {
|
||||
switch path[i] {
|
||||
case '/', '\\': return "";
|
||||
case '.': return new ? strings.clone(path[i+1:], allocator) : path[i+1:];
|
||||
}
|
||||
}
|
||||
|
||||
// path does not include a file extension
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
rel :: proc{rel_between, rel_current};
|
||||
|
||||
// returns the relative path from one path to another
|
||||
rel_between :: proc(from, to: string, allocator := context.allocator) -> string {
|
||||
if from == "" || to == "" do return "";
|
||||
|
||||
from, to := from, to;
|
||||
from = full(from, context.temp_allocator);
|
||||
to = full(to, context.temp_allocator);
|
||||
|
||||
from_is_dir := is_dir(from);
|
||||
to_is_dir := is_dir(to);
|
||||
|
||||
index, slash := 0, 0;
|
||||
|
||||
for {
|
||||
if index >= len(from) {
|
||||
if index >= len(to) || (from_is_dir && index < len(to) && (to[index] == '/' || to[index] == '\\')) {
|
||||
slash = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if index >= len(to) {
|
||||
if index >= len(from) || (to_is_dir && index < len(from) && (from[index] == '/' || from[index] == '\\')) {
|
||||
slash = index;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
lchar, skip := utf8.decode_rune_in_string(from[index:]);
|
||||
rchar, _ := utf8.decode_rune_in_string(to[index:]);
|
||||
|
||||
if (lchar == '/' || lchar == '\\') && (rchar == '/' || lchar == '\\') {
|
||||
slash = index;
|
||||
}
|
||||
else if lchar != rchar {
|
||||
break;
|
||||
}
|
||||
|
||||
index += skip;
|
||||
}
|
||||
|
||||
if slash < 1 {
|
||||
// there is no common path, use the absolute `to` path
|
||||
return strings.clone(to, allocator);
|
||||
}
|
||||
|
||||
from_slashes, to_slashes := 0, 0;
|
||||
|
||||
if slash < len(from) {
|
||||
from = from[slash+1:];
|
||||
|
||||
if from_is_dir {
|
||||
from_slashes += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
from = "";
|
||||
}
|
||||
|
||||
if slash < len(to) {
|
||||
to = to[slash+1:];
|
||||
|
||||
if to_is_dir {
|
||||
to_slashes += 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
to = "";
|
||||
}
|
||||
|
||||
for char in from {
|
||||
if char == '/' || char == '\\' {
|
||||
from_slashes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for char in to {
|
||||
if char == '/' || char == '\\' {
|
||||
to_slashes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if from_slashes == 0 {
|
||||
buffer := make([]byte, 2 + len(to), allocator);
|
||||
|
||||
buffer[0] = '.';
|
||||
buffer[1] = SEPARATOR;
|
||||
copy(buffer[2:], to);
|
||||
|
||||
return string(buffer);
|
||||
}
|
||||
else {
|
||||
buffer := make([]byte, from_slashes*3 + len(to), allocator);
|
||||
|
||||
for i in 0..<from_slashes {
|
||||
buffer[i*3+0] = '.';
|
||||
buffer[i*3+1] = '.';
|
||||
buffer[i*3+2] = SEPARATOR;
|
||||
}
|
||||
|
||||
copy(buffer[from_slashes*3:], to);
|
||||
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// returns the relative path from the current directory to another path
|
||||
rel_current :: proc(to: string, allocator := context.allocator) -> string {
|
||||
return rel_between(current(context.allocator), to, allocator);
|
||||
}
|
||||
|
||||
|
||||
// splits the path elements into slices of the original path string
|
||||
split :: proc(s: string, allocator := context.allocator) -> []string {
|
||||
return strings.split_multi(s, []string{"\\", "/"}, true, allocator);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//+build linux, darwin
|
||||
package path
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
|
||||
MAX :: 4096; // @note(bp): apparently PATH_MAX is bullshit
|
||||
|
||||
SEPARATOR :: '/';
|
||||
SEPARATOR_STRING :: "/";
|
||||
|
||||
|
||||
@(private)
|
||||
null_term :: proc(str: string) -> string {
|
||||
for c, i in str {
|
||||
if c == '\x00' {
|
||||
return str[:i];
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
full :: proc(path: string, allocator := context.temp_allocator) -> string {
|
||||
cpath := strings.clone_to_cstring(path, context.temp_allocator);
|
||||
|
||||
foreign libc {
|
||||
realpath :: proc(path: cstring, resolved_path: ^u8) -> cstring ---;
|
||||
}
|
||||
|
||||
buf := make([dynamic]u8, MAX, MAX, allocator);
|
||||
|
||||
cstr := realpath(cpath, &buf[0]);
|
||||
for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) {
|
||||
resize(&buf, len(buf) + MAX);
|
||||
cstr = realpath(cpath, &buf[0]);
|
||||
}
|
||||
|
||||
return null_term(string(buf[:]));
|
||||
}
|
||||
|
||||
current :: proc(allocator := context.temp_allocator) -> string {
|
||||
foreign libc{
|
||||
getcwd :: proc(buf: ^u8, size: int) -> cstring ---;
|
||||
}
|
||||
|
||||
buf := make([dynamic]u8, MAX, MAX, allocator);
|
||||
|
||||
cstr := getcwd(&buf[0], len(buf));
|
||||
for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) {
|
||||
resize(&buf, len(buf) + MAX);
|
||||
cstr = getcwd(&buf[0], len(buf));
|
||||
}
|
||||
|
||||
return null_term(string(buf[:]));
|
||||
}
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
if _, ok := os.stat(path); ok {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
if stat, ok := os.stat(path); ok {
|
||||
return os.S_ISDIR(u32(stat.mode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is_file :: proc(path: string) -> bool {
|
||||
if stat, ok := os.stat(path); ok {
|
||||
return os.S_ISREG(u32(stat.mode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package path
|
||||
|
||||
import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
|
||||
SEPARATOR :: '\\';
|
||||
SEPARATOR_STRING :: "\\";
|
||||
|
||||
|
||||
@(private)
|
||||
null_term :: proc "contextless" (str: string) -> string {
|
||||
for c, i in str {
|
||||
if c == '\x00' {
|
||||
return str[:i];
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
long :: proc(path: string, allocator := context.temp_allocator) -> string {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
length := win32.GetLongPathNameW(c_path, nil, 0);
|
||||
|
||||
if length == 0 do return "";
|
||||
|
||||
buf := make([]u16, length, context.temp_allocator);
|
||||
|
||||
win32.GetLongPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
|
||||
|
||||
res := win32.utf16_to_utf8(buf[:length], allocator);
|
||||
|
||||
return null_term(res);
|
||||
}
|
||||
|
||||
short :: proc(path: string, allocator := context.temp_allocator) -> string {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
length := win32.GetShortPathNameW(c_path, nil, 0);
|
||||
|
||||
if length == 0 do return "";
|
||||
|
||||
buf := make([]u16, length, context.temp_allocator);
|
||||
|
||||
win32.GetShortPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
|
||||
|
||||
res := win32.utf16_to_utf8(buf[:length], allocator);
|
||||
|
||||
return null_term(res);
|
||||
}
|
||||
|
||||
full :: proc(path: string, allocator := context.temp_allocator) -> string {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
length := win32.GetFullPathNameW(c_path, 0, nil, nil);
|
||||
|
||||
if length == 0 do return "";
|
||||
|
||||
buf := make([]u16, length, context.temp_allocator);
|
||||
|
||||
win32.GetFullPathNameW(c_path, length, win32.LPCWSTR(&buf[0]), nil);
|
||||
|
||||
res := win32.utf16_to_utf8(buf[:length], allocator);
|
||||
|
||||
return null_term(res);
|
||||
}
|
||||
|
||||
current :: proc(allocator := context.temp_allocator) -> string {
|
||||
length := win32.GetCurrentDirectoryW(0, nil);
|
||||
|
||||
if length == 0 do return "";
|
||||
|
||||
buf := make([]u16, length, context.temp_allocator);
|
||||
|
||||
win32.GetCurrentDirectoryW(length, win32.LPCWSTR(&buf[0]));
|
||||
|
||||
res := win32.utf16_to_utf8(buf[:length], allocator);
|
||||
|
||||
return strings.trim_null(res);
|
||||
}
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(c_path);
|
||||
|
||||
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(c_path);
|
||||
|
||||
return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
is_file :: proc(path: string) -> bool {
|
||||
c_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(c_path);
|
||||
|
||||
return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
|
||||
drive :: proc(path: string, new := false, allocator := context.allocator) -> string {
|
||||
if len(path) >= 3 {
|
||||
letter := path[:2];
|
||||
|
||||
if path[1] == ':' && (path[2] == '\\' || path[2] == '/') {
|
||||
return new ? strings.clone(path[:2], allocator) : path[:2];
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
Any :: struct #ordered {
|
||||
data: rawptr,
|
||||
type_info: ^Type_Info,
|
||||
}
|
||||
|
||||
String :: struct #ordered {
|
||||
data: ^byte,
|
||||
len: int,
|
||||
};
|
||||
|
||||
Slice :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
};
|
||||
|
||||
Dynamic_Array :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: Allocator,
|
||||
};
|
||||
|
||||
Dynamic_Map :: struct #ordered {
|
||||
hashes: [dynamic]int,
|
||||
entries: Dynamic_Array,
|
||||
};
|
||||
@@ -0,0 +1,903 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
|
||||
Type_Info :: runtime.Type_Info;
|
||||
|
||||
Type_Info_Named :: runtime.Type_Info_Named;
|
||||
Type_Info_Integer :: runtime.Type_Info_Integer;
|
||||
Type_Info_Rune :: runtime.Type_Info_Rune;
|
||||
Type_Info_Float :: runtime.Type_Info_Float;
|
||||
Type_Info_Complex :: runtime.Type_Info_Complex;
|
||||
Type_Info_Quaternion :: runtime.Type_Info_Quaternion;
|
||||
Type_Info_String :: runtime.Type_Info_String;
|
||||
Type_Info_Boolean :: runtime.Type_Info_Boolean;
|
||||
Type_Info_Any :: runtime.Type_Info_Any;
|
||||
Type_Info_Type_Id :: runtime.Type_Info_Type_Id;
|
||||
Type_Info_Pointer :: runtime.Type_Info_Pointer;
|
||||
Type_Info_Procedure :: runtime.Type_Info_Procedure;
|
||||
Type_Info_Array :: runtime.Type_Info_Array;
|
||||
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array;
|
||||
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array;
|
||||
Type_Info_Slice :: runtime.Type_Info_Slice;
|
||||
Type_Info_Tuple :: runtime.Type_Info_Tuple;
|
||||
Type_Info_Struct :: runtime.Type_Info_Struct;
|
||||
Type_Info_Union :: runtime.Type_Info_Union;
|
||||
Type_Info_Enum :: runtime.Type_Info_Enum;
|
||||
Type_Info_Map :: runtime.Type_Info_Map;
|
||||
Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field;
|
||||
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set;
|
||||
Type_Info_Opaque :: runtime.Type_Info_Opaque;
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector;
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer;
|
||||
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice;
|
||||
|
||||
|
||||
Type_Kind :: enum {
|
||||
Invalid,
|
||||
|
||||
Named,
|
||||
Integer,
|
||||
Rune,
|
||||
Float,
|
||||
Complex,
|
||||
Quaternion,
|
||||
String,
|
||||
Boolean,
|
||||
Any,
|
||||
Type_Id,
|
||||
Pointer,
|
||||
Procedure,
|
||||
Array,
|
||||
Enumerated_Array,
|
||||
Dynamic_Array,
|
||||
Slice,
|
||||
Tuple,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Map,
|
||||
Bit_Field,
|
||||
Bit_Set,
|
||||
Opaque,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
}
|
||||
|
||||
|
||||
type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T);
|
||||
if ti != nil {
|
||||
switch _ in ti.variant {
|
||||
case Type_Info_Named: return .Named;
|
||||
case Type_Info_Integer: return .Integer;
|
||||
case Type_Info_Rune: return .Rune;
|
||||
case Type_Info_Float: return .Float;
|
||||
case Type_Info_Complex: return .Complex;
|
||||
case Type_Info_Quaternion: return .Quaternion;
|
||||
case Type_Info_String: return .String;
|
||||
case Type_Info_Boolean: return .Boolean;
|
||||
case Type_Info_Any: return .Any;
|
||||
case Type_Info_Type_Id: return .Type_Id;
|
||||
case Type_Info_Pointer: return .Pointer;
|
||||
case Type_Info_Procedure: return .Procedure;
|
||||
case Type_Info_Array: return .Array;
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array;
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array;
|
||||
case Type_Info_Slice: return .Slice;
|
||||
case Type_Info_Tuple: return .Tuple;
|
||||
case Type_Info_Struct: return .Struct;
|
||||
case Type_Info_Union: return .Union;
|
||||
case Type_Info_Enum: return .Enum;
|
||||
case Type_Info_Map: return .Map;
|
||||
case Type_Info_Bit_Field: return .Bit_Field;
|
||||
case Type_Info_Bit_Set: return .Bit_Set;
|
||||
case Type_Info_Opaque: return .Opaque;
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector;
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer;
|
||||
case Type_Info_Relative_Slice: return .Relative_Slice;
|
||||
}
|
||||
|
||||
}
|
||||
return .Invalid;
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_base(T));
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_core(T));
|
||||
}
|
||||
|
||||
|
||||
type_info_base :: proc(info: ^runtime.Type_Info) -> ^runtime.Type_Info {
|
||||
if info == nil do return nil;
|
||||
|
||||
base := info;
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base;
|
||||
case: break loop;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
type_info_core :: proc(info: ^runtime.Type_Info) -> ^runtime.Type_Info {
|
||||
if info == nil do return nil;
|
||||
|
||||
base := info;
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base;
|
||||
case Type_Info_Enum: base = i.base;
|
||||
case Type_Info_Opaque: base = i.elem;
|
||||
case: break loop;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
type_info_base_without_enum :: type_info_core;
|
||||
|
||||
|
||||
typeid_base :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_of(id);
|
||||
ti = type_info_base(ti);
|
||||
return ti.id;
|
||||
}
|
||||
typeid_core :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_base_without_enum(type_info_of(id));
|
||||
return ti.id;
|
||||
}
|
||||
typeid_base_without_enum :: typeid_core;
|
||||
|
||||
typeid_elem :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_of(id);
|
||||
if ti == nil do return nil;
|
||||
|
||||
bits := 8*ti.size;
|
||||
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Complex:
|
||||
switch bits {
|
||||
case 64: return f32;
|
||||
case 128: return f64;
|
||||
}
|
||||
case Type_Info_Quaternion:
|
||||
switch bits {
|
||||
case 128: return f32;
|
||||
case 256: return f64;
|
||||
}
|
||||
case Type_Info_Pointer: return v.elem.id;
|
||||
case Type_Info_Opaque: return v.elem.id;
|
||||
case Type_Info_Array: return v.elem.id;
|
||||
case Type_Info_Enumerated_Array: return v.elem.id;
|
||||
case Type_Info_Slice: return v.elem.id;
|
||||
case Type_Info_Dynamic_Array: return v.elem.id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
size_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
return ti.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
return ti.align;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
as_bytes :: proc(v: any) -> []byte {
|
||||
if v != nil {
|
||||
sz := size_of_typeid(v.id);
|
||||
return mem.slice_ptr((^byte)(v.data), sz);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
|
||||
return v.data, v.id;
|
||||
}
|
||||
|
||||
is_nil :: proc(v: any) -> bool {
|
||||
if v == nil {
|
||||
return true;
|
||||
}
|
||||
data := as_bytes(v);
|
||||
if data != nil {
|
||||
return true;
|
||||
}
|
||||
for v in data do if v != 0 {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
length :: proc(val: any) -> int {
|
||||
if val == nil do return 0;
|
||||
|
||||
#partial switch a in type_info_of(val.id).variant {
|
||||
case Type_Info_Named:
|
||||
return length({val.data, a.base.id});
|
||||
|
||||
case Type_Info_Pointer:
|
||||
return length({val.data, a.elem.id});
|
||||
|
||||
case Type_Info_Array:
|
||||
return a.count;
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
return a.count;
|
||||
|
||||
case Type_Info_Slice:
|
||||
return (^mem.Raw_Slice)(val.data).len;
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).len;
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.len;
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(val.data)^);
|
||||
} else {
|
||||
return (^mem.Raw_String)(val.data).len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
capacity :: proc(val: any) -> int {
|
||||
if val == nil do return 0;
|
||||
|
||||
#partial switch a in type_info_of(val.id).variant {
|
||||
case Type_Info_Named:
|
||||
return capacity({val.data, a.base.id});
|
||||
|
||||
case Type_Info_Pointer:
|
||||
return capacity({val.data, a.elem.id});
|
||||
|
||||
case Type_Info_Array:
|
||||
return a.count;
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
return a.count;
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).cap;
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.cap;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
if val == nil do return nil;
|
||||
|
||||
#partial switch a in type_info_of(val.id).variant {
|
||||
case Type_Info_Named:
|
||||
return index({val.data, a.base.id}, i, loc);
|
||||
|
||||
case Type_Info_Pointer:
|
||||
ptr := (^rawptr)(val.data)^;
|
||||
if ptr == nil {
|
||||
return nil;
|
||||
}
|
||||
return index({ptr, a.elem.id}, i, loc);
|
||||
|
||||
case Type_Info_Array:
|
||||
runtime.bounds_check_error_loc(loc, i, a.count);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(val.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
runtime.bounds_check_error_loc(loc, i, a.count);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(val.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case Type_Info_Slice:
|
||||
raw := (^mem.Raw_Slice)(val.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
raw := (^mem.Raw_Dynamic_Array)(val.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring do return nil;
|
||||
|
||||
raw := (^mem.Raw_String)(val.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(size_of(u8) * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, typeid_of(u8)};
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Struct_Tag :: distinct string;
|
||||
|
||||
Struct_Field :: struct {
|
||||
name: string,
|
||||
type: typeid,
|
||||
tag: Struct_Tag,
|
||||
offset: uintptr,
|
||||
}
|
||||
|
||||
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
if 0 <= i && i < len(s.names) {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
for fname, i in s.names {
|
||||
if fname == name {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any {
|
||||
if a == nil do return nil;
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
for name, i in s.names {
|
||||
if name == field {
|
||||
return any{
|
||||
rawptr(uintptr(a.data) + s.offsets[i]),
|
||||
s.types[i].id,
|
||||
};
|
||||
}
|
||||
|
||||
if recurse && s.usings[i] {
|
||||
f := any{
|
||||
rawptr(uintptr(a.data) + s.offsets[i]),
|
||||
s.types[i].id,
|
||||
};
|
||||
|
||||
if res := struct_field_value_by_name(f, field, recurse); res != nil {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct_field_names :: proc(T: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.names;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.types;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return transmute([]Struct_Tag)s.tags;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.offsets;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
|
||||
value, _ = struct_tag_lookup(tag, key);
|
||||
return;
|
||||
}
|
||||
|
||||
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
|
||||
for t := tag; t != ""; /**/ {
|
||||
i := 0;
|
||||
for i < len(t) && t[i] == ' ' { // Skip whitespace
|
||||
i += 1;
|
||||
}
|
||||
t = t[i:];
|
||||
if len(t) == 0 do break;
|
||||
|
||||
i = 0;
|
||||
loop: for i < len(t) {
|
||||
switch t[i] {
|
||||
case ':', '"':
|
||||
break loop;
|
||||
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
|
||||
break loop;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i == 0 do break;
|
||||
if i+1 >= len(t) do break;
|
||||
|
||||
if t[i] != ':' || t[i+1] != '"' {
|
||||
break;
|
||||
}
|
||||
name := string(t[:i]);
|
||||
t = t[i+1:];
|
||||
|
||||
i = 1;
|
||||
for i < len(t) && t[i] != '"' { // find closing quote
|
||||
if t[i] == '\\' do i += 1; // Skip escaped characters
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i >= len(t) do break;
|
||||
|
||||
val := string(t[:i+1]);
|
||||
t = t[i+1:];
|
||||
|
||||
if key == name {
|
||||
return Struct_Tag(val[1:i]), true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
enum_string :: proc(a: any) -> string {
|
||||
if a == nil do return "";
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
v, _ := as_i64(a);
|
||||
for value, i in e.values {
|
||||
if value == runtime.Type_Info_Enum_Value(v) {
|
||||
return e.names[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic("expected an enum to reflect.enum_string");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// Given a enum type and a value name, get the enum value.
|
||||
enum_from_name :: proc($EnumType: typeid, name: string) -> (value: EnumType, ok: bool) {
|
||||
ti := type_info_base(type_info_of(EnumType));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
for value_name, i in eti.names {
|
||||
if value_name != name do continue;
|
||||
v := eti.values[i];
|
||||
value = EnumType(v);
|
||||
ok = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
panic("expected enum type to reflect.enum_from_name");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
|
||||
id := union_variant_typeid(a);
|
||||
return type_info_of(id);
|
||||
}
|
||||
|
||||
union_variant_typeid :: proc(a: any) -> typeid {
|
||||
if a == nil do return nil;
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
||||
tag_ptr := uintptr(a.data) + info.tag_offset;
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
||||
|
||||
tag: i64 = ---;
|
||||
switch i in tag_any {
|
||||
case u8: tag = i64(i);
|
||||
case i8: tag = i64(i);
|
||||
case u16: tag = i64(i);
|
||||
case i16: tag = i64(i);
|
||||
case u32: tag = i64(i);
|
||||
case i32: tag = i64(i);
|
||||
case u64: tag = i64(i);
|
||||
case i64: tag = i64(i);
|
||||
case: unimplemented();
|
||||
}
|
||||
|
||||
if a.data != nil && tag != 0 {
|
||||
return info.variants[tag-1].id;
|
||||
}
|
||||
} else {
|
||||
panic("expected a union to reflect.union_variant_typeid");
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
as_int :: proc(a: any) -> (value: int, valid: bool) {
|
||||
v: i64;
|
||||
v, valid = as_i64(a);
|
||||
value = int(v);
|
||||
return;
|
||||
}
|
||||
|
||||
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
|
||||
v: u64;
|
||||
v, valid = as_u64(a);
|
||||
value = uint(v);
|
||||
return;
|
||||
}
|
||||
|
||||
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
|
||||
if a == nil do return;
|
||||
a := a;
|
||||
ti := runtime.type_info_core(type_info_of(a.id));
|
||||
a.id = ti.id;
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Integer:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case i8: value = i64(v);
|
||||
case i16: value = i64(v);
|
||||
case i32: value = i64(v);
|
||||
case i64: value = i64(v);
|
||||
case i128: value = i64(v);
|
||||
case int: value = i64(v);
|
||||
|
||||
case u8: value = i64(v);
|
||||
case u16: value = i64(v);
|
||||
case u32: value = i64(v);
|
||||
case u64: value = i64(v);
|
||||
case u128: value = i64(v);
|
||||
case uint: value = i64(v);
|
||||
case uintptr: value = i64(v);
|
||||
|
||||
case u16le: value = i64(v);
|
||||
case u32le: value = i64(v);
|
||||
case u64le: value = i64(v);
|
||||
case u128le: value = i64(v);
|
||||
|
||||
case i16le: value = i64(v);
|
||||
case i32le: value = i64(v);
|
||||
case i64le: value = i64(v);
|
||||
case i128le: value = i64(v);
|
||||
|
||||
case u16be: value = i64(v);
|
||||
case u32be: value = i64(v);
|
||||
case u64be: value = i64(v);
|
||||
case u128be: value = i64(v);
|
||||
|
||||
case i16be: value = i64(v);
|
||||
case i32be: value = i64(v);
|
||||
case i64be: value = i64(v);
|
||||
case i128be: value = i64(v);
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
r := a.(rune);
|
||||
value = i64(r);
|
||||
valid = true;
|
||||
|
||||
case Type_Info_Float:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case f32: value = i64(f32(v));
|
||||
case f64: value = i64(f64(v));
|
||||
case f32le: value = i64(f32(v));
|
||||
case f64le: value = i64(f64(v));
|
||||
case f32be: value = i64(f32(v));
|
||||
case f64be: value = i64(f64(v));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Boolean:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case bool: value = i64(bool(v));
|
||||
case b8: value = i64(bool(v));
|
||||
case b16: value = i64(bool(v));
|
||||
case b32: value = i64(bool(v));
|
||||
case b64: value = i64(bool(v));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
switch v in a {
|
||||
case complex64:
|
||||
if imag(v) == 0 {
|
||||
value = i64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case complex128:
|
||||
if imag(v) == 0 {
|
||||
value = i64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
switch v in a {
|
||||
case quaternion128:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = i64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case quaternion256:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = i64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
|
||||
if a == nil do return;
|
||||
a := a;
|
||||
ti := runtime.type_info_core(type_info_of(a.id));
|
||||
a.id = ti.id;
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Integer:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case i8: value = u64(v);
|
||||
case i16: value = u64(v);
|
||||
case i32: value = u64(v);
|
||||
case i64: value = u64(v);
|
||||
case i128: value = u64(v);
|
||||
case int: value = u64(v);
|
||||
|
||||
case u8: value = u64(v);
|
||||
case u16: value = u64(v);
|
||||
case u32: value = u64(v);
|
||||
case u64: value = u64(v);
|
||||
case u128: value = u64(v);
|
||||
case uint: value = u64(v);
|
||||
case uintptr:value = u64(v);
|
||||
|
||||
case u16le: value = u64(v);
|
||||
case u32le: value = u64(v);
|
||||
case u64le: value = u64(v);
|
||||
case u128le: value = u64(v);
|
||||
|
||||
case i16le: value = u64(v);
|
||||
case i32le: value = u64(v);
|
||||
case i64le: value = u64(v);
|
||||
case i128le: value = u64(v);
|
||||
|
||||
case u16be: value = u64(v);
|
||||
case u32be: value = u64(v);
|
||||
case u64be: value = u64(v);
|
||||
case u128be: value = u64(v);
|
||||
|
||||
case i16be: value = u64(v);
|
||||
case i32be: value = u64(v);
|
||||
case i64be: value = u64(v);
|
||||
case i128be: value = u64(v);
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
r := a.(rune);
|
||||
value = u64(r);
|
||||
valid = true;
|
||||
|
||||
case Type_Info_Float:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case f32: value = u64(f32(v));
|
||||
case f64: value = u64(f64(v));
|
||||
case f32le: value = u64(f32(v));
|
||||
case f64le: value = u64(f64(v));
|
||||
case f32be: value = u64(f32(v));
|
||||
case f64be: value = u64(f64(v));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Boolean:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case bool: value = u64(bool(v));
|
||||
case b8: value = u64(bool(v));
|
||||
case b16: value = u64(bool(v));
|
||||
case b32: value = u64(bool(v));
|
||||
case b64: value = u64(bool(v));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
switch v in a {
|
||||
case complex64:
|
||||
if imag(v) == 0 {
|
||||
value = u64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case complex128:
|
||||
if imag(v) == 0 {
|
||||
value = u64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
switch v in a {
|
||||
case quaternion128:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = u64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case quaternion256:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = u64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
|
||||
if a == nil do return;
|
||||
a := a;
|
||||
ti := runtime.type_info_core(type_info_of(a.id));
|
||||
a.id = ti.id;
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Integer:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case i8: value = f64(v);
|
||||
case i16: value = f64(v);
|
||||
case i32: value = f64(v);
|
||||
case i64: value = f64(v);
|
||||
case i128: value = f64(v);
|
||||
|
||||
case u8: value = f64(v);
|
||||
case u16: value = f64(v);
|
||||
case u32: value = f64(v);
|
||||
case u64: value = f64(v);
|
||||
case u128: value = f64(v);
|
||||
|
||||
case u16le: value = f64(v);
|
||||
case u32le: value = f64(v);
|
||||
case u64le: value = f64(v);
|
||||
case u128le:value = f64(v);
|
||||
|
||||
case i16le: value = f64(v);
|
||||
case i32le: value = f64(v);
|
||||
case i64le: value = f64(v);
|
||||
case i128le:value = f64(v);
|
||||
|
||||
case u16be: value = f64(v);
|
||||
case u32be: value = f64(v);
|
||||
case u64be: value = f64(v);
|
||||
case u128be:value = f64(v);
|
||||
|
||||
case i16be: value = f64(v);
|
||||
case i32be: value = f64(v);
|
||||
case i64be: value = f64(v);
|
||||
case i128be:value = f64(v);
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
r := a.(rune);
|
||||
value = f64(i32(r));
|
||||
valid = true;
|
||||
|
||||
case Type_Info_Float:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case f32: value = f64(f32(v));
|
||||
case f64: value = f64(f64(v));
|
||||
case f32le: value = f64(f32(v));
|
||||
case f64le: value = f64(f64(v));
|
||||
case f32be: value = f64(f32(v));
|
||||
case f64be: value = f64(f64(v));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Boolean:
|
||||
valid = true;
|
||||
switch v in a {
|
||||
case bool: value = f64(i32(bool(v)));
|
||||
case b8: value = f64(i32(bool(v)));
|
||||
case b16: value = f64(i32(bool(v)));
|
||||
case b32: value = f64(i32(bool(v)));
|
||||
case b64: value = f64(i32(bool(v)));
|
||||
case: valid = false;
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
switch v in a {
|
||||
case complex64:
|
||||
if imag(v) == 0 {
|
||||
value = f64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case complex128:
|
||||
if imag(v) == 0 {
|
||||
value = f64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
switch v in a {
|
||||
case quaternion128:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = f64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
case quaternion256:
|
||||
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
|
||||
value = f64(real(v));
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
package reflect
|
||||
|
||||
import "core:strings"
|
||||
|
||||
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
if a == b do return true;
|
||||
|
||||
if (a == nil && b != nil) ||
|
||||
(a != nil && b == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
switch {
|
||||
case a.size != b.size, a.align != b.align:
|
||||
return false;
|
||||
}
|
||||
|
||||
switch x in a.variant {
|
||||
case Type_Info_Named:
|
||||
y, ok := b.variant.(Type_Info_Named);
|
||||
if !ok do return false;
|
||||
return x.base == y.base;
|
||||
|
||||
case Type_Info_Integer:
|
||||
y, ok := b.variant.(Type_Info_Integer);
|
||||
if !ok do return false;
|
||||
return x.signed == y.signed && x.endianness == y.endianness;
|
||||
|
||||
case Type_Info_Rune:
|
||||
_, ok := b.variant.(Type_Info_Rune);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Float:
|
||||
_, ok := b.variant.(Type_Info_Float);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Complex:
|
||||
_, ok := b.variant.(Type_Info_Complex);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
_, ok := b.variant.(Type_Info_Quaternion);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Type_Id:
|
||||
_, ok := b.variant.(Type_Info_Type_Id);
|
||||
return ok;
|
||||
|
||||
case Type_Info_String:
|
||||
_, ok := b.variant.(Type_Info_String);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Boolean:
|
||||
_, ok := b.variant.(Type_Info_Boolean);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Any:
|
||||
_, ok := b.variant.(Type_Info_Any);
|
||||
return ok;
|
||||
|
||||
case Type_Info_Pointer:
|
||||
y, ok := b.variant.(Type_Info_Pointer);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case Type_Info_Procedure:
|
||||
y, ok := b.variant.(Type_Info_Procedure);
|
||||
if !ok do return false;
|
||||
switch {
|
||||
case x.variadic != y.variadic,
|
||||
x.convention != y.convention:
|
||||
return false;
|
||||
}
|
||||
|
||||
return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
|
||||
|
||||
case Type_Info_Array:
|
||||
y, ok := b.variant.(Type_Info_Array);
|
||||
if !ok do return false;
|
||||
if x.count != y.count do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
y, ok := b.variant.(Type_Info_Enumerated_Array);
|
||||
if !ok do return false;
|
||||
if x.count != y.count do return false;
|
||||
return are_types_identical(x.index, y.index) &&
|
||||
are_types_identical(x.elem, y.elem);
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
y, ok := b.variant.(Type_Info_Dynamic_Array);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case Type_Info_Slice:
|
||||
y, ok := b.variant.(Type_Info_Slice);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case Type_Info_Tuple:
|
||||
y, ok := b.variant.(Type_Info_Tuple);
|
||||
if !ok do return false;
|
||||
if len(x.types) != len(y.types) do return false;
|
||||
for _, i in x.types {
|
||||
xt, yt := x.types[i], y.types[i];
|
||||
if !are_types_identical(xt, yt) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Info_Struct:
|
||||
y, ok := b.variant.(Type_Info_Struct);
|
||||
if !ok do return false;
|
||||
switch {
|
||||
case len(x.types) != len(y.types),
|
||||
x.is_packed != y.is_packed,
|
||||
x.is_raw_union != y.is_raw_union,
|
||||
x.custom_align != y.custom_align,
|
||||
x.soa_kind != y.soa_kind,
|
||||
x.soa_base_type != y.soa_base_type,
|
||||
x.soa_len != y.soa_len:
|
||||
return false;
|
||||
}
|
||||
for _, i in x.types {
|
||||
xn, yn := x.names[i], y.names[i];
|
||||
xt, yt := x.types[i], y.types[i];
|
||||
xl, yl := x.tags[i], y.tags[i];
|
||||
|
||||
if xn != yn do return false;
|
||||
if !are_types_identical(xt, yt) do return false;
|
||||
if xl != yl do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Info_Union:
|
||||
y, ok := b.variant.(Type_Info_Union);
|
||||
if !ok do return false;
|
||||
if len(x.variants) != len(y.variants) do return false;
|
||||
|
||||
for _, i in x.variants {
|
||||
xv, yv := x.variants[i], y.variants[i];
|
||||
if !are_types_identical(xv, yv) do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Info_Enum:
|
||||
// NOTE(bill): Should be handled above
|
||||
return false;
|
||||
|
||||
case Type_Info_Map:
|
||||
y, ok := b.variant.(Type_Info_Map);
|
||||
if !ok do return false;
|
||||
return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
y, ok := b.variant.(Type_Info_Bit_Field);
|
||||
if !ok do return false;
|
||||
if len(x.names) != len(y.names) do return false;
|
||||
|
||||
for _, i in x.names {
|
||||
xb, yb := x.bits[i], y.bits[i];
|
||||
xo, yo := x.offsets[i], y.offsets[i];
|
||||
xn, yn := x.names[i], y.names[i];
|
||||
|
||||
if xb != yb do return false;
|
||||
if xo != yo do return false;
|
||||
if xn != yn do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
y, ok := b.variant.(Type_Info_Bit_Set);
|
||||
if !ok do return false;
|
||||
return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper;
|
||||
|
||||
case Type_Info_Opaque:
|
||||
y, ok := b.variant.(Type_Info_Opaque);
|
||||
if !ok do return false;
|
||||
return x.elem == y.elem;
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
y, ok := b.variant.(Type_Info_Simd_Vector);
|
||||
if !ok do return false;
|
||||
return x.count == y.count && x.elem == y.elem;
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
y, ok := b.variant.(Type_Info_Relative_Pointer);
|
||||
if !ok do return false;
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer;
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
y, ok := b.variant.(Type_Info_Relative_Slice);
|
||||
if !ok do return false;
|
||||
return x.base_integer == y.base_integer && x.slice == y.slice;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
is_signed :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
#partial switch i in type_info_base(info).variant {
|
||||
case Type_Info_Integer: return i.signed;
|
||||
case Type_Info_Float: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_unsigned :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
#partial switch i in type_info_base(info).variant {
|
||||
case Type_Info_Integer: return !i.signed;
|
||||
case Type_Info_Float: return false;
|
||||
}
|
||||
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_quaternion :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Quaternion);
|
||||
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_cstring :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
v, ok := type_info_base(info).variant.(Type_Info_String);
|
||||
return ok && v.is_cstring;
|
||||
}
|
||||
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_enumerated_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_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_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;
|
||||
}
|
||||
is_opaque :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Opaque);
|
||||
return ok;
|
||||
}
|
||||
is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector);
|
||||
return ok;
|
||||
}
|
||||
is_relative_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer);
|
||||
return ok;
|
||||
}
|
||||
is_relative_slice :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
|
||||
write_type(buf, type_info_of(id));
|
||||
}
|
||||
|
||||
write_type :: proc(buf: ^strings.Builder, ti: ^Type_Info) {
|
||||
using strings;
|
||||
if ti == nil {
|
||||
write_string(buf, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
write_string(buf, info.name);
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(buf, "int");
|
||||
case uint: write_string(buf, "uint");
|
||||
case uintptr: write_string(buf, "uintptr");
|
||||
case:
|
||||
write_byte(buf, 'i' if info.signed else 'u');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
switch info.endianness {
|
||||
case .Platform: // Okay
|
||||
case .Little: write_string(buf, "le");
|
||||
case .Big: write_string(buf, "be");
|
||||
}
|
||||
}
|
||||
case Type_Info_Rune:
|
||||
write_string(buf, "rune");
|
||||
case Type_Info_Float:
|
||||
write_byte(buf, 'f');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
switch info.endianness {
|
||||
case .Platform: // Okay
|
||||
case .Little: write_string(buf, "le");
|
||||
case .Big: write_string(buf, "be");
|
||||
}
|
||||
case Type_Info_Complex:
|
||||
write_string(buf, "complex");
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case Type_Info_Quaternion:
|
||||
write_string(buf, "quaternion");
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_string(buf, "cstring");
|
||||
} else {
|
||||
write_string(buf, "string");
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(buf, "bool");
|
||||
case:
|
||||
write_byte(buf, 'b');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
}
|
||||
case Type_Info_Any:
|
||||
write_string(buf, "any");
|
||||
|
||||
case Type_Info_Type_Id:
|
||||
write_string(buf, "typeid");
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(buf, "rawptr");
|
||||
} else {
|
||||
write_string(buf, "^");
|
||||
write_type(buf, info.elem);
|
||||
}
|
||||
case Type_Info_Procedure:
|
||||
write_string(buf, "proc");
|
||||
if info.params == nil {
|
||||
write_string(buf, "()");
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Tuple);
|
||||
write_string(buf, "(");
|
||||
for t, i in t.types {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, t);
|
||||
}
|
||||
write_string(buf, ")");
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string(buf, " -> ");
|
||||
write_type(buf, info.results);
|
||||
}
|
||||
case Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do write_string(buf, "(");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
|
||||
t := info.types[i];
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
}
|
||||
write_type(buf, t);
|
||||
}
|
||||
if count != 1 do write_string(buf, ")");
|
||||
|
||||
case Type_Info_Array:
|
||||
write_string(buf, "[");
|
||||
write_i64(buf, i64(info.count), 10);
|
||||
write_string(buf, "]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
write_string(buf, "[");
|
||||
write_type(buf, info.index);
|
||||
write_string(buf, "]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_string(buf, "[dynamic]");
|
||||
write_type(buf, info.elem);
|
||||
case Type_Info_Slice:
|
||||
write_string(buf, "[]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case Type_Info_Map:
|
||||
write_string(buf, "map[");
|
||||
write_type(buf, info.key);
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.value);
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
write_string(buf, "#soa[");
|
||||
write_i64(buf, i64(info.soa_len));
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
case .Slice:
|
||||
write_string(buf, "#soa[]");
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
case .Dynamic:
|
||||
write_string(buf, "#soa[dynamic]");
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
}
|
||||
|
||||
write_string(buf, "struct ");
|
||||
if info.is_packed do write_string(buf, "#packed ");
|
||||
if info.is_raw_union do write_string(buf, "#raw_union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_type(buf, info.types[i]);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
write_string(buf, "union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, variant);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case Type_Info_Enum:
|
||||
write_string(buf, "enum ");
|
||||
write_type(buf, info.base);
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
write_string(buf, "bit_field ");
|
||||
if ti.align != 1 {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_i64(buf, i64(info.bits[i]), 10);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
write_string(buf, "bit_set[");
|
||||
switch {
|
||||
case is_enum(info.elem):
|
||||
write_type(buf, info.elem);
|
||||
case is_rune(info.elem):
|
||||
write_encoded_rune(buf, rune(info.lower));
|
||||
write_string(buf, "..");
|
||||
write_encoded_rune(buf, rune(info.upper));
|
||||
case:
|
||||
write_i64(buf, info.lower, 10);
|
||||
write_string(buf, "..");
|
||||
write_i64(buf, info.upper, 10);
|
||||
}
|
||||
if info.underlying != nil {
|
||||
write_string(buf, "; ");
|
||||
write_type(buf, info.underlying);
|
||||
}
|
||||
write_byte(buf, ']');
|
||||
|
||||
case Type_Info_Opaque:
|
||||
write_string(buf, "opaque ");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
write_string(buf, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
write_string(buf, "#simd[");
|
||||
write_i64(buf, i64(info.count));
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.elem);
|
||||
}
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
write_string(buf, "#relative(");
|
||||
write_type(buf, info.base_integer);
|
||||
write_string(buf, ") ");
|
||||
write_type(buf, info.pointer);
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
write_string(buf, "#relative(");
|
||||
write_type(buf, info.base_integer);
|
||||
write_string(buf, ") ");
|
||||
write_type(buf, info.slice);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
package runtime
|
||||
|
||||
import "core:os"
|
||||
|
||||
default_allocator_proc :: os.heap_allocator_proc;
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return os.heap_allocator();
|
||||
}
|
||||
|
||||
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(allocator: ^Default_Temp_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
allocator.data = data;
|
||||
allocator.curr_offset = 0;
|
||||
allocator.prev_offset = 0;
|
||||
allocator.backup_allocator = backup_allocator;
|
||||
allocator.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(using allocator: ^Default_Temp_Allocator) {
|
||||
if allocator == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
allocator^ = {};
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
allocator := (^Default_Temp_Allocator)(allocator_data);
|
||||
|
||||
if allocator.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
a := context.allocator;
|
||||
if !(context.allocator.procedure != default_temp_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
a = default_allocator();
|
||||
}
|
||||
default_temp_allocator_init(allocator, make([]byte, DEFAULT_SCRATCH_BACKING_SIZE, a), a);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch {
|
||||
case allocator.curr_offset+size <= len(allocator.data):
|
||||
offset := align_forward_uintptr(uintptr(allocator.curr_offset), uintptr(alignment));
|
||||
ptr := &allocator.data[offset];
|
||||
mem_zero(ptr, size);
|
||||
allocator.prev_offset = int(offset);
|
||||
allocator.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(allocator.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &allocator.data[offset];
|
||||
mem_zero(ptr, size);
|
||||
allocator.prev_offset = int(offset);
|
||||
allocator.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := allocator.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
allocator.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := mem_alloc(size, alignment, a, loc);
|
||||
if allocator.leaked_allocations == nil {
|
||||
allocator.leaked_allocations = make([dynamic]rawptr, a);
|
||||
}
|
||||
append(&allocator.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
|
||||
case .Free:
|
||||
if len(allocator.data) == 0 {
|
||||
return nil;
|
||||
}
|
||||
last_ptr := rawptr(&allocator.data[allocator.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := allocator.curr_offset - allocator.prev_offset;
|
||||
allocator.curr_offset = allocator.prev_offset;
|
||||
mem_zero(last_ptr, full_size);
|
||||
return nil;
|
||||
} else {
|
||||
#no_bounds_check start, end := &allocator.data[0], &allocator.data[allocator.curr_offset];
|
||||
if start <= old_memory && old_memory < end {
|
||||
// NOTE(bill): Cannot free this pointer
|
||||
return nil;
|
||||
}
|
||||
|
||||
if len(allocator.leaked_allocations) != 0 {
|
||||
for ptr, i in allocator.leaked_allocations {
|
||||
if ptr == old_memory {
|
||||
free(ptr, allocator.backup_allocator);
|
||||
ordered_remove(&allocator.leaked_allocations, i);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE(bill): It's a temporary memory, don't worry about freeing
|
||||
|
||||
case .Free_All:
|
||||
allocator.curr_offset = 0;
|
||||
allocator.prev_offset = 0;
|
||||
for ptr in allocator.leaked_allocations {
|
||||
free(ptr, allocator.backup_allocator);
|
||||
}
|
||||
clear(&allocator.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
last_ptr := #no_bounds_check rawptr(&allocator.data[allocator.prev_offset]);
|
||||
if old_memory == last_ptr && len(allocator.data)-allocator.prev_offset >= size {
|
||||
allocator.curr_offset = allocator.prev_offset+size;
|
||||
return old_memory;
|
||||
}
|
||||
ptr := default_temp_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
mem_copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,968 @@
|
||||
package runtime
|
||||
|
||||
import "core:os"
|
||||
|
||||
bswap_16 :: proc "none" (x: u16) -> u16 {
|
||||
return x>>8 | x<<8;
|
||||
}
|
||||
|
||||
bswap_32 :: proc "none" (x: u32) -> u32 {
|
||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24;
|
||||
}
|
||||
|
||||
bswap_64 :: proc "none" (x: u64) -> u64 {
|
||||
return u64(bswap_32(u32(x))) | u64(bswap_32(u32(x>>32)));
|
||||
}
|
||||
|
||||
bswap_128 :: proc "none" (x: u128) -> u128 {
|
||||
return u128(bswap_64(u64(x))) | u128(bswap_64(u64(x>>64)));
|
||||
}
|
||||
|
||||
|
||||
bswap_f32 :: proc "none" (f: f32) -> f32 {
|
||||
x := transmute(u32)f;
|
||||
z := x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24;
|
||||
return transmute(f32)z;
|
||||
|
||||
}
|
||||
|
||||
bswap_f64 :: proc "none" (f: f64) -> f64 {
|
||||
x := transmute(u64)f;
|
||||
z := u64(bswap_32(u32(x))) | u64(bswap_32(u32(x>>32)));
|
||||
return transmute(f64)z;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
is_power_of_two_int :: inline proc(x: int) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
assert(is_power_of_two_int(align));
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
is_power_of_two_uintptr :: inline proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward_uintptr :: inline proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two_uintptr(align));
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
if data == nil do return nil;
|
||||
if len < 0 do return data;
|
||||
memset(data, 0, len);
|
||||
return data;
|
||||
}
|
||||
|
||||
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
foreign _ {
|
||||
when ODIN_USE_LLVM_API {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
}
|
||||
} else {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
}
|
||||
}
|
||||
}
|
||||
llvm_memmove(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
foreign _ {
|
||||
when ODIN_USE_LLVM_API {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
}
|
||||
} else {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
}
|
||||
}
|
||||
}
|
||||
llvm_memcpy(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
mem_alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
mem_free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
mem_free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, 0, loc);
|
||||
}
|
||||
}
|
||||
|
||||
mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
switch {
|
||||
case allocator.procedure == nil:
|
||||
return nil;
|
||||
case new_size == 0:
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
return nil;
|
||||
case ptr == nil:
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print_u64 :: proc(fd: os.Handle, x: u64) {
|
||||
digits := "0123456789";
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
b := u64(10);
|
||||
u := x;
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
|
||||
os.write(fd, a[i:]);
|
||||
}
|
||||
|
||||
print_i64 :: proc(fd: os.Handle, x: i64) {
|
||||
digits := "0123456789";
|
||||
b :: i64(10);
|
||||
|
||||
u := x;
|
||||
neg := u < 0;
|
||||
u = abs(u);
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
if neg {
|
||||
i -= 1; a[i] = '-';
|
||||
}
|
||||
|
||||
os.write(fd, a[i:]);
|
||||
}
|
||||
|
||||
print_caller_location :: proc(fd: os.Handle, using loc: Source_Code_Location) {
|
||||
os.write_string(fd, file_path);
|
||||
os.write_byte(fd, '(');
|
||||
print_u64(fd, u64(line));
|
||||
os.write_byte(fd, ':');
|
||||
print_u64(fd, u64(column));
|
||||
os.write_byte(fd, ')');
|
||||
}
|
||||
print_typeid :: proc(fd: os.Handle, id: typeid) {
|
||||
if id == nil {
|
||||
os.write_string(fd, "nil");
|
||||
} else {
|
||||
ti := type_info_of(id);
|
||||
print_type(fd, ti);
|
||||
}
|
||||
}
|
||||
print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
if ti == nil {
|
||||
os.write_string(fd, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
os.write_string(fd, info.name);
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: os.write_string(fd, "int");
|
||||
case uint: os.write_string(fd, "uint");
|
||||
case uintptr: os.write_string(fd, "uintptr");
|
||||
case:
|
||||
os.write_byte(fd, 'i' if info.signed else 'u');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
}
|
||||
case Type_Info_Rune:
|
||||
os.write_string(fd, "rune");
|
||||
case Type_Info_Float:
|
||||
os.write_byte(fd, 'f');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_Complex:
|
||||
os.write_string(fd, "complex");
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_Quaternion:
|
||||
os.write_string(fd, "quaternion");
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_String:
|
||||
os.write_string(fd, "string");
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: os.write_string(fd, "bool");
|
||||
case:
|
||||
os.write_byte(fd, 'b');
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
}
|
||||
case Type_Info_Any:
|
||||
os.write_string(fd, "any");
|
||||
case Type_Info_Type_Id:
|
||||
os.write_string(fd, "typeid");
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
os.write_string(fd, "rawptr");
|
||||
} else {
|
||||
os.write_string(fd, "^");
|
||||
print_type(fd, info.elem);
|
||||
}
|
||||
case Type_Info_Procedure:
|
||||
os.write_string(fd, "proc");
|
||||
if info.params == nil {
|
||||
os.write_string(fd, "()");
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Tuple);
|
||||
os.write_byte(fd, '(');
|
||||
for t, i in t.types {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, t);
|
||||
}
|
||||
os.write_string(fd, ")");
|
||||
}
|
||||
if info.results != nil {
|
||||
os.write_string(fd, " -> ");
|
||||
print_type(fd, info.results);
|
||||
}
|
||||
case Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do os.write_byte(fd, '(');
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
|
||||
t := info.types[i];
|
||||
|
||||
if len(name) > 0 {
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
}
|
||||
print_type(fd, t);
|
||||
}
|
||||
if count != 1 do os.write_string(fd, ")");
|
||||
|
||||
case Type_Info_Array:
|
||||
os.write_byte(fd, '[');
|
||||
print_u64(fd, u64(info.count));
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
os.write_byte(fd, '[');
|
||||
print_type(fd, info.index);
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
os.write_string(fd, "[dynamic]");
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Slice:
|
||||
os.write_string(fd, "[]");
|
||||
print_type(fd, info.elem);
|
||||
|
||||
case Type_Info_Map:
|
||||
os.write_string(fd, "map[");
|
||||
print_type(fd, info.key);
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.value);
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
os.write_string(fd, "#soa[");
|
||||
print_u64(fd, u64(info.soa_len));
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
case .Slice:
|
||||
os.write_string(fd, "#soa[]");
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
case .Dynamic:
|
||||
os.write_string(fd, "#soa[dynamic]");
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
}
|
||||
|
||||
os.write_string(fd, "struct ");
|
||||
if info.is_packed do os.write_string(fd, "#packed ");
|
||||
if info.is_raw_union do os.write_string(fd, "#raw_union ");
|
||||
if info.custom_align {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
os.write_byte(fd, ' ');
|
||||
}
|
||||
os.write_byte(fd, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
print_type(fd, info.types[i]);
|
||||
}
|
||||
os.write_byte(fd, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
os.write_string(fd, "union ");
|
||||
if info.custom_align {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
}
|
||||
if info.no_nil {
|
||||
os.write_string(fd, "#no_nil ");
|
||||
}
|
||||
os.write_byte(fd, '{');
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, variant);
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Enum:
|
||||
os.write_string(fd, "enum ");
|
||||
print_type(fd, info.base);
|
||||
os.write_string(fd, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
os.write_string(fd, "bit_field ");
|
||||
if ti.align != 1 {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
os.write_byte(fd, ' ');
|
||||
}
|
||||
os.write_string(fd, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
os.write_string(fd, name);
|
||||
os.write_string(fd, ": ");
|
||||
print_u64(fd, u64(info.bits[i]));
|
||||
}
|
||||
os.write_string(fd, "}");
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
os.write_string(fd, "bit_set[");
|
||||
|
||||
#partial switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Rune:
|
||||
os.write_encoded_rune(fd, rune(info.lower));
|
||||
os.write_string(fd, "..");
|
||||
os.write_encoded_rune(fd, rune(info.upper));
|
||||
case:
|
||||
print_i64(fd, info.lower);
|
||||
os.write_string(fd, "..");
|
||||
print_i64(fd, info.upper);
|
||||
}
|
||||
if info.underlying != nil {
|
||||
os.write_string(fd, "; ");
|
||||
print_type(fd, info.underlying);
|
||||
}
|
||||
os.write_byte(fd, ']');
|
||||
|
||||
case Type_Info_Opaque:
|
||||
os.write_string(fd, "opaque ");
|
||||
print_type(fd, info.elem);
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
os.write_string(fd, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
os.write_string(fd, "#simd[");
|
||||
print_u64(fd, u64(info.count));
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
}
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
os.write_string(fd, "#relative(");
|
||||
print_type(fd, info.base_integer);
|
||||
os.write_string(fd, ") ");
|
||||
print_type(fd, info.pointer);
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
os.write_string(fd, "#relative(");
|
||||
print_type(fd, info.base_integer);
|
||||
os.write_string(fd, ") ");
|
||||
print_type(fd, info.slice);
|
||||
}
|
||||
}
|
||||
|
||||
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
||||
x := uintptr(a);
|
||||
y := uintptr(b);
|
||||
n := uintptr(n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := uintptr(n/SU + 1);
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := uintptr(0);
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
|
||||
vb := (^uintptr)(y + curr_block * size_of(uintptr))^;
|
||||
if va ~ vb != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
a := (^byte)(x+pos)^;
|
||||
b := (^byte)(y+pos)^;
|
||||
if a ~ b != 0 {
|
||||
return -1 if (int(a) - int(b)) < 0 else +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
a := (^byte)(x+offset)^;
|
||||
b := (^byte)(y+offset)^;
|
||||
if a ~ b != 0 {
|
||||
return -1 if (int(a) - int(b)) < 0 else +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_check {
|
||||
x := uintptr(a);
|
||||
n := uintptr(n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := uintptr(n/SU + 1);
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := uintptr(0);
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
|
||||
if va ~ 0 != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
a := (^byte)(x+pos)^;
|
||||
if a ~ 0 != 0 {
|
||||
return -1 if int(a) < 0 else +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
a := (^byte)(x+offset)^;
|
||||
if a ~ 0 != 0 {
|
||||
return -1 if int(a) < 0 else +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string_eq :: proc "contextless" (a, b: string) -> bool {
|
||||
x := transmute(Raw_String)a;
|
||||
y := transmute(Raw_String)b;
|
||||
switch {
|
||||
case x.len != y.len: return false;
|
||||
case x.len == 0: return true;
|
||||
case x.data == y.data: return true;
|
||||
}
|
||||
return string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
x := transmute(Raw_String)a;
|
||||
y := transmute(Raw_String)b;
|
||||
return memory_compare(x.data, y.data, min(x.len, y.len));
|
||||
}
|
||||
|
||||
string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
|
||||
string_lt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0; }
|
||||
string_gt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0; }
|
||||
string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0; }
|
||||
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
|
||||
|
||||
cstring_len :: proc "contextless" (s: cstring) -> int {
|
||||
p0 := uintptr((^byte)(s));
|
||||
p := p0;
|
||||
for p != 0 && (^byte)(p)^ != 0 {
|
||||
p += 1;
|
||||
}
|
||||
return int(p - p0);
|
||||
}
|
||||
|
||||
cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
if s == nil do return "";
|
||||
ptr := (^byte)(s);
|
||||
n := cstring_len(s);
|
||||
return transmute(string)Raw_String{ptr, n};
|
||||
}
|
||||
|
||||
|
||||
complex64_eq :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex64_ne :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
|
||||
bounds_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == "windows" {
|
||||
windows_trap_array_bounds();
|
||||
} else {
|
||||
trap();
|
||||
}
|
||||
}
|
||||
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == "windows" {
|
||||
windows_trap_type_assertion();
|
||||
} else {
|
||||
trap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count do return;
|
||||
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
|
||||
os.write_string(fd, " Index ");
|
||||
print_i64(fd, i64(index));
|
||||
os.write_string(fd, " is out of bounds range 0:");
|
||||
print_i64(fd, i64(count));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
handle_error(file, line, column, index, count);
|
||||
}
|
||||
|
||||
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
|
||||
os.write_string(fd, " Invalid slice indices: ");
|
||||
print_i64(fd, i64(lo));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(hi));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
|
||||
slice_expr_error_hi :: proc "contextless" (file: string, line, column: int, hi: int, len: int) {
|
||||
if 0 <= hi && hi <= len do return;
|
||||
slice_handle_error(file, line, column, 0, hi, len);
|
||||
}
|
||||
|
||||
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
|
||||
if 0 <= lo && lo <= len && lo <= hi && hi <= len do return;
|
||||
slice_handle_error(file, line, column, lo, hi, len);
|
||||
}
|
||||
|
||||
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
|
||||
if 0 <= low && low <= high && high <= max do return;
|
||||
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
|
||||
os.write_string(fd, " Invalid dynamic array values: ");
|
||||
print_i64(fd, i64(low));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(high));
|
||||
os.write_string(fd, ":");
|
||||
print_i64(fd, i64(max));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
handle_error(file, line, column, low, high, max);
|
||||
}
|
||||
|
||||
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid) {
|
||||
if ok do return;
|
||||
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
|
||||
os.write_string(fd, " Invalid type assertion from ");
|
||||
print_typeid(fd, from);
|
||||
os.write_string(fd, " to ");
|
||||
print_typeid(fd, to);
|
||||
os.write_byte(fd, '\n');
|
||||
type_assertion_trap();
|
||||
}
|
||||
handle_error(file, line, column, from, to);
|
||||
}
|
||||
|
||||
|
||||
string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
|
||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||
|
||||
@static accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
|
||||
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
|
||||
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
|
||||
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
|
||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||
};
|
||||
Accept_Range :: struct {lo, hi: u8};
|
||||
|
||||
@static accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0xbf},
|
||||
{0xa0, 0xbf},
|
||||
{0x80, 0x9f},
|
||||
{0x90, 0xbf},
|
||||
{0x80, 0x8f},
|
||||
};
|
||||
|
||||
MASKX :: 0b0011_1111;
|
||||
MASK2 :: 0b0001_1111;
|
||||
MASK3 :: 0b0000_1111;
|
||||
MASK4 :: 0b0000_0111;
|
||||
|
||||
LOCB :: 0b1000_0000;
|
||||
HICB :: 0b1011_1111;
|
||||
|
||||
|
||||
RUNE_ERROR :: '\ufffd';
|
||||
|
||||
n := len(s);
|
||||
if n < 1 {
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
s0 := s[0];
|
||||
x := accept_sizes[s0];
|
||||
if x >= 0xF0 {
|
||||
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
|
||||
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
|
||||
}
|
||||
sz := x & 7;
|
||||
accept := accept_ranges[x>>4];
|
||||
if n < int(sz) {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
b1 := s[1];
|
||||
if b1 < accept.lo || accept.hi < b1 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if sz == 2 {
|
||||
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
|
||||
}
|
||||
b2 := s[2];
|
||||
if b2 < LOCB || HICB < b2 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if sz == 3 {
|
||||
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
|
||||
}
|
||||
b3 := s[3];
|
||||
if b3 < LOCB || HICB < b3 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||
}
|
||||
|
||||
bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) {
|
||||
bounds_check_error(file_path, int(line), int(column), index, count);
|
||||
}
|
||||
|
||||
slice_expr_error_hi_loc :: inline proc "contextless" (using loc := #caller_location, hi: int, len: int) {
|
||||
slice_expr_error_hi(file_path, int(line), int(column), hi, len);
|
||||
}
|
||||
|
||||
slice_expr_error_lo_hi_loc :: inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) {
|
||||
slice_expr_error_lo_hi(file_path, int(line), int(column), lo, hi, len);
|
||||
}
|
||||
|
||||
dynamic_array_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, low, high, max: int) {
|
||||
dynamic_array_expr_error(file_path, int(line), int(column), low, high, max);
|
||||
}
|
||||
|
||||
|
||||
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
|
||||
if 0 <= len do return;
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid slice length for make: ");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
handle_error(loc, len);
|
||||
}
|
||||
|
||||
make_dynamic_array_error_loc :: inline proc "contextless" (using loc := #caller_location, len, cap: int) {
|
||||
if 0 <= len && len <= cap do return;
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid dynamic array parameters for make: ");
|
||||
print_i64(fd, i64(len));
|
||||
os.write_byte(fd, ':');
|
||||
print_i64(fd, i64(cap));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
handle_error(loc, len, cap);
|
||||
}
|
||||
|
||||
make_map_expr_error_loc :: inline proc "contextless" (loc := #caller_location, cap: int) {
|
||||
if 0 <= cap do return;
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
|
||||
context = default_context();
|
||||
fd := os.stderr;
|
||||
print_caller_location(fd, loc);
|
||||
os.write_string(fd, " Invalid map capacity for make: ");
|
||||
print_i64(fd, i64(cap));
|
||||
os.write_byte(fd, '\n');
|
||||
bounds_trap();
|
||||
}
|
||||
handle_error(loc, cap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@(default_calling_convention = "c")
|
||||
foreign {
|
||||
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
|
||||
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
|
||||
}
|
||||
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
|
||||
}
|
||||
return _abs(x);
|
||||
}
|
||||
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
|
||||
}
|
||||
return _abs(x);
|
||||
}
|
||||
|
||||
min_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
}
|
||||
min_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
}
|
||||
max_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
}
|
||||
max_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
}
|
||||
|
||||
abs_complex64 :: inline proc "contextless" (x: complex64) -> f32 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f32(r*r + i*i);
|
||||
}
|
||||
abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f64(r*r + i*i);
|
||||
}
|
||||
abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f32(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f64(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
|
||||
quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 {
|
||||
e, f: f32;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
ratio := imag(m) / real(m);
|
||||
denom := real(m) + ratio*imag(m);
|
||||
e = (real(n) + imag(n)*ratio) / denom;
|
||||
f = (imag(n) - real(n)*ratio) / denom;
|
||||
} else {
|
||||
ratio := real(m) / imag(m);
|
||||
denom := imag(m) + ratio*real(m);
|
||||
e = (real(n)*ratio + imag(n)) / denom;
|
||||
f = (imag(n)*ratio - real(n)) / denom;
|
||||
}
|
||||
|
||||
return complex(e, f);
|
||||
}
|
||||
|
||||
quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
|
||||
e, f: f64;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
ratio := imag(m) / real(m);
|
||||
denom := real(m) + ratio*imag(m);
|
||||
e = (real(n) + imag(n)*ratio) / denom;
|
||||
f = (imag(n) - real(n)*ratio) / denom;
|
||||
} else {
|
||||
ratio := real(m) / imag(m);
|
||||
denom := imag(m) + ratio*real(m);
|
||||
e = (real(n)*ratio + imag(n)) / denom;
|
||||
f = (imag(n)*ratio - real(n)) / denom;
|
||||
}
|
||||
|
||||
return complex(e, f);
|
||||
}
|
||||
|
||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
|
||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
|
||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
|
||||
|
||||
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
|
||||
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
|
||||
|
||||
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
|
||||
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package runtime
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---;
|
||||
_ = udivmod128(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__udivmodti4")
|
||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem);
|
||||
}
|
||||
|
||||
@(link_name="__udivti3")
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil);
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__modti3")
|
||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1);
|
||||
s_b := b >> (128 - 1);
|
||||
an := (a ~ s_a) - s_a;
|
||||
bn := (b ~ s_b) - s_b;
|
||||
|
||||
r: u128 = ---;
|
||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
|
||||
return (transmute(i128)r ~ s_a) - s_a;
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__divmodti4")
|
||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem);
|
||||
return transmute(i128)u;
|
||||
}
|
||||
|
||||
@(link_name="__divti3")
|
||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil);
|
||||
return transmute(i128)u;
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__fixdfti")
|
||||
fixdfti :: proc(a: u64) -> i128 {
|
||||
significandBits :: 52;
|
||||
typeWidth :: (size_of(u64)*8);
|
||||
exponentBits :: (typeWidth - significandBits - 1);
|
||||
maxExponent :: ((1 << exponentBits) - 1);
|
||||
exponentBias :: (maxExponent >> 1);
|
||||
|
||||
implicitBit :: (u64(1) << significandBits);
|
||||
significandMask :: (implicitBit - 1);
|
||||
signBit :: (u64(1) << (significandBits + exponentBits));
|
||||
absMask :: (signBit - 1);
|
||||
exponentMask :: (absMask ~ significandMask);
|
||||
|
||||
// Break a into sign, exponent, significand
|
||||
aRep := a;
|
||||
aAbs := aRep & absMask;
|
||||
sign := i128(-1 if aRep & signBit != 0 else 1);
|
||||
exponent := u64((aAbs >> significandBits) - exponentBias);
|
||||
significand := u64((aAbs & significandMask) | implicitBit);
|
||||
|
||||
// If exponent is negative, the result is zero.
|
||||
if exponent < 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if exponent >= size_of(i128) * 8 {
|
||||
return max(i128) if sign == 1 else min(i128);
|
||||
}
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
// Otherwise, shift left.
|
||||
if exponent < significandBits {
|
||||
return sign * i128(significand >> (significandBits - exponent));
|
||||
} else {
|
||||
return sign * (i128(significand) << (exponent - significandBits));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__floattidf")
|
||||
floattidf :: proc(a: i128) -> f64 {
|
||||
DBL_MANT_DIG :: 53;
|
||||
if a == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
a := a;
|
||||
N :: size_of(i128) * 8;
|
||||
s := a >> (N-1);
|
||||
a = (a ~ s) - s;
|
||||
sd: = N - _clz_i128(a); // number of significant digits
|
||||
e := u32(sd - 1); // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||
};
|
||||
|
||||
a |= i128((a & 4) != 0);
|
||||
a += 1;
|
||||
a >>= 2;
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1;
|
||||
e += 1;
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd);
|
||||
}
|
||||
fb: [2]u32;
|
||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||
((e + 1023) << 20) | // exponent
|
||||
((u32(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||
fb[1] = u32(a); // mantissa-low
|
||||
return transmute(f64)fb;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//+build linux, darwin
|
||||
package runtime
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
if ptr == nil || len == 0 {
|
||||
return ptr;
|
||||
}
|
||||
b := byte(val);
|
||||
|
||||
p_start := uintptr(ptr);
|
||||
p_end := p_start + uintptr(max(len, 0));
|
||||
for p := p_start; p < p_end; p += 1 {
|
||||
(^byte)(p)^ = b;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//+build wasm32
|
||||
package runtime
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
if ptr == nil || len == 0 {
|
||||
return ptr;
|
||||
}
|
||||
b := byte(val);
|
||||
|
||||
p_start := uintptr(ptr);
|
||||
p_end := p_start + uintptr(max(len, 0));
|
||||
for p := p_start; p < p_end; p += 1 {
|
||||
(^byte)(p)^ = b;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package runtime
|
||||
|
||||
@require foreign import "system:int64.lib"
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||
DWORD :: u32;
|
||||
ULONG_PTR :: uint;
|
||||
|
||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C;
|
||||
|
||||
foreign kernel32 {
|
||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||
}
|
||||
|
||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil);
|
||||
}
|
||||
|
||||
windows_trap_type_assertion :: proc "contextless" () -> ! {
|
||||
windows_trap_array_bounds();
|
||||
}
|
||||
|
||||
@(private, require, link_name="_fltused") _fltused: i32 = 0x9875;
|
||||
|
||||
@(private, require, link_name="_tls_index") _tls_index: u32;
|
||||
@(private, require, link_name="_tls_array") _tls_array: u32;
|
||||
|
||||
|
||||
|
||||
@(link_name="memcpy")
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if dst == nil || src == nil || len == 0 || dst == src {
|
||||
return dst;
|
||||
}
|
||||
d := uintptr(dst);
|
||||
s := uintptr(src);
|
||||
n := uintptr(len);
|
||||
|
||||
for i in 0..<n {
|
||||
(^byte)(d+i)^ = (^byte)(s+i)^;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memmove")
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if dst == nil || src == nil || len == 0 || dst == src {
|
||||
return dst;
|
||||
}
|
||||
|
||||
d := uintptr(dst);
|
||||
s := uintptr(src);
|
||||
n := uintptr(len);
|
||||
|
||||
if s < d && d < s+n {
|
||||
// Overlap
|
||||
for i := n-1; n >= 0; i -= 1 {
|
||||
(^byte)(d+i)^ = (^byte)(s+i)^;
|
||||
}
|
||||
|
||||
} else {
|
||||
for i in 0..<n {
|
||||
(^byte)(d+i)^ = (^byte)(s+i)^;
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
if ptr == nil || len == 0 {
|
||||
return ptr;
|
||||
}
|
||||
d := uintptr(ptr);
|
||||
b := byte(val);
|
||||
for i in 0..<uintptr(len) {
|
||||
(^byte)(d+i)^ = b;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// @(link_name="memcmp")
|
||||
// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 {
|
||||
// if dst == nil || src == nil {
|
||||
// return 0;
|
||||
// }
|
||||
// if dst == src {
|
||||
// return 0;
|
||||
// }
|
||||
// d, s := uintptr(dst), uintptr(src);
|
||||
// n := uintptr(len);
|
||||
|
||||
// for i := uintptr(0); i < n; i += 1 {
|
||||
// x, y := (^byte)(d+i)^, (^byte)(s+i)^;
|
||||
// if x != y {
|
||||
// return x < y ? -1 : +1;
|
||||
// }
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user