mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-07 12:11:36 -07:00
Compare commits
724 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 521ed28632 | |||
| d6300314c0 | |||
| 27130259cc | |||
| b4fb295bb3 | |||
| f7e608628b | |||
| 605d66845a | |||
| 37ec3d7006 | |||
| 89eb351d2b | |||
| abaacfe78d | |||
| f9f4551e8d | |||
| daf005d1ab | |||
| ce1ee962f5 | |||
| d0e4edfb43 | |||
| 749e5067fb | |||
| 75dcaf6d8d | |||
| 00a0a1e95d | |||
| 7a4106077a | |||
| 9c8eaeb988 | |||
| 7ed28e8a84 | |||
| a3d53a6288 | |||
| 2127dc56b1 | |||
| e59e34d334 | |||
| 4fd97c3ba6 | |||
| 107c7a36d0 | |||
| dcf2c43863 | |||
| 0c25f7cdc5 | |||
| e5c243ee93 | |||
| e9b6a8fc9a | |||
| a27c00862c | |||
| a06f75b6fb | |||
| d6cb105d5f | |||
| 5ac36b5f25 | |||
| 22bcf1ba70 | |||
| 51c705edf1 | |||
| e45401bfb4 | |||
| 6b652afb8e | |||
| 76b85c0622 | |||
| 6fa0679be9 | |||
| afea221d64 | |||
| b9ec2de4db | |||
| 3dfd53aee0 | |||
| b54fc8ff95 | |||
| 8745942255 | |||
| c7be30e0ea | |||
| 1baa47c78e | |||
| 0b33df4e9d | |||
| 4c40495742 | |||
| 824b97d250 | |||
| 5bbab05161 | |||
| 83558a1352 | |||
| cb183e968a | |||
| 27d56d0da4 | |||
| c663566cd5 | |||
| 13d052027f | |||
| 7076cf69e4 | |||
| 6ae8adaa45 | |||
| 15bbdb2030 | |||
| 48c9c1682c | |||
| d3c5143292 | |||
| 3949e2220f | |||
| 9ed4f95c1a | |||
| 8daecf7532 | |||
| 11d665c25a | |||
| 98a086b91b | |||
| f323a179d9 | |||
| c6f282d20b | |||
| ba49a9100d | |||
| 6fe77155b5 | |||
| 677e7ff642 | |||
| 682b5fa0d3 | |||
| ab00db2ebd | |||
| 0a0e8f36eb | |||
| bbe44b49bc | |||
| 25bec19b1f | |||
| 81f83d5780 | |||
| d2019e3e4d | |||
| 489e8dc592 | |||
| 3edb3d8d8c | |||
| a705a2e38b | |||
| 7dfbda58d9 | |||
| 9b88a38e54 | |||
| 16a494347c | |||
| ad0f11668b | |||
| 699cabeb1c | |||
| 7207f4b0c5 | |||
| 9c1b464c94 | |||
| 04a1e7d638 | |||
| 7cfbd87f57 | |||
| e9e05a3783 | |||
| 2b83f27f06 | |||
| 3d0e194298 | |||
| fcd8860990 | |||
| 22840ddf97 | |||
| f9576c2f5b | |||
| 16fc961010 | |||
| d2701d8b13 | |||
| a0bd31646b | |||
| 0d37da54b4 | |||
| 5d47e2a166 | |||
| 035c75d6a9 | |||
| b475481788 | |||
| 033525fe13 | |||
| 8852d090b6 | |||
| ac259ac790 | |||
| 7b4a87d37c | |||
| f6fc3ebe37 | |||
| 5c106abe3f | |||
| db748b7a05 | |||
| f2f2d532f5 | |||
| 1bcec3f769 | |||
| b035ee2bcd | |||
| 0424fb486b | |||
| 3858422f1d | |||
| d4f343751e | |||
| 79baddc157 | |||
| bcf437dc11 | |||
| 503eb470a7 | |||
| 667af1be58 | |||
| 366779f8c7 | |||
| dae299b781 | |||
| 2f29894b45 | |||
| 0819d05a0b | |||
| 6a4e44607c | |||
| a71daee545 | |||
| 046dd55032 | |||
| 2fc3da3fde | |||
| a74093784c | |||
| ed58374964 | |||
| 6dd4d1a924 | |||
| d77269dee2 | |||
| ea263b8cc5 | |||
| 45f0c812af | |||
| 810a1eee41 | |||
| e3e225d21b | |||
| 50e10ceb3b | |||
| da774e3fd2 | |||
| 2c3febd620 | |||
| bce62b98d4 | |||
| e914a8710d | |||
| c96e0afbf1 | |||
| f1c24f434b | |||
| e8517e1d02 | |||
| 92e406cef0 | |||
| 269913ede0 | |||
| 2ed16240a7 | |||
| ff36b754cb | |||
| 503b897677 | |||
| d69c74665a | |||
| fcf081283c | |||
| 7a6e8543a6 | |||
| f30755a871 | |||
| 503220e4c1 | |||
| 051814a69c | |||
| 21843da9e3 | |||
| 30f49f81c1 | |||
| 439f4776e4 | |||
| b743f56fb9 | |||
| 1fc3f6cb2e | |||
| df19c48da8 | |||
| f7211408fc | |||
| 30db316e16 | |||
| 8c01e952f3 | |||
| 3e66b88031 | |||
| f76316f889 | |||
| 32477a88ef | |||
| e8bc576b23 | |||
| 2eea6f2490 | |||
| 1d9d79542c | |||
| 1a6d4c955a | |||
| 717522efe4 | |||
| 8d06d9c23d | |||
| 765c1546c5 | |||
| 7ec6fd30f0 | |||
| 0ca773114a | |||
| 9e1576418f | |||
| b7ea169c81 | |||
| 3b583cbac7 | |||
| 382bd87667 | |||
| 6cc07dc24e | |||
| 01cdd22a01 | |||
| 35331e6973 | |||
| c18e98e8c5 | |||
| 3cd553565f | |||
| 9eec9f5788 | |||
| 2b7ca2bdd6 | |||
| 411c0add3b | |||
| 18d7ecc1a5 | |||
| 4812601e78 | |||
| 2d5779b660 | |||
| fd53e8b955 | |||
| 53a030c65b | |||
| 91ad6b42c5 | |||
| dad10ef800 | |||
| 71eb21aab7 | |||
| f8228e305a | |||
| 0e7109cab2 | |||
| c39ef1b25c | |||
| 9da37ed394 | |||
| 8fa571c283 | |||
| 83f3ae14d5 | |||
| 6a14c3edb4 | |||
| 2cd895c50b | |||
| ee89c0458f | |||
| cee847a68c | |||
| 413f96553a | |||
| 662ed4a67c | |||
| 85a263130d | |||
| d19ae37af1 | |||
| 22672a816e | |||
| dcb873c88d | |||
| 62ab2987b6 | |||
| 7bcde35651 | |||
| 4b8721a0bb | |||
| 7743e34596 | |||
| 4003b76fd3 | |||
| c27ed1896f | |||
| 7d217269b5 | |||
| a3c8882648 | |||
| 4389059834 | |||
| a55e90fefd | |||
| f58f922487 | |||
| 1a0930f841 | |||
| a5f8c3f692 | |||
| 92fb65cf2e | |||
| a51943e27f | |||
| 03c834e410 | |||
| 989107094c | |||
| fd8956b8f4 | |||
| 648e3c65ea | |||
| d5047e621d | |||
| 8fbdef01d6 | |||
| 9dee943fae | |||
| 8ceb691cec | |||
| f26516f6fa | |||
| fda8e8a30b | |||
| 2242178d96 | |||
| a459bc13dc | |||
| 53e84b7f31 | |||
| 098f51aa80 | |||
| 765969e6a3 | |||
| 8086c14dcc | |||
| 80ce1b7d85 | |||
| 9f55404845 | |||
| 075040ae05 | |||
| aa799d6a0d | |||
| 58e607e960 | |||
| ff51c5ee56 | |||
| 73c1f08776 | |||
| 412ca36230 | |||
| 06d1df4cae | |||
| 7662808bc9 | |||
| d48828dd80 | |||
| b725e01cdd | |||
| 874c1f076d | |||
| 2c14f0a109 | |||
| cf4afc2e7b | |||
| 5ed06f7eb8 | |||
| 765cd66b30 | |||
| 5a8fbc230d | |||
| 5c62211f00 | |||
| 835b8ffa22 | |||
| b84108c4b5 | |||
| 6642e1fc9d | |||
| c909e8e4b8 | |||
| 9bdbb45517 | |||
| 1b5860e574 | |||
| 047d45584e | |||
| 721486f875 | |||
| 29f2ecd228 | |||
| 970ac22647 | |||
| 419eab5059 | |||
| a1935bc1f4 | |||
| fc06c8ed9f | |||
| 7952b26e8b | |||
| fa6cfde4b0 | |||
| 4c78ba2152 | |||
| 9870e43ac0 | |||
| 63086c7eaf | |||
| ef0c6fc4b3 | |||
| 159c5311c3 | |||
| b6a65fac36 | |||
| ab7367ae47 | |||
| 457f509b5f | |||
| 7e5c063d98 | |||
| dfabd0e0ad | |||
| 141133e326 | |||
| e188a542da | |||
| 64f1e8b7a2 | |||
| 62440df051 | |||
| 5362e883f4 | |||
| 8b06fd0935 | |||
| bb9b58b8c4 | |||
| ee070c9bd3 | |||
| aebafdcd08 | |||
| 2b4fce8684 | |||
| de8f6709f7 | |||
| 683753db96 | |||
| d13dc7eca7 | |||
| e56920e445 | |||
| 1c9aad4d7b | |||
| 79fe30321a | |||
| 35ee7f3cba | |||
| 4c2e86b063 | |||
| d52a9b61af | |||
| 8a5b39f734 | |||
| ce09cb0bdb | |||
| b7fd91817e | |||
| a728047281 | |||
| 775c9648f9 | |||
| f65bdf5733 | |||
| 213d930f8c | |||
| fae025aac8 | |||
| 97477ee51c | |||
| 99894686cf | |||
| 1162e30768 | |||
| cd910b1512 | |||
| efa86ddf46 | |||
| d8f60cd7f2 | |||
| c4d19dfa92 | |||
| 35e70f4be1 | |||
| eb6c388f13 | |||
| 42144d957b | |||
| d1c778680b | |||
| 0fe006157e | |||
| 4d208dc092 | |||
| 162e86663f | |||
| 83ffb68bb7 | |||
| 4705321988 | |||
| 37a2356485 | |||
| 5cf473b31c | |||
| a7484f16cb | |||
| 6c8aad0afb | |||
| c767d55e9a | |||
| 7f601c9535 | |||
| 12cc7388e7 | |||
| 2ff61bdfc7 | |||
| eb0d7465e2 | |||
| 07d798c61a | |||
| b426e8577b | |||
| 532133d648 | |||
| c056a0d108 | |||
| 6fe1825db9 | |||
| b15968f140 | |||
| 0ddf1bf660 | |||
| dade5b5ef2 | |||
| 3aea9a7c20 | |||
| 0dce7769f4 | |||
| 4b73438833 | |||
| 8c3f01fbfa | |||
| b7abacfa7e | |||
| 3383e9c556 | |||
| 0380601bb4 | |||
| 4b4c2a2abd | |||
| b1542d4e98 | |||
| d469c2da48 | |||
| 29c5e390aa | |||
| 3455e5690c | |||
| 0ca8a5e186 | |||
| cb85d00e9e | |||
| 8ce1ce85ad | |||
| a6d3cbe824 | |||
| 9b9aa9c353 | |||
| 831620bfc4 | |||
| 4f50988799 | |||
| ff97a73152 | |||
| 769d8dd038 | |||
| 1d793ea338 | |||
| 5337413c56 | |||
| 380905618a | |||
| 3ff56e4405 | |||
| 58297774f7 | |||
| 5e9ff85fa8 | |||
| eb7a9c55b0 | |||
| 6157af56e9 | |||
| d6f84887ff | |||
| 729ffeee09 | |||
| 0092995f9d | |||
| 3fb69d59bb | |||
| cb207afdf3 | |||
| 756c1b7bcb | |||
| cd484979a8 | |||
| 9e3ea92829 | |||
| c37de9459e | |||
| 4d512c2cf6 | |||
| 81f10f53ad | |||
| fbf036a654 | |||
| 40bcfc7c8d | |||
| bfe0ffd6e6 | |||
| 8ee6bb5d4b | |||
| 0ebc2add03 | |||
| 7840c1b89f | |||
| 0428d5ae2e | |||
| b967ae2739 | |||
| c462496bd5 | |||
| a903e5024c | |||
| 7cce55e2fc | |||
| 99a1a10286 | |||
| 9640b49319 | |||
| 1bc0e66ed1 | |||
| 117d32dfc4 | |||
| 320b84df4f | |||
| 98eaf5c6c0 | |||
| 9842019205 | |||
| 479278be4e | |||
| 4767311a22 | |||
| f50fc33749 | |||
| 8aba92da9b | |||
| 59f3e10f0a | |||
| 8b82bcef7d | |||
| 1e595f2e26 | |||
| 28ad4f8623 | |||
| a3c04db828 | |||
| 3ea7af4c9c | |||
| 190c3ab0cd | |||
| 53c7cf895c | |||
| db1b7b2d21 | |||
| 7cd7886111 | |||
| 164ba944ac | |||
| e7fb2cf73b | |||
| 023cc8b572 | |||
| 0ff5ff6ff2 | |||
| a35d6a6f8d | |||
| 663b62e45f | |||
| 6910182011 | |||
| bba47b6f54 | |||
| ef372bd861 | |||
| fbbfe438dc | |||
| 7e495a5fe5 | |||
| 0f036eebc0 | |||
| e008eeac6a | |||
| 25e330500f | |||
| fa20988f51 | |||
| 99f4cc3006 | |||
| a1487e4814 | |||
| 183a02c584 | |||
| 913e8b2e02 | |||
| 5800e085e8 | |||
| 623d687192 | |||
| fcb668663b | |||
| ad98efe1fd | |||
| 3fae8b49db | |||
| 8fb9db3deb | |||
| 0859ccc5c0 | |||
| 0c9ddd51a4 | |||
| f77709e67e | |||
| 81e3b64ecd | |||
| 39728b8bfb | |||
| 3b5998af12 | |||
| eea19f8112 | |||
| 268fb22bca | |||
| 37e23a19b5 | |||
| 7d55bfc120 | |||
| ab1741ab38 | |||
| af76d26771 | |||
| d2097e9fdd | |||
| d325c36eb8 | |||
| 79b55d5e2b | |||
| 0c9aaed9f7 | |||
| 82d5f48fa7 | |||
| 2239e43faf | |||
| 70b0ade8c3 | |||
| 86b6d01242 | |||
| 826a3b3012 | |||
| 35d622c131 | |||
| 4bdd2ff93c | |||
| 6fffed179b | |||
| e6b91d3d7c | |||
| 99a7bf9faa | |||
| 44eb478437 | |||
| fc2cd3e1d5 | |||
| a5a9347308 | |||
| b6ed117726 | |||
| 4b23decb08 | |||
| a70ea6579d | |||
| cade30b117 | |||
| 1ca641f718 | |||
| 6222e7be78 | |||
| c7deff4d2e | |||
| 590615ba52 | |||
| b1dafcfe6d | |||
| 4998cf80c1 | |||
| 37e23133e9 | |||
| 91fd9c1ef2 | |||
| 12687a63f4 | |||
| d699d872d9 | |||
| fb2cbe471b | |||
| 17894add95 | |||
| 23d93f6846 | |||
| 2e3dd8dd0b | |||
| 426f02906b | |||
| 2d12ba3ac0 | |||
| 21335e6459 | |||
| 8421cb6d21 | |||
| cac72a9423 | |||
| 9266b81aff | |||
| 52475b1761 | |||
| 2f6347b924 | |||
| eb5456f9c7 | |||
| f914fd0219 | |||
| c94ca4c0cb | |||
| 31a192454c | |||
| 4b2246ba9f | |||
| 0ffffb12da | |||
| 4c857bf031 | |||
| 3f3f4fafff | |||
| 4367ae4acf | |||
| 4eafb0ce7f | |||
| 7a4891b6b9 | |||
| 0171c276f0 | |||
| 0743dd195d | |||
| d1a204a784 | |||
| c2809c2948 | |||
| 99e5a14703 | |||
| 0efc79bcb9 | |||
| 57dea0e4d8 | |||
| 706d0c3a91 | |||
| 1637de3ebb | |||
| 45691a4622 | |||
| 9e47c72b98 | |||
| f5d13dc568 | |||
| a36c1cd54a | |||
| 74458ab096 | |||
| c39b1a31db | |||
| 635c7fa153 | |||
| b7ac0a9e8d | |||
| 3f3ae4b2b6 | |||
| c2423dc07f | |||
| 1296630160 | |||
| 63eec25044 | |||
| 7a9b0731cf | |||
| d45661c405 | |||
| 002bec256a | |||
| 01e8668357 | |||
| 000861cba8 | |||
| 36473b2774 | |||
| 4188f50105 | |||
| 3e3b9ae2df | |||
| e89f0de232 | |||
| 4858e16a11 | |||
| 902a6db0e1 | |||
| 19ae6122c7 | |||
| b82b91ea08 | |||
| 636f0d7063 | |||
| ed73441a4c | |||
| 0f3cebd2b7 | |||
| 4c2be6cd49 | |||
| f3f51bd643 | |||
| 7479ac48e8 | |||
| d5f94d73ad | |||
| 4c5672119a | |||
| 8482f943ea | |||
| 15aaf7dfa0 | |||
| 768abf83f6 | |||
| ca76d53452 | |||
| b0904d6598 | |||
| b0a09f7b9e | |||
| eb4891bcc8 | |||
| 803fd8f037 | |||
| 67bdb5b1a3 | |||
| acc635b535 | |||
| 4e8ce87792 | |||
| 2c8daa25dc | |||
| 054ee0a8b5 | |||
| d0cadaf1a6 | |||
| 317db2758a | |||
| 25102d4792 | |||
| d39f1c461e | |||
| 7a6fc3a93b | |||
| 83c002c197 | |||
| fc47b5dee0 | |||
| 6c2e0b09ba | |||
| 3d4698debe | |||
| 4a25cfb27c | |||
| 294bd6a446 | |||
| d0109db23b | |||
| ee3ee66aae | |||
| f74e281efa | |||
| c0cd02883f | |||
| d9adb0fd6b | |||
| 6363013dd8 | |||
| 934131abf8 | |||
| 4e5337412a | |||
| 00f2e911a7 | |||
| c82d7d3d87 | |||
| ecfea027a0 | |||
| 96be494730 | |||
| 12c8db927b | |||
| 027ea587fc | |||
| 026900c7f0 | |||
| ffa87f55c4 | |||
| c9eed04b51 | |||
| b50b6b9f33 | |||
| 8fd5bef0bd | |||
| d6b49994a2 | |||
| 776927709b | |||
| 96e033b22c | |||
| 3469178dc1 | |||
| af1b3b6368 | |||
| d56789e5a7 | |||
| aeacf3a9d8 | |||
| 4ba486baa2 | |||
| f1ffd90294 | |||
| 777aa8b118 | |||
| cb9e16f4df | |||
| 2908923db9 | |||
| 8c1dfabb6b | |||
| 7fe36de069 | |||
| 27d556735a | |||
| b70d211f21 | |||
| b3e3b6c656 | |||
| 1734286252 | |||
| c8c076f970 | |||
| e40b3ad338 | |||
| afec321db2 | |||
| 6e9f9e6f3c | |||
| f504b200a9 | |||
| 82e840a0ca | |||
| 82765ca96e | |||
| 5387ec5f29 | |||
| f2908cbc5a | |||
| 5337b0b471 | |||
| e51afc3509 | |||
| 2c004dbcc9 | |||
| e128ed7d26 | |||
| 9064ebfe97 | |||
| 4f7bbe0e4a | |||
| 208f168564 | |||
| 737bccbd5e | |||
| 87094ef96c | |||
| f5431a046d | |||
| 5a9422b6bc | |||
| d30198c99a | |||
| 0c8d59dd20 | |||
| a460d140fe | |||
| 881d18ee88 | |||
| d73a4aa34b | |||
| 5c298c1501 | |||
| a83ca2120e | |||
| 81799f7f78 | |||
| 7973f7e750 | |||
| 3dc62a67e0 | |||
| 081e36c909 | |||
| 3a1d364f59 | |||
| e50648279d | |||
| 929af320da | |||
| 8e7c7eeeba | |||
| b739044e69 | |||
| 9e0107c9fc | |||
| 107e016508 | |||
| 22d16c20f8 | |||
| 697c839c84 | |||
| de8bd88d2a | |||
| 595efba747 | |||
| c041d15569 | |||
| 57b20e634b | |||
| e285796fc1 | |||
| a19494d3a7 | |||
| d2a362fd52 | |||
| 0f3562ef02 | |||
| 03f683f9e7 | |||
| cecadce86d | |||
| a7c3906003 | |||
| 70dc0c15fd | |||
| 9eeed9d5bd | |||
| a054c2934e | |||
| 38102f14c1 | |||
| 0997df4fcf | |||
| a5a56e061c | |||
| 8b007ad55a | |||
| 57dd5ec4db | |||
| 5b621d5be1 | |||
| 7aee762f3a | |||
| 4ee50c5a35 | |||
| 28f440dd9e | |||
| 43b78e51a4 | |||
| 84f9fb706b | |||
| 812823cad8 | |||
| 0655260378 | |||
| cfc3723879 | |||
| 4c3281b3f2 | |||
| ff94c605e0 | |||
| cb0a59bb2c | |||
| 076700bd22 | |||
| bcccc8338f | |||
| 425dec8bb8 | |||
| 838554460b | |||
| 659c3c528d | |||
| 60aeab3c38 | |||
| 5e3cf45df3 | |||
| 4633591918 | |||
| 0e6a8b7c72 | |||
| 147848ca20 | |||
| cde002c579 | |||
| f23d93ba89 | |||
| c97a8418dc | |||
| 4aca9372a6 | |||
| 4912ecc3ea | |||
| c1c8ceafc2 | |||
| 9c0a3b6c60 | |||
| 7b539e3025 | |||
| b2b0043875 | |||
| 53e0d182af | |||
| a6fa41e290 | |||
| edba99d636 | |||
| 35674959f2 | |||
| dc8b7a0eb8 | |||
| 8d1f46d837 | |||
| a2117d23b2 | |||
| a58e4d0359 | |||
| 576914aee1 | |||
| 8171f8209a | |||
| 64ff05303c | |||
| 6caab6225d | |||
| 326411498a | |||
| d50fcf0020 | |||
| d354d36a3b | |||
| 483a72ac61 | |||
| dbec4b0d0e | |||
| 4cb489b9e4 | |||
| 28ec50d567 | |||
| 0cb1a578d0 | |||
| e139d1cbe4 | |||
| cb04116caf | |||
| 62ff8daa78 |
+13
-13
@@ -38,10 +38,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
@@ -92,10 +92,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
@@ -156,6 +156,13 @@ jobs:
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\internal
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -163,13 +170,6 @@ jobs:
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -50,6 +50,7 @@ jobs:
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp libLLVM* dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
|
||||
@@ -271,6 +271,7 @@ odin
|
||||
odin.dSYM
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
@@ -283,3 +284,4 @@ shared/
|
||||
*.sublime-workspace
|
||||
examples/bug/
|
||||
build.sh
|
||||
!core/debug/
|
||||
@@ -82,7 +82,7 @@ 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.
|
||||
Get live support and talk with other Odin programmers on the Odin Discord.
|
||||
|
||||
### Articles
|
||||
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
where /Q cl.exe || (
|
||||
set __VSCMD_ARG_NO_LOGO=1
|
||||
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
|
||||
if "!VS!" equ "" (
|
||||
echo ERROR: Visual Studio installation not found
|
||||
exit /b 1
|
||||
)
|
||||
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
|
||||
)
|
||||
|
||||
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
|
||||
+40
-3
@@ -32,7 +32,7 @@ config_darwin() {
|
||||
if [ ARCH == arm64 ]; then
|
||||
MIN_LLVM_VERSION=("13.0.0")
|
||||
else
|
||||
# allow for x86 / amd64 all llvm versions begining from 11
|
||||
# allow for x86 / amd64 all llvm versions beginning from 11
|
||||
MIN_LLVM_VERSION=("11.1.0")
|
||||
fi
|
||||
|
||||
@@ -44,13 +44,31 @@ config_darwin() {
|
||||
fi
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
|
||||
config_freebsd() {
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
|
||||
: ${LLVM_CONFIG=}
|
||||
|
||||
if [ ! "$LLVM_CONFIG" ]; then
|
||||
if which llvm-config11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config11
|
||||
elif which llvm-config12 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config12
|
||||
elif which llvm-config13 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config13
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
fi
|
||||
fi
|
||||
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
@@ -85,9 +103,20 @@ config_linux() {
|
||||
panic "Requirement: llvm-config must be base version greater than 11"
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
|
||||
|
||||
# Creates a copy of the llvm library in the build dir, this is meant to support compiler explorer.
|
||||
# The annoyance is that this copy can be cluttering the development folder. TODO: split staging folders
|
||||
# for development and compiler explorer builds
|
||||
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||
}
|
||||
|
||||
build_odin() {
|
||||
@@ -117,6 +146,14 @@ run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
}
|
||||
|
||||
have_which() {
|
||||
if ! which which > /dev/null 2>&1; then
|
||||
panic "Could not find \`which\`"
|
||||
fi
|
||||
}
|
||||
|
||||
have_which
|
||||
|
||||
case $OS in
|
||||
Linux)
|
||||
config_linux
|
||||
|
||||
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
|
||||
if not exist "vendor\cgltf\lib\*.lib" (
|
||||
pushd vendor\cgltf\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
@@ -15,20 +15,16 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
||||
|
||||
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||
s.stream_data = rw
|
||||
s.stream_vtable = _read_writer_vtable
|
||||
s.stream_vtable = &_read_writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_read_writer_vtable := &io.Stream_VTable{
|
||||
_read_writer_vtable := io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read(b, p)
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read_byte(b)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_unread_byte(b)
|
||||
|
||||
@@ -353,14 +353,14 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
// reader_to_stream converts a Reader into an io.Stream
|
||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _reader_vtable
|
||||
s.stream_vtable = &_reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
_reader_vtable := io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data)
|
||||
reader_destroy(b)
|
||||
|
||||
@@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) {
|
||||
}
|
||||
|
||||
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
// Returns the first non-EOF error that was encountered by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, nil:
|
||||
|
||||
@@ -223,14 +223,14 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
// writer_to_stream converts a Writer into an io.Stream
|
||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _writer_vtable
|
||||
s.stream_vtable = &_writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_writer_vtable := &io.Stream_VTable{
|
||||
_writer_vtable := io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data)
|
||||
writer_destroy(b)
|
||||
|
||||
@@ -240,14 +240,11 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
|
||||
if offset < 0 || offset >= len(b.buf) {
|
||||
if uint(offset) >= len(b.buf) {
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
|
||||
if 0 <= offset && offset < len(b.buf) {
|
||||
n = copy(p, b.buf[offset:])
|
||||
}
|
||||
n = copy(p, b.buf[offset:])
|
||||
if n > 0 {
|
||||
b.last_read = .Read
|
||||
}
|
||||
@@ -374,12 +371,12 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
|
||||
|
||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _buffer_vtable
|
||||
s.stream_vtable = &_buffer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_vtable := &io.Stream_VTable{
|
||||
_buffer_vtable := io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return i64(buffer_capacity(b))
|
||||
|
||||
@@ -638,7 +638,7 @@ trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
|
||||
|
||||
index_rune :: proc(s: []byte, r: rune) -> int {
|
||||
switch {
|
||||
case 0 <= r && r < utf8.RUNE_SELF:
|
||||
case u32(r) < utf8.RUNE_SELF:
|
||||
return index_byte(s, byte(r))
|
||||
|
||||
case r == utf8.RUNE_ERROR:
|
||||
|
||||
@@ -17,7 +17,7 @@ reader_init :: proc(r: ^Reader, s: []byte) {
|
||||
|
||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = r
|
||||
s.stream_vtable = _reader_vtable
|
||||
s.stream_vtable = &_reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
_reader_vtable := io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_size(r)
|
||||
|
||||
@@ -49,8 +49,8 @@ foreign libc {
|
||||
// 7.3.8 Power and absolute-value functions
|
||||
cabs :: proc(z: complex_double) -> complex_double ---
|
||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
||||
cpow :: proc(z: complex_double) -> complex_double ---
|
||||
cpowf :: proc(z: complex_float) -> complex_float ---
|
||||
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||
csqrtf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
|
||||
+15
-2
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
when ODIN_OS == .Linux {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__libc_errno_location")
|
||||
@(link_name="__errno_location")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
|
||||
fma :: proc{libc_fma, libc_fmaf}
|
||||
|
||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
||||
// a trick is used here where we use explicit procedrual overloading of one
|
||||
// a trick is used here where we use explicit procedural overloading of one
|
||||
// procedure. This is done because the foreign block is marked @(private) and
|
||||
// aliasing functions does not remove privateness from the entity.
|
||||
acosf :: proc{libc_acosf}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package libc
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:legacy_stdio_definitions.lib",
|
||||
}
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
|
||||
+27
-1
@@ -88,7 +88,6 @@ foreign libc {
|
||||
srand :: proc(seed: uint) ---
|
||||
|
||||
// 7.22.3 Memory management functions
|
||||
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
|
||||
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
||||
free :: proc(ptr: rawptr) ---
|
||||
malloc :: proc(size: size_t) -> rawptr ---
|
||||
@@ -125,3 +124,30 @@ foreign libc {
|
||||
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
||||
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
|
||||
}
|
||||
|
||||
|
||||
aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign libc {
|
||||
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||
}
|
||||
return _aligned_malloc(size=size, alignment=alignment)
|
||||
} else {
|
||||
foreign libc {
|
||||
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||
}
|
||||
return aligned_alloc(alignment=alignment, size=size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign libc {
|
||||
_aligned_free :: proc(ptr: rawptr) ---
|
||||
}
|
||||
_aligned_free(ptr)
|
||||
} else {
|
||||
free(ptr)
|
||||
}
|
||||
}
|
||||
@@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size + offset {
|
||||
buf := z.input_data[offset:][:size]
|
||||
return (^T)(&buf[0])^, .None
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF
|
||||
} else {
|
||||
return T{}, .Short_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
@@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
// Get current position to return to.
|
||||
cur_pos, e1 := z.input->impl_seek(0, .Current)
|
||||
if e1 != .None {
|
||||
return T{}, e1
|
||||
}
|
||||
|
||||
// Seek to offset.
|
||||
pos, e2 := z.input->impl_seek(offset, .Start)
|
||||
if e2 != .None {
|
||||
return T{}, e2
|
||||
}
|
||||
|
||||
r, e3 := io.to_reader_at(z.input)
|
||||
if !e3 {
|
||||
return T{}, .Empty
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
}
|
||||
_, e4 := io.read_at(r, b[:], pos)
|
||||
if e4 != .None {
|
||||
return T{}, .Empty
|
||||
}
|
||||
|
||||
// Return read head to original position.
|
||||
z.input->impl_seek(cur_pos, .Start)
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
|
||||
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
|
||||
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||
j := j
|
||||
queue := pq.queue[:]
|
||||
n := builtin.len(queue)
|
||||
for 0 <= j {
|
||||
i := (j-1)/2
|
||||
if i == j || !pq.less(queue[j], queue[i]) {
|
||||
|
||||
@@ -73,11 +73,18 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
@@ -92,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
|
||||
idx := q.offset%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
|
||||
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
// Push an element to the back of the queue
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||
if space(q^) == 0 {
|
||||
|
||||
@@ -8,40 +8,40 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
}
|
||||
|
||||
|
||||
len :: proc(a: $A/Small_Array) -> int {
|
||||
len :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
cap :: proc(a: $A/Small_Array) -> int {
|
||||
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data)
|
||||
}
|
||||
|
||||
space :: proc(a: $A/Small_Array) -> int {
|
||||
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data) - a.len
|
||||
}
|
||||
|
||||
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
a.data[index] = item
|
||||
}
|
||||
|
||||
resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, builtin.len(a.data))
|
||||
}
|
||||
|
||||
|
||||
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.data[a.len] = item
|
||||
a.len += 1
|
||||
@@ -50,7 +50,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.len += 1
|
||||
data := slice(a)
|
||||
@@ -61,14 +61,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[0]
|
||||
s := slice(a)
|
||||
@@ -77,7 +77,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
return item
|
||||
}
|
||||
|
||||
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[a.len-1]
|
||||
a.len -= 1
|
||||
@@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[0]
|
||||
s := slice(a)
|
||||
@@ -97,16 +97,16 @@ pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||
resize(a, 0)
|
||||
}
|
||||
|
||||
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
|
||||
### Disclaimer
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
||||
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
|
||||
@@ -30,6 +30,6 @@ equivalence.
|
||||
|
||||
For the most part, alterations to the base fiat-crypto generated code was
|
||||
kept to a minimum, to aid auditability. This results in a somewhat
|
||||
ideosyncratic style, and in some cases minor performance penalties.
|
||||
idiosyncratic style, and in some cases minor performance penalties.
|
||||
|
||||
[1]: https://github.com/mit-plv/fiat-crypto
|
||||
|
||||
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
|
||||
ctx.last_block = len(data) / 8 * 8
|
||||
ctx.buf = data
|
||||
i := 0
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
package debug_pe
|
||||
|
||||
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c
|
||||
PE_SIGNATURE :: u32le(0x0000_4550) // "PE\x00\x00"
|
||||
PE_SIGNATURE_STRING :: "PE\x00\x00"
|
||||
|
||||
OPTIONAL_HEADER_MAGIC :: enum u16le {
|
||||
PE32 = 0x010b,
|
||||
PE32_PLUS = 0x020b,
|
||||
}
|
||||
|
||||
Optional_Header_Base :: struct #packed {
|
||||
magic: OPTIONAL_HEADER_MAGIC,
|
||||
major_linker_version: u8,
|
||||
minor_linker_version: u8,
|
||||
size_of_code: u32le,
|
||||
size_of_initialized_data: u32le,
|
||||
size_of_uninitialized_data: u32le,
|
||||
address_of_entry_point: u32le,
|
||||
base_of_code: u32le,
|
||||
}
|
||||
|
||||
File_Header :: struct #packed {
|
||||
machine: IMAGE_FILE_MACHINE,
|
||||
number_of_sections: u16le,
|
||||
time_date_stamp: u32le,
|
||||
pointer_to_symbol_table: u32le,
|
||||
number_of_symbols: u32le,
|
||||
size_of_optional_header: u16le,
|
||||
characteristics: IMAGE_FILE_CHARACTERISTICS,
|
||||
}
|
||||
|
||||
Data_Directory :: struct #packed {
|
||||
virtual_address: u32le,
|
||||
size: u32le,
|
||||
}
|
||||
|
||||
Optional_Header32 :: struct #packed {
|
||||
using base: Optional_Header_Base,
|
||||
base_of_data: u32le,
|
||||
image_base: u32le,
|
||||
section_alignment: u32le,
|
||||
file_alignment: u32le,
|
||||
major_operating_system_version: u16le,
|
||||
minor_operating_system_version: u16le,
|
||||
major_image_version: u16le,
|
||||
minor_image_version: u16le,
|
||||
major_subsystem_version: u16le,
|
||||
minor_subsystem_version: u16le,
|
||||
win32_version_value: u32le,
|
||||
size_of_image: u32le,
|
||||
size_of_headers: u32le,
|
||||
check_sum: u32le,
|
||||
subsystem: IMAGE_SUBSYSTEM,
|
||||
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||
size_of_stack_reserve: u32le,
|
||||
size_of_stack_commit: u32le,
|
||||
size_of_heap_reserve: u32le,
|
||||
size_of_heap_commit: u32le,
|
||||
loader_flags: u32le,
|
||||
number_of_rva_and_sizes: u32le,
|
||||
data_directory: [16]Data_Directory,
|
||||
}
|
||||
|
||||
Optional_Header64 :: struct #packed {
|
||||
using base: Optional_Header_Base,
|
||||
image_base: u64le,
|
||||
section_alignment: u32le,
|
||||
file_alignment: u32le,
|
||||
major_operating_system_version: u16le,
|
||||
minor_operating_system_version: u16le,
|
||||
major_image_version: u16le,
|
||||
minor_image_version: u16le,
|
||||
major_subsystem_version: u16le,
|
||||
minor_subsystem_version: u16le,
|
||||
win32_version_value: u32le,
|
||||
size_of_image: u32le,
|
||||
size_of_headers: u32le,
|
||||
check_sum: u32le,
|
||||
subsystem: IMAGE_SUBSYSTEM,
|
||||
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||
size_of_stack_reserve: u64le,
|
||||
size_of_stack_commit: u64le,
|
||||
size_of_heap_reserve: u64le,
|
||||
size_of_heap_commit: u64le,
|
||||
loader_flags: u32le,
|
||||
number_of_rva_and_sizes: u32le,
|
||||
data_directory: [16]Data_Directory,
|
||||
}
|
||||
|
||||
// .debug section
|
||||
Debug_Directory_Entry :: struct {
|
||||
characteristics: u32le,
|
||||
time_date_stamp: u32le,
|
||||
major_version: u16le,
|
||||
minor_version: u16le,
|
||||
type: IMAGE_DEBUG_TYPE,
|
||||
size_of_data: u32le,
|
||||
address_of_raw_data: u32le,
|
||||
pointer_to_raw_data: u32le,
|
||||
}
|
||||
|
||||
|
||||
IMAGE_FILE_MACHINE :: enum u16le {
|
||||
UNKNOWN = 0x0,
|
||||
AM33 = 0x1d3,
|
||||
AMD64 = 0x8664,
|
||||
ARM = 0x1c0,
|
||||
ARMNT = 0x1c4,
|
||||
ARM64 = 0xaa64,
|
||||
EBC = 0xebc,
|
||||
I386 = 0x14c,
|
||||
IA64 = 0x200,
|
||||
LOONGARCH32 = 0x6232,
|
||||
LOONGARCH64 = 0x6264,
|
||||
M32R = 0x9041,
|
||||
MIPS16 = 0x266,
|
||||
MIPSFPU = 0x366,
|
||||
MIPSFPU16 = 0x466,
|
||||
POWERPC = 0x1f0,
|
||||
POWERPCFP = 0x1f1,
|
||||
R4000 = 0x166,
|
||||
SH3 = 0x1a2,
|
||||
SH3DSP = 0x1a3,
|
||||
SH4 = 0x1a6,
|
||||
SH5 = 0x1a8,
|
||||
THUMB = 0x1c2,
|
||||
WCEMIPSV2 = 0x169,
|
||||
}
|
||||
|
||||
// IMAGE_DIRECTORY_ENTRY constants
|
||||
IMAGE_DIRECTORY_ENTRY :: enum u8 {
|
||||
EXPORT = 0,
|
||||
IMPORT = 1,
|
||||
RESOURCE = 2,
|
||||
EXCEPTION = 3,
|
||||
SECURITY = 4,
|
||||
BASERELOC = 5,
|
||||
DEBUG = 6,
|
||||
ARCHITECTURE = 7, // reserved
|
||||
GLOBALPTR = 8,
|
||||
TLS = 9,
|
||||
LOAD_CONFIG = 10,
|
||||
BOUND_IMPORT = 11,
|
||||
IAT = 12,
|
||||
DELAY_IMPORT = 13,
|
||||
COM_DESCRIPTOR = 14, // DLR Runtime headers
|
||||
_RESERVED = 15,
|
||||
}
|
||||
#assert(len(IMAGE_DIRECTORY_ENTRY) == 16)
|
||||
|
||||
|
||||
IMAGE_FILE_CHARACTERISTICS :: distinct bit_set[IMAGE_FILE_CHARACTERISTIC; u16le]
|
||||
IMAGE_FILE_CHARACTERISTIC :: enum u16le {
|
||||
RELOCS_STRIPPED = 0,
|
||||
EXECUTABLE_IMAGE = 1,
|
||||
LINE_NUMS_STRIPPED = 2,
|
||||
LOCAL_SYMS_STRIPPED = 3,
|
||||
AGGRESIVE_WS_TRIM = 4,
|
||||
LARGE_ADDRESS_AWARE = 5,
|
||||
|
||||
BYTES_REVERSED_LO = 7,
|
||||
MACHINE_32BIT = 8, // IMAGE_FILE_32BIT_MACHINE originally
|
||||
DEBUG_STRIPPED = 9,
|
||||
REMOVABLE_RUN_FROM_SWAP = 10,
|
||||
NET_RUN_FROM_SWAP = 11,
|
||||
SYSTEM = 12,
|
||||
DLL = 13,
|
||||
UP_SYSTEM_ONLY = 14,
|
||||
BYTES_REVERSED_HI = 15,
|
||||
}
|
||||
|
||||
IMAGE_SUBSYSTEM :: enum u16le {
|
||||
UNKNOWN = 0,
|
||||
NATIVE = 1,
|
||||
WINDOWS_GUI = 2,
|
||||
WINDOWS_CUI = 3,
|
||||
OS2_CUI = 5,
|
||||
POSIX_CUI = 7,
|
||||
NATIVE_WINDOWS = 8,
|
||||
WINDOWS_CE_GUI = 9,
|
||||
EFI_APPLICATION = 10,
|
||||
EFI_BOOT_SERVICE_DRIVER = 11,
|
||||
EFI_RUNTIME_DRIVER = 12,
|
||||
EFI_ROM = 13,
|
||||
XBOX = 14,
|
||||
WINDOWS_BOOT_APPLICATION = 16,
|
||||
}
|
||||
|
||||
IMAGE_DLLCHARACTERISTICS :: distinct bit_set[IMAGE_DLLCHARACTERISTIC; u16le]
|
||||
IMAGE_DLLCHARACTERISTIC :: enum u16le {
|
||||
HIGH_ENTROPY_VA = 5,
|
||||
DYNAMIC_BASE = 6,
|
||||
FORCE_INTEGRITY = 7,
|
||||
NX_COMPAT = 8,
|
||||
NO_ISOLATION = 9,
|
||||
NO_SEH = 10,
|
||||
NO_BIND = 11,
|
||||
APPCONTAINER = 12,
|
||||
WDM_DRIVER = 13,
|
||||
GUARD_CF = 14,
|
||||
TERMINAL_SERVER_AWARE = 15,
|
||||
}
|
||||
|
||||
IMAGE_DEBUG_TYPE :: enum u32le {
|
||||
UNKNOWN = 0, // An unknown value that is ignored by all tools.
|
||||
COFF = 1, // The COFF debug information (line numbers, symbol table, and string table). This type of debug information is also pointed to by fields in the file headers.
|
||||
CODEVIEW = 2, // The Visual C++ debug information.
|
||||
FPO = 3, // The frame pointer omission (FPO) information. This information tells the debugger how to interpret nonstandard stack frames, which use the EBP register for a purpose other than as a frame pointer.
|
||||
MISC = 4, // The location of DBG file.
|
||||
EXCEPTION = 5, // A copy of .pdata section.
|
||||
FIXUP = 6, // Reserved.
|
||||
OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image.
|
||||
OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image.
|
||||
BORLAND = 9, // Reserved for Borland.
|
||||
RESERVED10 = 10, // Reserved.
|
||||
CLSID = 11, // Reserved.
|
||||
REPRO = 16, // PE determinism or reproducibility.
|
||||
EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package debug_pe
|
||||
|
||||
import "core:runtime"
|
||||
import "core:io"
|
||||
|
||||
Section_Header32 :: struct {
|
||||
name: [8]u8,
|
||||
virtual_size: u32le,
|
||||
virtual_address: u32le,
|
||||
size_of_raw_data: u32le,
|
||||
pointer_to_raw_data: u32le,
|
||||
pointer_to_relocations: u32le,
|
||||
pointer_to_line_numbers: u32le,
|
||||
number_of_relocations: u16le,
|
||||
number_of_line_numbers: u16le,
|
||||
characteristics: IMAGE_SCN_CHARACTERISTICS,
|
||||
}
|
||||
|
||||
Reloc :: struct {
|
||||
virtual_address: u32le,
|
||||
symbol_table_index: u32le,
|
||||
type: IMAGE_REL,
|
||||
}
|
||||
|
||||
IMAGE_SCN_CHARACTERISTICS :: enum u32le {
|
||||
TYPE_NO_PAD = 0x00000008, // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. = 0x00000010, // Reserved for future use.
|
||||
CNT_CODE = 0x00000020, // The section contains executable code.
|
||||
CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data.
|
||||
CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data.
|
||||
LNK_OTHER = 0x00000100, // Reserved for future use.
|
||||
LNK_INFO = 0x00000200, // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. = 0x00000400, // Reserved for future use.
|
||||
LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files.
|
||||
LNK_COMDAT = 0x00001000, // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files.
|
||||
GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP).
|
||||
MEM_PURGEABLE = 0x00020000, // Reserved for future use.
|
||||
MEM_16BIT = 0x00020000, // Reserved for future use.
|
||||
MEM_LOCKED = 0x00040000, // Reserved for future use.
|
||||
MEM_PRELOAD = 0x00080000, // Reserved for future use.
|
||||
ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. Valid only for object files.
|
||||
ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. Valid only for object files.
|
||||
ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. Valid only for object files.
|
||||
ALIGN_8BYTES = 0x00400000, // Align data on an 8-byte boundary. Valid only for object files.
|
||||
ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. Valid only for object files.
|
||||
ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. Valid only for object files.
|
||||
ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. Valid only for object files.
|
||||
ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. Valid only for object files.
|
||||
ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. Valid only for object files.
|
||||
ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. Valid only for object files.
|
||||
ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. Valid only for object files.
|
||||
ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. Valid only for object files.
|
||||
ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. Valid only for object files.
|
||||
ALIGN_8192BYTES = 0x00E00000, // Align data on an 8192-byte boundary. Valid only for object files.
|
||||
LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations.
|
||||
MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed.
|
||||
MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
|
||||
MEM_NOT_PAGED = 0x08000000, // The section is not pageable.
|
||||
MEM_SHARED = 0x10000000, // The section can be shared in memory.
|
||||
MEM_EXECUTE = 0x20000000, // The section can be executed as code.
|
||||
MEM_READ = 0x40000000, // The section can be read.
|
||||
MEM_WRITE = 0x80000000, // The section can be written to.
|
||||
}
|
||||
|
||||
|
||||
IMAGE_REL :: enum u16le {
|
||||
I386_ABSOLUTE = 0x0000,
|
||||
I386_DIR16 = 0x0001,
|
||||
I386_REL16 = 0x0002,
|
||||
I386_DIR32 = 0x0006,
|
||||
I386_DIR32NB = 0x0007,
|
||||
I386_SEG12 = 0x0009,
|
||||
I386_SECTION = 0x000A,
|
||||
I386_SECREL = 0x000B,
|
||||
I386_TOKEN = 0x000C,
|
||||
I386_SECREL7 = 0x000D,
|
||||
I386_REL32 = 0x0014,
|
||||
|
||||
AMD64_ABSOLUTE = 0x0000,
|
||||
AMD64_ADDR64 = 0x0001,
|
||||
AMD64_ADDR32 = 0x0002,
|
||||
AMD64_ADDR32NB = 0x0003,
|
||||
AMD64_REL32 = 0x0004,
|
||||
AMD64_REL32_1 = 0x0005,
|
||||
AMD64_REL32_2 = 0x0006,
|
||||
AMD64_REL32_3 = 0x0007,
|
||||
AMD64_REL32_4 = 0x0008,
|
||||
AMD64_REL32_5 = 0x0009,
|
||||
AMD64_SECTION = 0x000A,
|
||||
AMD64_SECREL = 0x000B,
|
||||
AMD64_SECREL7 = 0x000C,
|
||||
AMD64_TOKEN = 0x000D,
|
||||
AMD64_SREL32 = 0x000E,
|
||||
AMD64_PAIR = 0x000F,
|
||||
AMD64_SSPAN32 = 0x0010,
|
||||
|
||||
ARM_ABSOLUTE = 0x0000,
|
||||
ARM_ADDR32 = 0x0001,
|
||||
ARM_ADDR32NB = 0x0002,
|
||||
ARM_BRANCH24 = 0x0003,
|
||||
ARM_BRANCH11 = 0x0004,
|
||||
ARM_SECTION = 0x000E,
|
||||
ARM_SECREL = 0x000F,
|
||||
ARM_MOV32 = 0x0010,
|
||||
|
||||
THUMB_MOV32 = 0x0011,
|
||||
THUMB_BRANCH20 = 0x0012,
|
||||
THUMB_BRANCH24 = 0x0014,
|
||||
THUMB_BLX23 = 0x0015,
|
||||
|
||||
ARM_PAIR = 0x0016,
|
||||
|
||||
ARM64_ABSOLUTE = 0x0000,
|
||||
ARM64_ADDR32 = 0x0001,
|
||||
ARM64_ADDR32NB = 0x0002,
|
||||
ARM64_BRANCH26 = 0x0003,
|
||||
ARM64_PAGEBASE_REL21 = 0x0004,
|
||||
ARM64_REL21 = 0x0005,
|
||||
ARM64_PAGEOFFSET_12A = 0x0006,
|
||||
ARM64_PAGEOFFSET_12L = 0x0007,
|
||||
ARM64_SECREL = 0x0008,
|
||||
ARM64_SECREL_LOW12A = 0x0009,
|
||||
ARM64_SECREL_HIGH12A = 0x000A,
|
||||
ARM64_SECREL_LOW12L = 0x000B,
|
||||
ARM64_TOKEN = 0x000C,
|
||||
ARM64_SECTION = 0x000D,
|
||||
ARM64_ADDR64 = 0x000E,
|
||||
ARM64_BRANCH19 = 0x000F,
|
||||
ARM64_BRANCH14 = 0x0010,
|
||||
ARM64_REL32 = 0x0011,
|
||||
}
|
||||
|
||||
PE_CODE_VIEW_SIGNATURE_RSDS :: u32le(0x5344_5352)
|
||||
@@ -0,0 +1,108 @@
|
||||
package debug_pe
|
||||
|
||||
COFF_SYMBOL_SIZE :: 18
|
||||
|
||||
COFF_Symbol :: struct {
|
||||
name: [8]u8,
|
||||
value: u32le,
|
||||
section_number: i16le,
|
||||
type: IMAGE_SYM_TYPE,
|
||||
storage_class: IMAGE_SYM_CLASS,
|
||||
number_of_aux_symbols: u8,
|
||||
}
|
||||
|
||||
// COFF_Symbol_Aux_Format5 describes the expected form of an aux symbol
|
||||
// attached to a section definition symbol. The PE format defines a
|
||||
// number of different aux symbol formats: format 1 for function
|
||||
// definitions, format 2 for .be and .ef symbols, and so on. Format 5
|
||||
// holds extra info associated with a section definition, including
|
||||
// number of relocations + line numbers, as well as COMDAT info. See
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
|
||||
// for more on what's going on here.
|
||||
COFF_Symbol_Aux_Format5 :: struct {
|
||||
size: u32le,
|
||||
num_relocs: u16le,
|
||||
num_line_numbers: u16le,
|
||||
checksum: u32le,
|
||||
sec_num: u16le,
|
||||
selection: IMAGE_COMDAT_SELECT,
|
||||
_: [3]u8, // padding
|
||||
}
|
||||
|
||||
IMAGE_COMDAT_SELECT :: enum u8 {
|
||||
NODUPLICATES = 1,
|
||||
ANY = 2,
|
||||
SAME_SIZE = 3,
|
||||
EXACT_MATCH = 4,
|
||||
ASSOCIATIVE = 5,
|
||||
LARGEST = 6,
|
||||
}
|
||||
|
||||
|
||||
// The symbol record is not yet assigned a section. A value of zero indicates
|
||||
// that a reference to an external symbol is defined elsewhere. A value of
|
||||
// non-zero is a common symbol with a size that is specified by the value.
|
||||
IMAGE_SYM_UNDEFINED :: 0
|
||||
// The symbol has an absolute (non-relocatable) value and is not an address.
|
||||
IMAGE_SYM_ABSOLUTE :: -1
|
||||
// The symbol provides general type or debugging information but does not
|
||||
// correspond to a section. Microsoft tools use this setting along
|
||||
// with .file records (storage class FILE).
|
||||
IMAGE_SYM_DEBUG :: -2
|
||||
|
||||
IMAGE_SYM_TYPE :: enum u16le {
|
||||
NULL = 0,
|
||||
VOID = 1,
|
||||
CHAR = 2,
|
||||
SHORT = 3,
|
||||
INT = 4,
|
||||
LONG = 5,
|
||||
FLOAT = 6,
|
||||
DOUBLE = 7,
|
||||
STRUCT = 8,
|
||||
UNION = 9,
|
||||
ENUM = 10,
|
||||
MOE = 11,
|
||||
BYTE = 12,
|
||||
WORD = 13,
|
||||
UINT = 14,
|
||||
DWORD = 15,
|
||||
PCODE = 32768,
|
||||
|
||||
DTYPE_NULL = 0,
|
||||
DTYPE_POINTER = 0x10,
|
||||
DTYPE_FUNCTION = 0x20,
|
||||
DTYPE_ARRAY = 0x30,
|
||||
}
|
||||
|
||||
IMAGE_SYM_CLASS :: enum u8 {
|
||||
NULL = 0,
|
||||
AUTOMATIC = 1,
|
||||
EXTERNAL = 2,
|
||||
STATIC = 3,
|
||||
REGISTER = 4,
|
||||
EXTERNAL_DEF = 5,
|
||||
LABEL = 6,
|
||||
UNDEFINED_LABEL = 7,
|
||||
MEMBER_OF_STRUCT = 8,
|
||||
ARGUMENT = 9,
|
||||
STRUCT_TAG = 10,
|
||||
MEMBER_OF_UNION = 11,
|
||||
UNION_TAG = 12,
|
||||
TYPE_DEFINITION = 13,
|
||||
UNDEFINED_STATIC = 14,
|
||||
ENUM_TAG = 15,
|
||||
MEMBER_OF_ENUM = 16,
|
||||
REGISTER_PARAM = 17,
|
||||
BIT_FIELD = 18,
|
||||
FAR_EXTERNAL = 68, // Not in PECOFF v8 spec
|
||||
BLOCK = 100,
|
||||
FUNCTION = 101,
|
||||
END_OF_STRUCT = 102,
|
||||
FILE = 103,
|
||||
SECTION = 104,
|
||||
WEAK_EXTERNAL = 105,
|
||||
CLR_TOKEN = 107,
|
||||
|
||||
END_OF_FUNCTION = 255,
|
||||
}
|
||||
@@ -107,7 +107,7 @@ Node :: struct {
|
||||
/* Conventions */
|
||||
/* ------------
|
||||
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
|
||||
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
|
||||
*/
|
||||
|
||||
|
||||
+227
-50
@@ -5,6 +5,7 @@ import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:io"
|
||||
|
||||
Marshal_Data_Error :: enum {
|
||||
@@ -17,25 +18,54 @@ Marshal_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
||||
Marshal_Options :: struct {
|
||||
// output based on spec
|
||||
spec: Specification,
|
||||
|
||||
// use line breaks & tab|spaces
|
||||
pretty: bool,
|
||||
|
||||
// spacing
|
||||
use_spaces: bool,
|
||||
spaces: int,
|
||||
|
||||
// state
|
||||
indentation: int,
|
||||
|
||||
// option to output uint in JSON5 & MJSON
|
||||
write_uint_as_hex: bool,
|
||||
|
||||
// mjson output options
|
||||
mjson_keys_use_quotes: bool,
|
||||
mjson_keys_use_equal_sign: bool,
|
||||
|
||||
// mjson state
|
||||
mjson_skipped_first_braces_start: bool,
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator)
|
||||
defer if err != nil {
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
marshal_to_builder(&b, v) or_return
|
||||
opt := opt
|
||||
marshal_to_builder(&b, v, &opt) or_return
|
||||
|
||||
if len(b.buf) != 0 {
|
||||
data = b.buf[:]
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v)
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v, opt)
|
||||
}
|
||||
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
if v == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return
|
||||
@@ -56,6 +86,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case i16: u = u128(i)
|
||||
case i32: u = u128(i)
|
||||
case i64: u = u128(i)
|
||||
case i128: u = u128(i)
|
||||
case int: u = u128(i)
|
||||
case u8: u = u128(i)
|
||||
case u16: u = u128(i)
|
||||
@@ -82,7 +113,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case u128be: u = u128(i)
|
||||
}
|
||||
|
||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s: string
|
||||
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
|
||||
|
||||
@@ -147,6 +192,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Procedure:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -166,88 +214,104 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
for i in 0..<array.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
io.write_byte(w, '{') or_return
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return .Unsupported_Type
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := rawptr(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
name: string
|
||||
|
||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
case: return .Unsupported_Type
|
||||
}
|
||||
}
|
||||
|
||||
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||
}
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
io.write_byte(w, '{') or_return
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
for name, i in info.names {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
opt_write_key(w, opt, name) or_return
|
||||
}
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
marshal_to_writer(w, any{data, id}) or_return
|
||||
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||
@@ -270,11 +334,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
io.write_string(w, "null") or_return
|
||||
} else {
|
||||
id := info.variants[tag-1].id
|
||||
return marshal_to_writer(w, any{v.data, id})
|
||||
return marshal_to_writer(w, any{v.data, id}, opt)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_to_writer(w, any{v.data, info.base.id})
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
@@ -330,3 +394,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// write key as quoted string or with optional quotes in mjson
|
||||
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
|
||||
case .MJSON:
|
||||
if opt.mjson_keys_use_quotes {
|
||||
io.write_quoted_string(w, name) or_return
|
||||
} else {
|
||||
io.write_string(w, name) or_return
|
||||
}
|
||||
|
||||
if opt.mjson_keys_use_equal_sign {
|
||||
io.write_string(w, " = ") or_return
|
||||
} else {
|
||||
io.write_string(w, ": ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert start byte and increase indentation on pretty
|
||||
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
// skip mjson starting braces
|
||||
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
||||
opt.mjson_skipped_first_braces_start = true
|
||||
return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
opt.indentation += 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert comma seperation and write indentations
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
if iteration > 0 {
|
||||
io.write_string(w, ", ") or_return
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
|
||||
case .MJSON:
|
||||
if iteration > 0 {
|
||||
// on pretty no commas necessary
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
} else {
|
||||
// comma seperation necessary for non pretty output!
|
||||
io.write_string(w, ", ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// decrease indent, write spacing and insert end byte
|
||||
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
||||
if opt.indentation == 0 {
|
||||
opt.mjson_skipped_first_braces_end = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt.indentation -= 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
return
|
||||
}
|
||||
|
||||
// writes current indentation level based on options
|
||||
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
|
||||
if !opt.pretty {
|
||||
return
|
||||
}
|
||||
|
||||
if opt.use_spaces {
|
||||
spaces := opt.spaces == 0 ? 4 : opt.spaces
|
||||
for _ in 0..<opt.indentation * spaces {
|
||||
io.write_byte(w, ' ') or_return
|
||||
}
|
||||
} else {
|
||||
for _ in 0..<opt.indentation {
|
||||
io.write_byte(w, '\t') or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -380,13 +380,18 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
field := any{field_ptr, type.id}
|
||||
unmarshal_value(p, field) or_return
|
||||
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
}
|
||||
continue struct_loop
|
||||
} else {
|
||||
// allows skipping unused struct fields
|
||||
parse_value(p) or_return
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
}
|
||||
continue struct_loop
|
||||
}
|
||||
|
||||
return Unsupported_Type_Error{v.id, p.curr_token}
|
||||
}
|
||||
|
||||
case reflect.Type_Info_Map:
|
||||
@@ -394,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
raw_map := (^mem.Raw_Map)(v.data)
|
||||
if raw_map.entries.allocator.procedure == nil {
|
||||
raw_map.entries.allocator = p.allocator
|
||||
if raw_map.allocator.procedure == nil {
|
||||
raw_map.allocator = p.allocator
|
||||
}
|
||||
|
||||
header := runtime.__get_map_header_runtime(raw_map, t)
|
||||
|
||||
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||
defer delete(elem_backing, p.allocator)
|
||||
|
||||
@@ -415,19 +418,16 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
delete(key, p.allocator)
|
||||
return err
|
||||
}
|
||||
|
||||
hash := runtime.Map_Hash {
|
||||
hash = runtime.default_hasher_string(&key, 0),
|
||||
key_ptr = &key,
|
||||
}
|
||||
|
||||
|
||||
key_ptr := rawptr(&key)
|
||||
|
||||
key_cstr: cstring
|
||||
if reflect.is_cstring(t.key) {
|
||||
key_cstr = cstring(raw_data(key))
|
||||
hash.key_ptr = &key_cstr
|
||||
key_ptr = &key_cstr
|
||||
}
|
||||
|
||||
set_ptr := runtime.__dynamic_map_set(header, hash, map_backing_value.data)
|
||||
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||
if set_ptr == nil {
|
||||
delete(key, p.allocator)
|
||||
}
|
||||
|
||||
+47
-15
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
p("Panic", message, loc)
|
||||
}
|
||||
|
||||
// formatted printing for cstrings
|
||||
caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// c string with temp allocator
|
||||
ctprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// sbprint formats using the default print settings and writes to buf
|
||||
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
|
||||
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
|
||||
was_prev_index := false
|
||||
|
||||
loop: for i := 0; i < end; /**/ {
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
|
||||
|
||||
prev_i := i
|
||||
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
||||
@@ -975,7 +993,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
io.write_string(fi.writer, s[:fi.width], &fi.n)
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1031,6 +1049,15 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
|
||||
io.write_string(fi.writer, "#soa{data=0x", &fi.n)
|
||||
_fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
io.write_string(fi.writer, ", index=", &fi.n)
|
||||
_fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
io.write_string(fi.writer, "}", &fi.n)
|
||||
}
|
||||
|
||||
|
||||
enum_value_to_string :: proc(val: any) -> (string, bool) {
|
||||
v := val
|
||||
v.id = runtime.typeid_base(v.id)
|
||||
@@ -1867,6 +1894,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fmt_pointer(fi, ptr, verb)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
ptr := (^runtime.Raw_Soa_Pointer)(v.data)^
|
||||
fmt_soa_pointer(fi, ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
ptr := (^rawptr)(v.data)^
|
||||
if ptr == nil {
|
||||
@@ -2038,26 +2069,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
if j > 0 {
|
||||
io.write_string(fi.writer, ", ", &fi.n)
|
||||
}
|
||||
j += 1
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
key := data + entry_type.offsets[2]
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
|
||||
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
|
||||
value := data + entry_type.offsets[3]
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ foreign odin_env {
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_vtable := &io.Stream_VTable{
|
||||
write_vtable := io.Stream_VTable{
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := u32(uintptr(s.stream_data))
|
||||
write(fd, p)
|
||||
@@ -22,14 +22,14 @@ write_vtable := &io.Stream_VTable{
|
||||
@(private="file")
|
||||
stdout := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_vtable = &write_vtable,
|
||||
stream_data = rawptr(uintptr(1)),
|
||||
},
|
||||
}
|
||||
@(private="file")
|
||||
stderr := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_vtable = &write_vtable,
|
||||
stream_data = rawptr(uintptr(2)),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
// fprintf formats according to the specififed format string and writes to fd
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
// println formats using the default print settings and writes to os.stdout
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
// printf formats according to the specififed format string and writes to os.stdout
|
||||
// printf formats according to the specified format string and writes to os.stdout
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
// eprint formats using the default print settings and writes to os.stderr
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintln formats using the default print settings and writes to os.stderr
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintf formats according to the specififed format string and writes to os.stderr
|
||||
// eprintf formats according to the specified format string and writes to os.stderr
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
+7
-3
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
return
|
||||
}
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
return h
|
||||
}
|
||||
|
||||
fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
|
||||
+65
-7
@@ -46,7 +46,7 @@ Image :: struct {
|
||||
height: int,
|
||||
channels: int,
|
||||
depth: int, // Channel depth in bits, typically 8 or 16
|
||||
pixels: bytes.Buffer,
|
||||
pixels: bytes.Buffer `fmt:"-"`,
|
||||
/*
|
||||
Some image loaders/writers can return/take an optional background color.
|
||||
For convenience, we return them as u16 so we don't need to switch on the type
|
||||
@@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil {
|
||||
^Netpbm_Info,
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
^TGA_Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
|
||||
|
||||
General_Image_Error :: enum {
|
||||
None = 0,
|
||||
Unsupported_Option,
|
||||
// File I/O
|
||||
Unable_To_Read_File,
|
||||
Unable_To_Write_File,
|
||||
@@ -376,10 +378,20 @@ QOI_Info :: struct {
|
||||
header: QOI_Header,
|
||||
}
|
||||
|
||||
TGA_Data_Type :: enum u8 {
|
||||
No_Image_Data = 0,
|
||||
Uncompressed_Color_Mapped = 1,
|
||||
Uncompressed_RGB = 2,
|
||||
Uncompressed_Black_White = 3,
|
||||
Compressed_Color_Mapped = 9,
|
||||
Compressed_RGB = 10,
|
||||
Compressed_Black_White = 11,
|
||||
}
|
||||
|
||||
TGA_Header :: struct #packed {
|
||||
id_length: u8,
|
||||
color_map_type: u8,
|
||||
data_type_code: u8,
|
||||
data_type_code: TGA_Data_Type,
|
||||
color_map_origin: u16le,
|
||||
color_map_length: u16le,
|
||||
color_map_depth: u8,
|
||||
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
|
||||
}
|
||||
#assert(size_of(TGA_Header) == 18)
|
||||
|
||||
New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
|
||||
|
||||
TGA_Footer :: struct #packed {
|
||||
extension_area_offset: u32le,
|
||||
developer_directory_offset: u32le,
|
||||
signature: [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
|
||||
}
|
||||
#assert(size_of(TGA_Footer) == 26)
|
||||
|
||||
TGA_Extension :: struct #packed {
|
||||
extension_size: u16le, // Size of this struct. If not 495 bytes it means it's an unsupported version.
|
||||
author_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
author_comments: [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
|
||||
datetime: struct {month, day, year, hour, minute, second: u16le},
|
||||
job_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
job_time: struct {hour, minute, second: u16le},
|
||||
software_id: [41]u8 `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
|
||||
software_version: struct #packed {
|
||||
number: u16le, // Version number * 100
|
||||
letter: u8 `fmt:"r"`, // " " if not used
|
||||
},
|
||||
key_color: [4]u8, // ARGB key color used at time of production
|
||||
aspect_ratio: [2]u16le, // Numerator / Denominator
|
||||
gamma: [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
|
||||
color_correction_offset: u32le, // 0 if no color correction information
|
||||
postage_stamp_offset: u32le, // 0 if no thumbnail
|
||||
scanline_offset: u32le, // 0 if no scanline table
|
||||
attributes: TGA_Alpha_Kind,
|
||||
}
|
||||
#assert(size_of(TGA_Extension) == 495)
|
||||
|
||||
TGA_Alpha_Kind :: enum u8 {
|
||||
None,
|
||||
Undefined_Ignore,
|
||||
Undefined_Retain,
|
||||
Useful,
|
||||
Premultiplied,
|
||||
}
|
||||
|
||||
TGA_Info :: struct {
|
||||
header: TGA_Header,
|
||||
image_id: string,
|
||||
footer: Maybe(TGA_Footer),
|
||||
extension: Maybe(TGA_Extension),
|
||||
}
|
||||
|
||||
// Function to help with image buffer calculations
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||
@@ -469,7 +527,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
|
||||
}
|
||||
|
||||
// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -489,7 +547,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -498,7 +556,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -518,7 +576,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -527,7 +585,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 1..4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package png
|
||||
|
||||
import "core:image"
|
||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
defer close(fd)
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||
)
|
||||
|
||||
if channels == 3 {
|
||||
|
||||
@@ -1002,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o16 = o16[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1195,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o = o[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1206,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw
|
||||
returns will likely bypass this processing pipeline.
|
||||
*/
|
||||
unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
panic("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
}
|
||||
|
||||
return img, nil
|
||||
|
||||
+341
-2
@@ -4,6 +4,7 @@
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Benoit Jacquier: tga loader
|
||||
*/
|
||||
|
||||
|
||||
@@ -14,11 +15,16 @@ import "core:mem"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:strings"
|
||||
|
||||
// TODO: alpha_premultiply support
|
||||
|
||||
Error :: image.Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
GA_Pixel :: image.GA_Pixel
|
||||
RGB_Pixel :: image.RGB_Pixel
|
||||
RGBA_Pixel :: image.RGBA_Pixel
|
||||
|
||||
@@ -57,7 +63,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
||||
}
|
||||
|
||||
header := image.TGA_Header{
|
||||
data_type_code = 0x02, // Color, uncompressed.
|
||||
data_type_code = .Uncompressed_RGB,
|
||||
dimensions = {u16le(img.width), u16le(img.height)},
|
||||
bits_per_pixel = u8(img.depth * img.channels),
|
||||
image_descriptor = 1 << 5, // Origin is top left.
|
||||
@@ -98,4 +104,337 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
|
||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
if .alpha_premultiply in options {
|
||||
return nil, .Unsupported_Option
|
||||
}
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
if .return_header in options && .return_metadata in options {
|
||||
options -= {.return_header}
|
||||
}
|
||||
|
||||
// First check for a footer.
|
||||
filesize := compress.input_size(ctx) or_return
|
||||
|
||||
footer: image.TGA_Footer
|
||||
have_valid_footer := false
|
||||
|
||||
extension: image.TGA_Extension
|
||||
have_valid_extension := false
|
||||
|
||||
if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
|
||||
if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
|
||||
if string(f.signature[:]) == image.New_TGA_Signature {
|
||||
have_valid_footer = true
|
||||
footer = f
|
||||
|
||||
if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
|
||||
if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
|
||||
if e.extension_size == size_of(image.TGA_Extension) {
|
||||
have_valid_extension = true
|
||||
extension = e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header := image.read_data(ctx, image.TGA_Header) or_return
|
||||
|
||||
// Header checks
|
||||
rle_encoding := false
|
||||
color_mapped := false
|
||||
black_white := false
|
||||
src_channels := 0
|
||||
dest_depth := header.bits_per_pixel
|
||||
dest_channels := 0
|
||||
|
||||
#partial switch header.data_type_code {
|
||||
// Supported formats: RGB(A), RGB(A) RLE
|
||||
case .Compressed_RGB:
|
||||
rle_encoding = true
|
||||
case .Uncompressed_RGB:
|
||||
// Intentionally blank
|
||||
case .Uncompressed_Black_White:
|
||||
black_white = true
|
||||
dest_depth = 24
|
||||
case .Uncompressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
case .Compressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
rle_encoding = true
|
||||
case .Compressed_Black_White:
|
||||
black_white = true
|
||||
rle_encoding = true
|
||||
dest_depth = 24
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if color_mapped {
|
||||
if header.color_map_type != 1 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
dest_depth = header.color_map_depth
|
||||
|
||||
// Expect LUT entry index to be 8 bits
|
||||
if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
}
|
||||
|
||||
switch dest_depth {
|
||||
case 15: // B5G5R5
|
||||
src_channels = 2
|
||||
dest_channels = 3
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 16: // B5G5R5A1
|
||||
src_channels = 2
|
||||
dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it.
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 24: // RGB8
|
||||
src_channels = 1 if (color_mapped || black_white) else 3
|
||||
dest_channels = 3
|
||||
case 32: // RGBA8
|
||||
src_channels = 4 if !color_mapped else 1
|
||||
dest_channels = 4
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS {
|
||||
return nil, .Image_Dimensions_Too_Large
|
||||
}
|
||||
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
|
||||
defer if err != nil {
|
||||
destroy(img)
|
||||
}
|
||||
|
||||
img.which = .TGA
|
||||
img.channels = 4 if .alpha_add_if_missing in options else dest_channels
|
||||
img.channels = 3 if .alpha_drop_if_present in options else img.channels
|
||||
|
||||
img.depth = 8
|
||||
img.width = int(header.dimensions[0])
|
||||
img.height = int(header.dimensions[1])
|
||||
|
||||
// Read Image ID if present
|
||||
image_id := ""
|
||||
if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
if .return_metadata in options {
|
||||
id := strings.trim_right_null(string(_id))
|
||||
image_id = strings.clone(id)
|
||||
}
|
||||
}
|
||||
|
||||
color_map := make([]RGBA_Pixel, header.color_map_length)
|
||||
defer delete(color_map)
|
||||
|
||||
if color_mapped {
|
||||
switch header.color_map_depth {
|
||||
case 16:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rg = lut
|
||||
color_map[i].ba = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 24:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rgb = lut
|
||||
color_map[i].a = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 32:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGBA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i] = lut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .return_metadata in options {
|
||||
info := new(image.TGA_Info)
|
||||
info.header = header
|
||||
info.image_id = image_id
|
||||
if have_valid_footer {
|
||||
info.footer = footer
|
||||
}
|
||||
if have_valid_extension {
|
||||
info.extension = extension
|
||||
}
|
||||
img.metadata = info
|
||||
}
|
||||
|
||||
if .do_not_decompress_image in options {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
|
||||
return img, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
origin_is_top := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK != 0
|
||||
origin_is_left := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0
|
||||
rle_repetition_count := 0
|
||||
read_pixel := true
|
||||
is_packet_rle := false
|
||||
|
||||
pixel: RGBA_Pixel
|
||||
|
||||
stride := img.width * dest_channels
|
||||
line := 0 if origin_is_top else img.height - 1
|
||||
|
||||
for _ in 0..<img.height {
|
||||
offset := line * stride + (0 if origin_is_left else (stride - dest_channels))
|
||||
for _ in 0..<img.width {
|
||||
// handle RLE decoding
|
||||
if rle_encoding {
|
||||
if rle_repetition_count == 0 {
|
||||
rle_cmd, err := compress.read_u8(ctx)
|
||||
if err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
is_packet_rle = (rle_cmd >> 7) != 0
|
||||
rle_repetition_count = 1 + int(rle_cmd & 0x7F)
|
||||
read_pixel = true
|
||||
} else if !is_packet_rle {
|
||||
read_pixel = rle_repetition_count > 0
|
||||
} else {
|
||||
read_pixel = false
|
||||
}
|
||||
}
|
||||
// Read pixel
|
||||
if read_pixel {
|
||||
src, src_err := compress.read_slice(ctx, src_channels)
|
||||
if src_err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
switch src_channels {
|
||||
case 1:
|
||||
// Color-mapped or Black & White
|
||||
if black_white {
|
||||
pixel = {src[0], src[0], src[0], 255}
|
||||
} else if header.color_map_depth == 24 {
|
||||
pixel = color_map[src[0]].bgra
|
||||
} else if header.color_map_depth == 16 {
|
||||
lut := color_map[src[0]]
|
||||
v := u16(lut.r) | u16(lut.g) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
}
|
||||
|
||||
case 2:
|
||||
v := u16(src[0]) | u16(src[1]) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
|
||||
case 3:
|
||||
pixel = {src[2], src[1], src[0], 255}
|
||||
case 4:
|
||||
pixel = {src[2], src[1], src[0], src[3]}
|
||||
case:
|
||||
return img, .Corrupt
|
||||
}
|
||||
}
|
||||
|
||||
// Write pixel
|
||||
copy(img.pixels.buf[offset:], pixel[:dest_channels])
|
||||
offset += dest_channels if origin_is_left else -dest_channels
|
||||
rle_repetition_count -= 1
|
||||
}
|
||||
line += 1 if origin_is_top else -1
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
return img, err
|
||||
}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil || img.width == 0 || img.height == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
if v, ok := img.metadata.(^image.TGA_Info); ok {
|
||||
delete(v.image_id)
|
||||
free(v)
|
||||
}
|
||||
|
||||
// Make destroy idempotent
|
||||
img.width = 0
|
||||
img.height = 0
|
||||
free(img)
|
||||
}
|
||||
|
||||
IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7)
|
||||
IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
|
||||
IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.TGA, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package intrinsics
|
||||
|
||||
// Package-Related
|
||||
@@ -188,6 +188,11 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
|
||||
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
// SIMD related
|
||||
@@ -295,6 +300,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
// Internal compiler use only
|
||||
|
||||
__entry_point :: proc() ---
|
||||
@@ -122,73 +122,3 @@ to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #option
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable == nil || s.impl_read_byte == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_byte == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
if s.impl_read_byte != nil {
|
||||
ok = true
|
||||
} else if s.impl_read != nil {
|
||||
ok = true
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable == nil || s.impl_write_byte == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, ok: bool = true) #optional_ok {
|
||||
r.stream = s
|
||||
if s.stream_vtable == nil || s.impl_read_rune == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, ok: bool = true) #optional_ok {
|
||||
r.stream = s
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_rune == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
if s.impl_read_rune != nil {
|
||||
ok = true
|
||||
} else if s.impl_read != nil {
|
||||
ok = true
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+53
-34
@@ -123,13 +123,6 @@ Writer_At :: struct {using stream: Stream}
|
||||
Reader_From :: struct {using stream: Stream}
|
||||
Writer_To :: struct {using stream: Stream}
|
||||
|
||||
Byte_Reader :: struct {using stream: Stream}
|
||||
Byte_Scanner :: struct {using stream: Stream}
|
||||
Byte_Writer :: struct {using stream: Stream}
|
||||
|
||||
Rune_Reader :: struct {using stream: Stream}
|
||||
Rune_Scanner :: struct {using stream: Stream}
|
||||
|
||||
|
||||
destroy :: proc(s: Stream) -> Error {
|
||||
close_err := close({s})
|
||||
@@ -147,24 +140,48 @@ destroy :: proc(s: Stream) -> Error {
|
||||
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
|
||||
// bytes read along with the error.
|
||||
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
n, err = s->impl_read(p)
|
||||
if n_read != nil {
|
||||
n_read^ += n
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_read != nil {
|
||||
n, err = s->impl_read(p)
|
||||
if n_read != nil {
|
||||
n_read^ += n
|
||||
}
|
||||
return
|
||||
} else if s.impl_read_byte != nil {
|
||||
bytes_read := 0
|
||||
defer if n_read != nil {
|
||||
n_read^ += bytes_read
|
||||
}
|
||||
for _, i in p {
|
||||
p[i] = s->impl_read_byte() or_return
|
||||
bytes_read += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return 0, .Empty
|
||||
}
|
||||
|
||||
// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
|
||||
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
n, err = s->impl_write(p)
|
||||
if n_written != nil {
|
||||
n_written^ += n
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_write != nil {
|
||||
n, err = s->impl_write(p)
|
||||
if n_written != nil {
|
||||
n_written^ += n
|
||||
}
|
||||
return
|
||||
} else if s.impl_write_byte != nil {
|
||||
bytes_written := 0
|
||||
defer if n_written != nil {
|
||||
n_written^ += bytes_written
|
||||
}
|
||||
for c in p {
|
||||
s->impl_write_byte(c) or_return
|
||||
bytes_written += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return 0, .Empty
|
||||
}
|
||||
@@ -319,7 +336,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
|
||||
|
||||
|
||||
// read_byte reads and returns the next byte from r.
|
||||
read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
read_byte :: proc(r: Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
defer if err == nil && n_read != nil {
|
||||
n_read^ += 1
|
||||
}
|
||||
@@ -339,21 +356,12 @@ read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
write_byte :: proc{
|
||||
write_byte_to_byte_writer,
|
||||
write_byte_to_writer,
|
||||
}
|
||||
|
||||
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
return _write_byte(w, c, n_written)
|
||||
}
|
||||
|
||||
write_byte_to_writer :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
return _write_byte(auto_cast w, c, n_written)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
|
||||
_write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
|
||||
defer if err == nil && n_written != nil {
|
||||
n_written^ += 1
|
||||
}
|
||||
@@ -373,7 +381,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
|
||||
}
|
||||
|
||||
// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
|
||||
read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
|
||||
read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
|
||||
defer if err == nil && n_read != nil {
|
||||
n_read^ += size
|
||||
}
|
||||
@@ -417,13 +425,21 @@ read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int,
|
||||
return
|
||||
}
|
||||
|
||||
unread_byte :: proc(s: Byte_Scanner) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_byte != nil {
|
||||
unread_byte :: proc(s: Stream) -> Error {
|
||||
if s.stream_vtable == nil {
|
||||
return .Empty
|
||||
}
|
||||
if s.impl_unread_byte != nil {
|
||||
return s->impl_unread_byte()
|
||||
}
|
||||
if s.impl_seek != nil {
|
||||
_, err := s->impl_seek(-1, .Current)
|
||||
return err
|
||||
}
|
||||
|
||||
return .Empty
|
||||
}
|
||||
unread_rune :: proc(s: Rune_Scanner) -> Error {
|
||||
unread_rune :: proc(s: Writer) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_rune != nil {
|
||||
return s->impl_unread_rune()
|
||||
}
|
||||
@@ -442,7 +458,10 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
|
||||
n_written^ += size
|
||||
}
|
||||
|
||||
if s.stream_vtable != nil && s.impl_write_rune != nil {
|
||||
if s.stream_vtable == nil {
|
||||
return 0, .Empty
|
||||
}
|
||||
if s.impl_write_rune != nil {
|
||||
return s->impl_write_rune(r)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
|
||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_console_logger :: proc(log: ^Logger) {
|
||||
destroy_console_logger :: proc(log: Logger) {
|
||||
free(log.data)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
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)
|
||||
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
+5
-6
@@ -6,7 +6,6 @@ import "core:fmt"
|
||||
|
||||
// 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,
|
||||
@@ -16,8 +15,8 @@ Logger_Level :: enum {
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
Level :: runtime.Logger_Level
|
||||
|
||||
Option :: runtime.Logger_Option
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
@@ -30,11 +29,12 @@ Option :: enum {
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
Option :: runtime.Logger_Option
|
||||
|
||||
Options :: runtime.Logger_Options
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
Options :: runtime.Logger_Options
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
@@ -52,12 +52,11 @@ Location_File_Opts :: Options{
|
||||
}
|
||||
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
|
||||
Logger :: runtime.Logger
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
@@ -66,6 +65,7 @@ Logger :: struct {
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
Logger :: runtime.Logger
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
@@ -75,7 +75,6 @@ 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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Log_Allocator :: struct {
|
||||
allocator: runtime.Allocator,
|
||||
level: Level,
|
||||
prefix: string,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
|
||||
la.allocator = allocator
|
||||
la.level = level
|
||||
la.prefix = prefix
|
||||
la.locked = false
|
||||
}
|
||||
|
||||
|
||||
log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = log_allocator_proc,
|
||||
data = la,
|
||||
}
|
||||
}
|
||||
|
||||
log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
la := (^Log_Allocator)(allocator_data)
|
||||
|
||||
padding := " " if la.prefix != "" else ""
|
||||
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Alloc_Non_Zeroed:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Free:
|
||||
if old_size != 0 {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size},
|
||||
location = location,
|
||||
)
|
||||
} else {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
|
||||
args = {la.prefix, padding, old_memory},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
case .Free_All:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Resize:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Features:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Info:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
if err != nil {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR ERROR=%v",
|
||||
args = {la.prefix, padding, error},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
@@ -25,8 +25,6 @@
|
||||
TODO: Handle +/- Infinity and NaN.
|
||||
*/
|
||||
|
||||
|
||||
//+ignore
|
||||
package math_big
|
||||
|
||||
import "core:mem"
|
||||
|
||||
@@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
|
||||
in the loop is non-zero, although very low.
|
||||
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
|
||||
|
||||
If the BPSW test and/or the addtional Frobenious test have been
|
||||
If the BPSW test and/or the additional Frobenious test have been
|
||||
performed instead of just the Miller-Rabin test with the bases 2 and 3,
|
||||
a single extra test should suffice, so such a very unlikely event will not do much harm.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
*/
|
||||
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package math_big
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -356,7 +356,7 @@ Flux_Tween :: struct($T: typeid) {
|
||||
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
return {
|
||||
values = make(map[^T]Flux_Tween(T), value_capacity),
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity)
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
|
||||
package linalg
|
||||
@@ -81,7 +81,7 @@ max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T))
|
||||
} else when N == 2 {
|
||||
out = builtin.max(a[0], a[1])
|
||||
} else when N == 3 {
|
||||
out = builtin.max(a[0], a[1], a[3])
|
||||
out = builtin.max(a[0], a[1], a[2])
|
||||
}else {
|
||||
out = builtin.max(a[0], a[1])
|
||||
for i in 2..<N {
|
||||
|
||||
@@ -94,7 +94,7 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
|
||||
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
|
||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
return v / length(v)
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
@@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize}
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
m := length(v)
|
||||
return 0 if m == 0 else v/m
|
||||
}
|
||||
@@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
|
||||
return math.sqrt(dot(v, v))
|
||||
}
|
||||
|
||||
|
||||
+379
-29
@@ -1,6 +1,7 @@
|
||||
package math
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:builtin"
|
||||
_ :: intrinsics
|
||||
|
||||
Float_Class :: enum {
|
||||
@@ -36,6 +37,11 @@ MAX_F16_PRECISION :: 4 // Maximum number of meaningful digits after the decimal
|
||||
RAD_PER_DEG :: TAU/360.0
|
||||
DEG_PER_RAD :: 360.0/TAU
|
||||
|
||||
abs :: builtin.abs
|
||||
min :: builtin.min
|
||||
max :: builtin.max
|
||||
clamp :: builtin.clamp
|
||||
|
||||
sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
|
||||
sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
|
||||
sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
|
||||
@@ -108,6 +114,92 @@ exp :: proc{
|
||||
exp_f64, exp_f64le, exp_f64be,
|
||||
}
|
||||
|
||||
pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
|
||||
pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
|
||||
pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
|
||||
pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
|
||||
pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
|
||||
pow10_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(pow10_f64(f64(x))) }
|
||||
pow10 :: proc{
|
||||
pow10_f16, pow10_f16le, pow10_f16be,
|
||||
pow10_f32, pow10_f32le, pow10_f32be,
|
||||
pow10_f64, pow10_f64le, pow10_f64be,
|
||||
}
|
||||
|
||||
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
@static pow10_pos_tab := [?]f16{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f16{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 4 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -7 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f16(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
@static pow10_pos_tab := [?]f32{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f32{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
|
||||
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
|
||||
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
|
||||
1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39,
|
||||
1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 38 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -45 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f32(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
|
||||
@static pow10_tab := [?]f64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31,
|
||||
}
|
||||
@static pow10_pos_tab32 := [?]f64{
|
||||
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
|
||||
}
|
||||
@static pow10_neg_tab32 := [?]f64{
|
||||
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 308 {
|
||||
return pow10_pos_tab32[uint(n)/32] * pow10_tab[uint(n)%32]
|
||||
}
|
||||
if -323 <= n && n <= 0 {
|
||||
return pow10_neg_tab32[uint(-n)/32] / pow10_tab[uint(-n)%32]
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
|
||||
@@ -185,16 +277,23 @@ log :: proc{
|
||||
log_f64, log_f64le, log_f64be,
|
||||
}
|
||||
|
||||
log2_f16 :: logb_f16
|
||||
log2_f16le :: logb_f16le
|
||||
log2_f16be :: logb_f16be
|
||||
log2_f32 :: logb_f32
|
||||
log2_f32le :: logb_f32le
|
||||
log2_f32be :: logb_f32be
|
||||
log2_f64 :: logb_f64
|
||||
log2_f64le :: logb_f64le
|
||||
log2_f64be :: logb_f64be
|
||||
log2 :: logb
|
||||
log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
|
||||
log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
|
||||
log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
|
||||
|
||||
log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
|
||||
log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
|
||||
log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
|
||||
|
||||
log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
|
||||
log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
|
||||
log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
|
||||
|
||||
log2 :: proc{
|
||||
log2_f16, log2_f16le, log2_f16be,
|
||||
log2_f32, log2_f32le, log2_f32be,
|
||||
log2_f64, log2_f64le, log2_f64be,
|
||||
}
|
||||
|
||||
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
|
||||
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
|
||||
@@ -607,6 +706,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
|
||||
return r
|
||||
}
|
||||
|
||||
divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
return
|
||||
}
|
||||
|
||||
floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
if (div > 0 && y < 0) || (mod < 0 && y > 0) {
|
||||
div -= 1
|
||||
mod += y
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
|
||||
shift :: F16_SHIFT
|
||||
mask :: F16_MASK
|
||||
@@ -1056,7 +1174,7 @@ is_nan :: proc{
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1073,7 +1191,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1090,7 +1208,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1312,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case x == 0:
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, 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(PI/4, y)
|
||||
}
|
||||
return copy_sign(0, y)
|
||||
}
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.75, y)
|
||||
return copy_sign(3*PI/4, y)
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case is_inf(y, 0):
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
}
|
||||
|
||||
q := atan(y / x)
|
||||
@@ -1347,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
||||
}
|
||||
|
||||
atan2 :: proc{
|
||||
atan2_f16, atan2_f16le, atan2_f16be,
|
||||
atan2_f32, atan2_f32le, atan2_f32be,
|
||||
atan2_f64, atan2_f64le, atan2_f64be,
|
||||
atan2_f64, atan2_f32, atan2_f16,
|
||||
atan2_f64le, atan2_f64be,
|
||||
atan2_f32le, atan2_f32be,
|
||||
atan2_f16le, atan2_f16be,
|
||||
}
|
||||
|
||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1)
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
|
||||
|
||||
asin_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
x := x
|
||||
z, r, s: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x*pio2_hi + 1e-120
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if ix < 0x3e500000 && ix >= 0x00100000 {
|
||||
return x
|
||||
}
|
||||
return x + x*R(x*x)
|
||||
}
|
||||
/* 1 > |x| >= 0.5 */
|
||||
z = (1 - abs(x))*0.5
|
||||
s = sqrt(z)
|
||||
r = R(z)
|
||||
if ix >= 0x3fef3333 { /* if |x| > 0.975 */
|
||||
x = pio2_hi-(2*(s+s*r)-pio2_lo)
|
||||
} else {
|
||||
f, c: f64
|
||||
/* f+c = sqrt(z) */
|
||||
f = s
|
||||
(^u64)(&f)^ &= 0xffffffff_00000000
|
||||
c = (z-f*f)/(s+f)
|
||||
x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
|
||||
}
|
||||
return -x if hx >> 31 != 0 else x
|
||||
}
|
||||
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(asin_f64(f64(x)))
|
||||
}
|
||||
asin :: proc{
|
||||
asin_f64, asin_f32, asin_f16,
|
||||
asin_f64le, asin_f64be,
|
||||
asin_f32le, asin_f32be,
|
||||
asin_f16le, asin_f16be,
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
|
||||
|
||||
acos_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
z, w, s, c, df: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if hx >> 31 != 0 {
|
||||
return 2*pio2_hi + 1e-120
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
if ix <= 0x3c600000 { /* |x| < 2**-57 */
|
||||
return pio2_hi + 1e-120
|
||||
}
|
||||
return pio2_hi - (x - (pio2_lo-x*R(x*x)))
|
||||
}
|
||||
/* x < -0.5 */
|
||||
if hx >> 31 != 0 {
|
||||
z = (1.0+x)*0.5
|
||||
s = sqrt(z)
|
||||
w = R(z)*s-pio2_lo
|
||||
return 2*(pio2_hi - (s+w))
|
||||
}
|
||||
/* x > 0.5 */
|
||||
z = (1.0-x)*0.5
|
||||
s = sqrt(z)
|
||||
df = s
|
||||
(^u64)(&df)^ &= 0xffffffff_00000000
|
||||
c = (z-df*df)/(s+df)
|
||||
w = R(z)*s+c
|
||||
return 2*(df+w)
|
||||
}
|
||||
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(acos_f64(f64(x)))
|
||||
}
|
||||
acos :: proc{
|
||||
acos_f64, acos_f32, acos_f16,
|
||||
acos_f64le, acos_f64be,
|
||||
acos_f32le, acos_f32be,
|
||||
acos_f16le, acos_f16be,
|
||||
}
|
||||
|
||||
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) - exp(-x))*0.5
|
||||
return copy_sign(((exp(x) - exp(-x))*0.5), x)
|
||||
}
|
||||
|
||||
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) + exp(-x))*0.5
|
||||
return ((exp(x) + exp(-x))*0.5)
|
||||
}
|
||||
|
||||
tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
t := exp(2*x)
|
||||
return (t - 1) / (t + 1)
|
||||
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
P0 :: -9.64399179425052238628e-1
|
||||
P1 :: -9.92877231001918586564e1
|
||||
P2 :: -1.61468768441708447952e3
|
||||
Q0 :: +1.12811678491632931402e2
|
||||
Q1 :: +2.23548839060100448583e3
|
||||
Q2 :: +4.84406305325125486048e3
|
||||
|
||||
MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
|
||||
|
||||
|
||||
x := f64(y)
|
||||
z := abs(x)
|
||||
switch {
|
||||
case z > 0.5*MAXLOG:
|
||||
if x < 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case z >= 0.625:
|
||||
s := exp(2 * z)
|
||||
z = 1 - 2/(s+1)
|
||||
if x < 0 {
|
||||
z = -z
|
||||
}
|
||||
case:
|
||||
if x == 0 {
|
||||
return T(x)
|
||||
}
|
||||
s := x * x
|
||||
z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
|
||||
}
|
||||
return T(z)
|
||||
}
|
||||
|
||||
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random element from the given slice
|
||||
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
}
|
||||
+31
-99
@@ -61,114 +61,50 @@ DEFAULT_PAGE_SIZE ::
|
||||
4 * 1024
|
||||
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||
return err
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
|
||||
return err
|
||||
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return runtime.mem_free_all(allocator, loc)
|
||||
}
|
||||
|
||||
resize :: 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, old_size, loc)
|
||||
}
|
||||
return nil
|
||||
} else if ptr == nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
runtime.copy(data, byte_slice(ptr, old_size))
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ptr := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
runtime.copy(data, old_data)
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return data, err
|
||||
return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, 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, loc)
|
||||
allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
|
||||
return set
|
||||
}
|
||||
return nil
|
||||
@@ -177,29 +113,27 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
|
||||
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, loc)
|
||||
allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc)
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc)
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free((^byte)(str), allocator, loc)
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc)
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc)
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
free(raw.entries.data, raw.entries.allocator, loc)
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -230,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
||||
@@ -245,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
@@ -260,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
array = transmute(T)s
|
||||
return
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, cap, loc)
|
||||
return m
|
||||
}
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||
|
||||
+89
-40
@@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct {
|
||||
}
|
||||
|
||||
|
||||
arena_init :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
a.peak_used = 0
|
||||
a.temp_count = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.arena_init'")
|
||||
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
@@ -51,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena := cast(^Arena)allocator_data
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
@@ -64,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(ptr, size)
|
||||
}
|
||||
return byte_slice(ptr, size), nil
|
||||
|
||||
case .Free:
|
||||
@@ -79,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -154,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size := size
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
@@ -162,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -172,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -203,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return ptr, err
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
@@ -258,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -293,6 +310,14 @@ Stack :: struct {
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.stack_init'")
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
@@ -317,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
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) {
|
||||
@@ -335,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, alignment, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -376,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, alignment)
|
||||
return raw_alloc(s, size, alignment, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -402,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment)
|
||||
data, err := raw_alloc(s, size, alignment, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -423,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
case .Query_Info:
|
||||
@@ -445,27 +472,34 @@ Small_Stack_Allocation_Header :: struct {
|
||||
|
||||
// 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,
|
||||
data: []byte,
|
||||
offset: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'small_stack_init'")
|
||||
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
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,
|
||||
data = stack,
|
||||
}
|
||||
}
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data
|
||||
|
||||
if s.data == nil {
|
||||
@@ -474,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
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) {
|
||||
@@ -490,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, align)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, align, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -525,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, align)
|
||||
return raw_alloc(s, size, align, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -548,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return byte_slice(old_memory, size), nil
|
||||
}
|
||||
|
||||
data, err := raw_alloc(s, size, align)
|
||||
data, err := raw_alloc(s, size, align, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -557,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -600,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
pool := (^Dynamic_Pool)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return dynamic_pool_alloc_bytes(pool, size)
|
||||
case .Free:
|
||||
return nil, .Mode_Not_Implemented
|
||||
@@ -620,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -771,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called")
|
||||
}
|
||||
case .Alloc_Non_Zeroed:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called")
|
||||
@@ -808,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
mode: Allocator_Mode,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
@@ -826,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
|
||||
t.backing = backing_allocator
|
||||
t.allocation_map.allocator = internals_allocator
|
||||
t.bad_free_array.allocator = internals_allocator
|
||||
|
||||
if .Free_All in query_features(t.backing) {
|
||||
t.clear_on_free_all = true
|
||||
}
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
@@ -833,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
@@ -842,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data)
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory)
|
||||
@@ -854,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
info.pointer = nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return
|
||||
}
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
})
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
||||
}
|
||||
result_ptr := raw_data(result)
|
||||
|
||||
@@ -877,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -898,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -906,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -914,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
unreachable()
|
||||
}
|
||||
|
||||
return result, err
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+1
-42
@@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int {
|
||||
}
|
||||
|
||||
compare_byte_ptrs :: proc "contextless" (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
|
||||
return runtime.memory_compare(a, b, n)
|
||||
}
|
||||
|
||||
check_zero :: proc(data: []byte) -> bool {
|
||||
|
||||
+3
-6
@@ -1,5 +1,6 @@
|
||||
package mem
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
|
||||
Raw_Any :: runtime.Raw_Any
|
||||
@@ -8,6 +9,7 @@ Raw_Cstring :: runtime.Raw_Cstring
|
||||
Raw_Slice :: runtime.Raw_Slice
|
||||
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
|
||||
Raw_Map :: runtime.Raw_Map
|
||||
Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
|
||||
|
||||
Raw_Complex64 :: struct {real, imag: f32}
|
||||
Raw_Complex128 :: struct {real, imag: f64}
|
||||
@@ -20,12 +22,7 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: runtime.raw_array_data
|
||||
raw_simd_data :: runtime.raw_simd_data
|
||||
raw_string_data :: runtime.raw_string_data
|
||||
raw_slice_data :: runtime.raw_slice_data
|
||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
||||
raw_data :: runtime.raw_data
|
||||
raw_data :: builtin.raw_data
|
||||
|
||||
|
||||
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Arena_Kind :: enum uint {
|
||||
Growing = 0, // Chained memory blocks (singly linked list).
|
||||
Static = 1, // Fixed reservation sized.
|
||||
Buffer = 2, // Uses a fixed sized buffer.
|
||||
}
|
||||
|
||||
Arena :: struct {
|
||||
kind: Arena_Kind,
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
minimum_block_size: uint,
|
||||
temp_count: uint,
|
||||
}
|
||||
|
||||
|
||||
// 1 MiB should be enough to start with
|
||||
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: 1<<20
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Growing
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Static
|
||||
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
|
||||
if len(buffer) < size_of(Memory_Block) {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
arena.kind = .Buffer
|
||||
|
||||
mem.zero_slice(buffer)
|
||||
|
||||
block_base := raw_data(buffer)
|
||||
block := (^Memory_Block)(block_base)
|
||||
block.base = block_base[size_of(Memory_Block):]
|
||||
block.reserved = len(buffer) - size_of(Memory_Block)
|
||||
block.committed = block.reserved
|
||||
block.used = 0
|
||||
|
||||
arena.curr_block = block
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
|
||||
|
||||
size := size
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
|
||||
size = mem.align_forward_uint(size, alignment)
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||
}
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
prev_used := arena.curr_block.used
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used += arena.curr_block.used - prev_used
|
||||
case .Static:
|
||||
if arena.curr_block == nil {
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
|
||||
}
|
||||
arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
|
||||
}
|
||||
fallthrough
|
||||
case .Buffer:
|
||||
if arena.curr_block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used = arena.curr_block.used
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
|
||||
if arena.curr_block != nil {
|
||||
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
|
||||
|
||||
prev_pos := arena.curr_block.used
|
||||
arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
|
||||
}
|
||||
arena.total_used = arena.curr_block.used
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
arena.total_used = 0
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if free_block := arena.curr_block; free_block != nil {
|
||||
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
}
|
||||
|
||||
arena_free_all :: proc(arena: ^Arena) {
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
for arena.curr_block != nil {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_reserved = 0
|
||||
case .Static, .Buffer:
|
||||
arena_static_reset_to(arena, 0)
|
||||
}
|
||||
arena.total_used = 0
|
||||
}
|
||||
|
||||
arena_destroy :: proc(arena: ^Arena) {
|
||||
arena_free_all(arena)
|
||||
if arena.kind != .Buffer {
|
||||
memory_block_dealloc(arena.curr_block)
|
||||
}
|
||||
arena.curr_block = nil
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
arena.temp_count = 0
|
||||
}
|
||||
|
||||
arena_growing_bootstrap_new :: proc{
|
||||
arena_growing_bootstrap_new_by_offset,
|
||||
arena_growing_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
arena_static_bootstrap_new :: proc{
|
||||
arena_static_bootstrap_new_by_offset,
|
||||
arena_static_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Growing
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Static
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
|
||||
return mem.Allocator{arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Arena)(allocator_data)
|
||||
|
||||
size, alignment := uint(size), uint(alignment)
|
||||
old_size := uint(old_size)
|
||||
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
case .Free_All:
|
||||
arena_free_all(arena)
|
||||
case .Resize:
|
||||
old_data := ([^]byte)(old_memory)
|
||||
|
||||
switch {
|
||||
case old_data == nil:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case size == old_size:
|
||||
// return old memory
|
||||
data = old_data[:size]
|
||||
return
|
||||
case size == 0:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
|
||||
// shrink data in-place
|
||||
data = old_data[:size]
|
||||
return
|
||||
}
|
||||
|
||||
new_memory := arena_alloc(arena, size, alignment) or_return
|
||||
if new_memory == nil {
|
||||
return
|
||||
}
|
||||
copy(new_memory, old_data[:old_size])
|
||||
return new_memory, nil
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
case .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Arena_Temp :: struct {
|
||||
arena: ^Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
|
||||
assert(arena != nil, "nil arena", loc)
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
memory_block_found := false
|
||||
for block := arena.curr_block; block != nil; block = block.prev {
|
||||
if block == temp.block {
|
||||
memory_block_found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !memory_block_found {
|
||||
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
|
||||
}
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
}
|
||||
|
||||
arena_temp_end :: proc{
|
||||
static_arena_temp_end,
|
||||
growing_arena_temp_end,
|
||||
}
|
||||
|
||||
arena_check_temp :: proc{
|
||||
static_arena_check_temp,
|
||||
growing_arena_check_temp,
|
||||
}
|
||||
|
||||
arena_allocator :: proc{
|
||||
static_arena_allocator,
|
||||
growing_arena_allocator,
|
||||
}
|
||||
|
||||
arena_alloc :: proc{
|
||||
static_arena_alloc,
|
||||
growing_arena_alloc,
|
||||
}
|
||||
|
||||
arena_free_all :: proc{
|
||||
static_arena_free_all,
|
||||
growing_arena_free_all,
|
||||
}
|
||||
|
||||
arena_destroy :: proc{
|
||||
static_arena_destroy,
|
||||
growing_arena_destroy,
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Growing_Arena :: struct {
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
|
||||
|
||||
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||
mask := uintptr(alignment-1)
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = uint(alignment) - uint(ptr & mask)
|
||||
}
|
||||
return alignment_offset
|
||||
}
|
||||
|
||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||
|
||||
size := uint(0)
|
||||
if arena.curr_block != nil {
|
||||
size = uint(min_size) + align_forward_offset(arena, alignment)
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
|
||||
size = uint(mem.align_forward_int(min_size, alignment))
|
||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(block_size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
|
||||
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
|
||||
if err == nil {
|
||||
arena.total_used += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
|
||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
||||
for arena.curr_block != nil {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
}
|
||||
|
||||
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
|
||||
growing_arena_free_all(arena)
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Growing_Arena
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
growing_arena_bootstrap_new :: proc{
|
||||
growing_arena_bootstrap_new_by_offset,
|
||||
growing_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{growing_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return growing_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
growing_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
Growing_Arena_Temp :: struct {
|
||||
arena: ^Growing_Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Static_Arena :: struct {
|
||||
block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_destroy :: proc(arena: ^Static_Arena) {
|
||||
memory_block_dealloc(arena.block)
|
||||
arena^ = {}
|
||||
}
|
||||
|
||||
|
||||
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
|
||||
mask := align-1
|
||||
return (ptr + mask) &~ mask
|
||||
}
|
||||
|
||||
if arena.block == nil {
|
||||
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
|
||||
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
|
||||
}
|
||||
|
||||
MINIMUM_ALIGN :: 2*align_of(uintptr)
|
||||
|
||||
defer arena.total_used = arena.block.used
|
||||
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
|
||||
}
|
||||
|
||||
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
|
||||
if arena.block != nil {
|
||||
prev_pos := arena.block.used
|
||||
arena.block.used = clamp(pos, 0, arena.block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
|
||||
}
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static_arena_free_all :: proc(arena: ^Static_Arena) {
|
||||
static_arena_reset_to(arena, 0)
|
||||
}
|
||||
|
||||
|
||||
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Static_Arena
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
static_arena_bootstrap_new :: proc{
|
||||
static_arena_bootstrap_new_by_offset,
|
||||
static_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
|
||||
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{static_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Static_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return static_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
static_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Static_Arena_Temp :: struct {
|
||||
arena: ^Static_Arena,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
|
||||
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.used = arena.block.used if arena.block != nil else 0
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
used := arena.block.used if arena.block != nil else 0
|
||||
|
||||
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
|
||||
|
||||
static_arena_reset_to(arena, temp.used)
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
|
||||
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
DEFAULT_PAGE_SIZE := uint(4096)
|
||||
|
||||
@@ -63,6 +64,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
}
|
||||
|
||||
page_size := DEFAULT_PAGE_SIZE
|
||||
assert(mem.is_power_of_two(uintptr(page_size)))
|
||||
committed := committed
|
||||
committed = clamp(committed, 0, reserved)
|
||||
|
||||
@@ -82,8 +84,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock := platform_memory_alloc(0, total_size) or_return
|
||||
|
||||
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||
commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
|
||||
assert(commit_err == nil)
|
||||
platform_memory_commit(pmblock, uint(base_offset) + committed) or_return
|
||||
|
||||
// Should be zeroed
|
||||
assert(pmblock.block.used == 0)
|
||||
@@ -95,18 +96,11 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
|
||||
sentinel := &global_platform_memory_block_sentinel
|
||||
platform_mutex_lock()
|
||||
pmblock.next = sentinel
|
||||
pmblock.prev = sentinel.prev
|
||||
pmblock.prev.next = pmblock
|
||||
pmblock.next.prev = pmblock
|
||||
platform_mutex_unlock()
|
||||
|
||||
return &pmblock.block, nil
|
||||
}
|
||||
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(block.base[block.used:])
|
||||
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
return nil
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||
size := uint(min_size) + alignment_offset
|
||||
size, size_ok := safe_add(min_size, alignment_offset)
|
||||
if !size_ok {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
|
||||
if block.used + size > block.reserved {
|
||||
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
@@ -153,12 +154,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
||||
platform_mutex_lock()
|
||||
block.prev.next = block.next
|
||||
block.next.prev = block.prev
|
||||
platform_mutex_unlock()
|
||||
|
||||
platform_memory_free(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
||||
z, did_overflow := intrinsics.overflow_add(x, y)
|
||||
return z, !did_overflow
|
||||
}
|
||||
@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
}
|
||||
|
||||
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
committed: uint,
|
||||
reserved: uint,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
|
||||
}
|
||||
}
|
||||
|
||||
platform_mutex_lock :: proc() {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
platform_mutex_unlock :: proc() {
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(init)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
|
||||
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||
if to_commit < block.committed {
|
||||
return nil
|
||||
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
|
||||
commit(block, to_commit) or_return
|
||||
block.committed = to_commit
|
||||
return nil
|
||||
|
||||
@@ -51,6 +51,7 @@ PAGE_TARGETS_INVALID :: 0x40000000
|
||||
PAGE_TARGETS_NO_UPDATE :: 0x40000000
|
||||
|
||||
ERROR_INVALID_ADDRESS :: 487
|
||||
ERROR_COMMITMENT_LIMIT :: 1455
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Kernel32 {
|
||||
@@ -76,12 +77,13 @@ _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
||||
if result == nil {
|
||||
switch err := GetLastError(); err {
|
||||
case ERROR_INVALID_ADDRESS:
|
||||
case 0:
|
||||
return .Invalid_Argument
|
||||
case ERROR_INVALID_ADDRESS, ERROR_COMMITMENT_LIMIT:
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
// TODO(bill): Handle errors correctly
|
||||
return .Invalid_Argument
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Optional_Ok,
|
||||
Optional_Second,
|
||||
Optional_Allocator_Error,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32]
|
||||
|
||||
@@ -700,6 +700,7 @@ Proc_Type :: struct {
|
||||
|
||||
Pointer_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
pointer: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
r.results = auto_cast clone(r.results)
|
||||
case ^Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
r.tag = clone(r.tag)
|
||||
case ^Multi_Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
case ^Array_Type:
|
||||
|
||||
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
|
||||
Relative_Slice = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
Soa_Pointer = 24,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4
|
||||
@@ -245,6 +246,7 @@ Type :: struct {
|
||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||
// .Multi_Pointer - 1 type: 0=element
|
||||
// .Matrix - 1 type: 0=element
|
||||
// .Soa_Pointer - 1 type: 0=element
|
||||
types: Array(Type_Index),
|
||||
|
||||
// Used by:
|
||||
|
||||
@@ -245,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||
return
|
||||
}
|
||||
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||
if .Optional_Semicolons not_in p.flags {
|
||||
return false
|
||||
}
|
||||
|
||||
prev := p.curr_tok
|
||||
if tokenizer.is_newline(prev) {
|
||||
if tokenizer.is_newline(p.curr_tok) {
|
||||
advance_token(p)
|
||||
return true
|
||||
}
|
||||
@@ -2067,7 +2062,7 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
||||
case "bounds_check": tags += {.Bounds_Check}
|
||||
case "no_bounds_check": tags += {.No_Bounds_Check}
|
||||
case "optional_ok": tags += {.Optional_Ok}
|
||||
case "optional_second": tags += {.Optional_Second}
|
||||
case "optional_allocator_error": tags += {.Optional_Allocator_Error}
|
||||
case:
|
||||
}
|
||||
}
|
||||
@@ -2235,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
return parse_call_expr(p, bd)
|
||||
|
||||
|
||||
case "soa", "simd":
|
||||
case "soa":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
@@ -2244,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case ^ast.Dynamic_Array_Type: t.tag = bd
|
||||
case ^ast.Pointer_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
|
||||
}
|
||||
return original_type
|
||||
|
||||
case "simd":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
original_type := parse_type(p)
|
||||
type := ast.unparen_expr(original_type)
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
@@ -41,9 +40,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
return
|
||||
|
||||
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
total_read: int
|
||||
length := len(data)
|
||||
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
e: win32.BOOL
|
||||
if is_console {
|
||||
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
|
||||
+6
-6
@@ -120,7 +120,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
|
||||
|
||||
data = make([]byte, int(length), allocator)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read_full(fd, data)
|
||||
@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
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))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:io"
|
||||
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
s.stream_data = f
|
||||
s.stream_vtable = _file_stream_vtable
|
||||
s.stream_vtable = &_file_stream_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
|
||||
|
||||
@(private)
|
||||
_file_stream_vtable := &io.Stream_VTable{
|
||||
_file_stream_vtable := io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
|
||||
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.fd = rawptr(handle)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment)
|
||||
|
||||
case .Free:
|
||||
|
||||
@@ -4,17 +4,17 @@ package os2
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int, zero_memory: bool) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size)
|
||||
return heap_alloc(new_size, zero_memory)
|
||||
}
|
||||
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
@@ -36,16 +36,16 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr), zero_memory)
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -72,12 +72,12 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p)
|
||||
return aligned_alloc(new_size, new_alignment, true, p)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment)
|
||||
return aligned_alloc(size, alignment, true)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment)
|
||||
|
||||
|
||||
+10
-10
@@ -6,19 +6,19 @@ import "core:runtime"
|
||||
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("LocalAppData")
|
||||
dir = get_env("LocalAppData", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
@@ -34,19 +34,19 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
|
||||
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("AppData")
|
||||
dir = get_env("AppData", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
@@ -59,13 +59,13 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
|
||||
return
|
||||
}
|
||||
|
||||
user_home_dir :: proc() -> (dir: string, err: Error) {
|
||||
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
env := "HOME"
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
env = "USERPROFILE"
|
||||
}
|
||||
if v := get_env(env); v != "" {
|
||||
if v := get_env(env, allocator); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
|
||||
+42
-15
@@ -369,27 +369,48 @@ close :: proc(fd: Handle) -> bool {
|
||||
return _unix_close(fd) == 0
|
||||
}
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 0x7fffffff // The limit on Darwin is max(i32), trying to read/write more than that fails.
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
assert(fd != -1)
|
||||
|
||||
if len(data) == 0 {
|
||||
return 0, 0
|
||||
bytes_total := len(data)
|
||||
bytes_written_total := 0
|
||||
|
||||
for bytes_written_total < bytes_total {
|
||||
bytes_to_write := min(bytes_total - bytes_written_total, MAX_RW)
|
||||
slice := data[bytes_written_total:bytes_written_total + bytes_to_write]
|
||||
bytes_written := _unix_write(fd, raw_data(slice), bytes_to_write)
|
||||
if bytes_written == -1 {
|
||||
return bytes_written_total, 1
|
||||
}
|
||||
bytes_written_total += bytes_written
|
||||
}
|
||||
bytes_written := _unix_write(fd, raw_data(data), len(data))
|
||||
if bytes_written == -1 {
|
||||
return 0, 1
|
||||
}
|
||||
return bytes_written, 0
|
||||
|
||||
return bytes_written_total, 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
|
||||
bytes_total := len(data)
|
||||
bytes_read_total := 0
|
||||
|
||||
for bytes_read_total < bytes_total {
|
||||
bytes_to_read := min(bytes_total - bytes_read_total, MAX_RW)
|
||||
slice := data[bytes_read_total:bytes_read_total + bytes_to_read]
|
||||
bytes_read := _unix_read(fd, raw_data(slice), bytes_to_read)
|
||||
if bytes_read == -1 {
|
||||
return bytes_read_total, 1
|
||||
}
|
||||
if bytes_read == 0 {
|
||||
break
|
||||
}
|
||||
bytes_read_total += bytes_read
|
||||
}
|
||||
return bytes_read, 0
|
||||
|
||||
return bytes_read_total, 0
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
@@ -596,7 +617,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
return "", Errno(get_last_error())
|
||||
}
|
||||
|
||||
path := strings.clone_from_cstring(cstring(&buf[0]), context.temp_allocator)
|
||||
path := strings.clone_from_cstring(cstring(&buf[0]))
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -625,9 +646,15 @@ access :: proc(path: string, mask: int) -> bool {
|
||||
return _unix_access(cstr, mask) == 0
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0)
|
||||
return _unix_calloc(1, size)
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, size)
|
||||
} else {
|
||||
return _unix_malloc(size)
|
||||
}
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||
|
||||
@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
return (int) (es.ThreadGetID(es.CURRENT_THREAD));
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return es.HeapAllocate(size, false);
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return es.HeapAllocate(size, zero_memory);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
|
||||
@@ -603,9 +603,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
+10
-4
@@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
|
||||
low := uintptr(offset & 0xFFFFFFFF)
|
||||
high := uintptr(offset >> 32)
|
||||
result: i64
|
||||
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
|
||||
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
|
||||
return -1 if res < 0 else result
|
||||
}
|
||||
}
|
||||
@@ -755,9 +755,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
+10
-4
@@ -605,9 +605,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -705,4 +711,4 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
res[i] = string(arg)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
+33
-4
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
current_dir: Handle = 3
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, -1
|
||||
oflags: wasi.oflags_t
|
||||
if mode & O_CREATE == O_CREATE {
|
||||
oflags += {.CREATE}
|
||||
}
|
||||
if mode & O_EXCL == O_EXCL {
|
||||
oflags += {.EXCL}
|
||||
}
|
||||
if mode & O_TRUNC == O_TRUNC {
|
||||
oflags += {.TRUNC}
|
||||
}
|
||||
|
||||
rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: rights += {.FD_READ}
|
||||
case O_WRONLY: rights += {.FD_WRITE}
|
||||
case O_RDWR: rights += {.FD_READ, .FD_WRITE}
|
||||
}
|
||||
|
||||
fdflags: wasi.fdflags_t
|
||||
if mode & O_APPEND == O_APPEND {
|
||||
fdflags += {.APPEND}
|
||||
}
|
||||
if mode & O_NONBLOCK == O_NONBLOCK {
|
||||
fdflags += {.NONBLOCK}
|
||||
}
|
||||
if mode & O_SYNC == O_SYNC {
|
||||
fdflags += {.SYNC}
|
||||
}
|
||||
fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
|
||||
return Handle(fd), Errno(err)
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
err := wasi.fd_close(wasi.fd_t(fd))
|
||||
@@ -72,7 +101,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return nil
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -96,4 +125,4 @@ heap_free :: proc(ptr: rawptr) {
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
|
||||
+10
-10
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
}
|
||||
|
||||
@(private)
|
||||
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
|
||||
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
windows_set_file_info_times(&fi, &d)
|
||||
|
||||
return fi, ERROR_NONE
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,13 +5,13 @@ import "core:io"
|
||||
stream_from_handle :: proc(fd: Handle) -> io.Stream {
|
||||
s: io.Stream
|
||||
s.stream_data = rawptr(uintptr(fd))
|
||||
s.stream_vtable = _file_stream_vtable
|
||||
s.stream_vtable = &_file_stream_vtable
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_file_stream_vtable := &io.Stream_VTable{
|
||||
_file_stream_vtable := io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
os_err: Errno
|
||||
|
||||
@@ -14,7 +14,7 @@ import "core:slice"
|
||||
// The sole exception is if 'skip_dir' is returned as true:
|
||||
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
|
||||
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool)
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool)
|
||||
|
||||
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
|
||||
// All errors that happen visiting files and directories are filtered by walk_proc
|
||||
@@ -22,28 +22,28 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno,
|
||||
// NOTE: Walking large directories can be inefficient due to the lexical sort
|
||||
// NOTE: walk does not follow symbolic links
|
||||
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc) -> os.Errno {
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno {
|
||||
info, err := os.lstat(root, context.temp_allocator)
|
||||
defer os.file_info_delete(info, context.temp_allocator)
|
||||
|
||||
skip_dir: bool
|
||||
if err != 0 {
|
||||
err, skip_dir = walk_proc(info, err)
|
||||
err, skip_dir = walk_proc(info, err, user_data)
|
||||
} else {
|
||||
err, skip_dir = _walk(info, walk_proc)
|
||||
err, skip_dir = _walk(info, walk_proc, user_data)
|
||||
}
|
||||
return 0 if skip_dir else err
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_dir: bool) {
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) {
|
||||
if !info.is_dir {
|
||||
if info.fullpath == "" && info.name == "" {
|
||||
// ignore empty things
|
||||
return
|
||||
}
|
||||
return walk_proc(info, 0)
|
||||
return walk_proc(info, 0, user_data)
|
||||
}
|
||||
|
||||
fis: []os.File_Info
|
||||
@@ -51,14 +51,14 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
|
||||
fis, err = read_dir(info.fullpath, context.temp_allocator)
|
||||
defer os.file_info_slice_delete(fis, context.temp_allocator)
|
||||
|
||||
err1, skip_dir = walk_proc(info, err)
|
||||
err1, skip_dir = walk_proc(info, err, user_data)
|
||||
if err != 0 || err1 != 0 || skip_dir {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
for fi in fis {
|
||||
err, skip_dir = _walk(fi, walk_proc)
|
||||
err, skip_dir = _walk(fi, walk_proc, user_data)
|
||||
if err != 0 || skip_dir {
|
||||
if !fi.is_dir || !skip_dir {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_array(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Array:
|
||||
if it^ < info.count {
|
||||
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Slice:
|
||||
array := (^runtime.Raw_Slice)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Dynamic_Array:
|
||||
array := (^runtime.Raw_Dynamic_Array)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_map(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Map:
|
||||
if info.map_info == nil {
|
||||
break
|
||||
}
|
||||
rm := (^runtime.Raw_Map)(val.data)
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
|
||||
for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
|
||||
if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
|
||||
key_ptr := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
|
||||
value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
|
||||
|
||||
key.data = rawptr(key_ptr)
|
||||
value.data = rawptr(value_ptr)
|
||||
key.id = info.key.id
|
||||
value.id = info.value.id
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
_ :: runtime
|
||||
_ :: mem
|
||||
|
||||
Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
|
||||
m := m
|
||||
rm := (^mem.Raw_Map)(&m)
|
||||
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
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)
|
||||
key_offset := entry_type.offsets[2]
|
||||
value_offset := entry_type.offsets[3]
|
||||
entry_size := uintptr(ed.elem_size)
|
||||
|
||||
entries = make(type_of(entries), rm.entries.len)
|
||||
|
||||
data := uintptr(rm.entries.data)
|
||||
for i in 0..<rm.entries.len {
|
||||
header := (^runtime.Map_Entry_Header)(data)
|
||||
|
||||
hash := header.hash
|
||||
key := (^K)(data + key_offset)^
|
||||
value := (^V)(data + value_offset)^
|
||||
|
||||
entries[i] = {hash, key, value}
|
||||
|
||||
data += entry_size
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
+50
-24
@@ -1,8 +1,9 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:mem"
|
||||
_ :: mem
|
||||
_ :: intrinsics
|
||||
|
||||
Type_Info :: runtime.Type_Info
|
||||
@@ -34,6 +35,7 @@ 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_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
|
||||
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
|
||||
|
||||
@@ -68,6 +70,7 @@ Type_Kind :: enum {
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +105,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Slice: return .Relative_Slice
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,6 +198,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
|
||||
}
|
||||
case Type_Info_Pointer: return v.elem.id
|
||||
case Type_Info_Multi_Pointer: return v.elem.id
|
||||
case Type_Info_Soa_Pointer: 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
|
||||
@@ -220,7 +225,7 @@ align_of_typeid :: proc(T: typeid) -> int {
|
||||
as_bytes :: proc(v: any) -> []byte {
|
||||
if v != nil {
|
||||
sz := size_of_typeid(v.id)
|
||||
return mem.slice_ptr((^byte)(v.data), sz)
|
||||
return ([^]byte)(v.data)[:sz]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -262,19 +267,19 @@ length :: proc(val: any) -> int {
|
||||
return a.count
|
||||
|
||||
case Type_Info_Slice:
|
||||
return (^mem.Raw_Slice)(val.data).len
|
||||
return (^runtime.Raw_Slice)(val.data).len
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).len
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).len
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.len
|
||||
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(val.data)^)
|
||||
} else {
|
||||
return (^mem.Raw_String)(val.data).len
|
||||
return (^runtime.Raw_String)(val.data).len
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -297,10 +302,10 @@ capacity :: proc(val: any) -> int {
|
||||
return a.count
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).cap
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).cap
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.cap
|
||||
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -340,14 +345,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
return any{data, a.elem.id}
|
||||
|
||||
case Type_Info_Slice:
|
||||
raw := (^mem.Raw_Slice)(val.data)
|
||||
raw := (^runtime.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)
|
||||
raw := (^runtime.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)
|
||||
@@ -356,7 +361,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
case Type_Info_String:
|
||||
if a.is_cstring { return nil }
|
||||
|
||||
raw := (^mem.Raw_String)(val.data)
|
||||
raw := (^runtime.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)
|
||||
@@ -725,6 +730,26 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||
panic("expected a union to reflect.get_union_variant_raw_tag")
|
||||
}
|
||||
|
||||
get_union_variant :: proc(a: any) -> any {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
id := union_variant_typeid(a)
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
return any{a.data, id}
|
||||
}
|
||||
|
||||
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
|
||||
ptr := rawptr(val)
|
||||
tag := get_union_variant_raw_tag(val^)
|
||||
mem.copy(&res, &ptr, size_of(ptr))
|
||||
set_union_variant_raw_tag(res, tag)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
||||
if a == nil { return }
|
||||
@@ -822,17 +847,17 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
ti := runtime.type_info_base(type_info_of(dst.id))
|
||||
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
||||
if value.id == nil {
|
||||
mem.zero(dst.data, ti.size)
|
||||
intrinsics.mem_zero(dst.data, ti.size)
|
||||
return true
|
||||
}
|
||||
if ti.id == runtime.typeid_base(value.id) {
|
||||
mem.copy(dst.data, value.data, ti.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, ti.size)
|
||||
return true
|
||||
}
|
||||
|
||||
if type_info_union_is_pure_maybe(info) {
|
||||
if variant := info.variants[0]; variant.id == value.id {
|
||||
mem.copy(dst.data, value.data, variant.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, variant.size)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -844,7 +869,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
if !info.no_nil {
|
||||
tag += 1
|
||||
}
|
||||
mem.copy(dst.data, value.data, variant.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, variant.size)
|
||||
set_union_variant_raw_tag(dst, tag)
|
||||
return true
|
||||
}
|
||||
@@ -1337,11 +1362,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
|
||||
case Type_Info_Slice:
|
||||
valid = true
|
||||
value = (^mem.Raw_Slice)(a.data).data
|
||||
value = (^runtime.Raw_Slice)(a.data).data
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
valid = true
|
||||
value = (^mem.Raw_Dynamic_Array)(a.data).data
|
||||
value = (^runtime.Raw_Dynamic_Array)(a.data).data
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1383,7 +1408,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
}
|
||||
|
||||
if .Simple_Compare in t.flags {
|
||||
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
}
|
||||
|
||||
t = runtime.type_info_core(t)
|
||||
@@ -1419,8 +1444,9 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
case Type_Info_String:
|
||||
if v.is_cstring {
|
||||
@@ -1474,8 +1500,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
if !including_indirect_array_recursion {
|
||||
return false
|
||||
}
|
||||
array_a := (^mem.Raw_Slice)(a.data)
|
||||
array_b := (^mem.Raw_Slice)(b.data)
|
||||
array_a := (^runtime.Raw_Slice)(a.data)
|
||||
array_b := (^runtime.Raw_Slice)(b.data)
|
||||
if array_a.len != array_b.len {
|
||||
return false
|
||||
}
|
||||
@@ -1494,8 +1520,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
if !including_indirect_array_recursion {
|
||||
return false
|
||||
}
|
||||
array_a := (^mem.Raw_Dynamic_Array)(a.data)
|
||||
array_b := (^mem.Raw_Dynamic_Array)(b.data)
|
||||
array_a := (^runtime.Raw_Dynamic_Array)(a.data)
|
||||
array_b := (^runtime.Raw_Dynamic_Array)(b.data)
|
||||
if array_a.len != array_b.len {
|
||||
return false
|
||||
}
|
||||
@@ -1503,7 +1529,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
return true
|
||||
}
|
||||
if .Simple_Compare in v.elem.flags {
|
||||
return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
|
||||
return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
|
||||
}
|
||||
|
||||
for i in 0..<array_a.len {
|
||||
|
||||
@@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
y := b.variant.(Type_Info_Multi_Pointer) or_return
|
||||
return are_types_identical(x.elem, y.elem)
|
||||
|
||||
case Type_Info_Soa_Pointer:
|
||||
y := b.variant.(Type_Info_Soa_Pointer) or_return
|
||||
return are_types_identical(x.elem, y.elem)
|
||||
|
||||
|
||||
case Type_Info_Procedure:
|
||||
y := b.variant.(Type_Info_Procedure) or_return
|
||||
switch {
|
||||
@@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
|
||||
return ok
|
||||
}
|
||||
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
#partial switch v in info.variant {
|
||||
@@ -292,6 +302,11 @@ is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Map)
|
||||
return ok
|
||||
}
|
||||
is_bit_set :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
|
||||
return ok
|
||||
}
|
||||
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Slice)
|
||||
@@ -437,6 +452,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
|
||||
case Type_Info_Multi_Pointer:
|
||||
io.write_string(w, "[^]", &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
case Type_Info_Soa_Pointer:
|
||||
io.write_string(w, "#soa ^", &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
case Type_Info_Procedure:
|
||||
io.write_string(w, "proc", &n) or_return
|
||||
if info.params == nil {
|
||||
|
||||
+39
-7
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
|
||||
values: []Type_Info_Enum_Value,
|
||||
}
|
||||
Type_Info_Map :: struct {
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
generated_struct: ^Type_Info,
|
||||
key_equal: Equal_Proc,
|
||||
key_hasher: Hasher_Proc,
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
map_info: ^Map_Info,
|
||||
}
|
||||
Type_Info_Bit_Set :: struct {
|
||||
elem: ^Type_Info,
|
||||
@@ -176,6 +174,9 @@ Type_Info_Matrix :: struct {
|
||||
column_count: int,
|
||||
// Total element count = column_count * elem_stride
|
||||
}
|
||||
Type_Info_Soa_Pointer :: struct {
|
||||
elem: ^Type_Info,
|
||||
}
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
@@ -217,6 +218,7 @@ Type_Info :: struct {
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Slice,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -299,6 +301,7 @@ Allocator_Mode :: enum byte {
|
||||
Resize,
|
||||
Query_Features,
|
||||
Query_Info,
|
||||
Alloc_Non_Zeroed,
|
||||
}
|
||||
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||
@@ -389,9 +392,32 @@ Raw_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
// The raw, type-erased representation of a map.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Raw_Map :: struct {
|
||||
hashes: []int,
|
||||
entries: Raw_Dynamic_Array,
|
||||
// A single allocation spanning all keys, values, and hashes.
|
||||
// {
|
||||
// k: Map_Cell(K) * (capacity / ks_per_cell)
|
||||
// v: Map_Cell(V) * (capacity / vs_per_cell)
|
||||
// h: Map_Cell(H) * (capacity / hs_per_cell)
|
||||
// }
|
||||
//
|
||||
// The data is allocated assuming 64-byte alignment, meaning the address is
|
||||
// always a multiple of 64. This means we have 6 bits of zeros in the pointer
|
||||
// to store the capacity. We can store a value as large as 2^6-1 or 63 in
|
||||
// there. This conveniently is the maximum log2 capacity we can have for a map
|
||||
// as Odin uses signed integers to represent capacity.
|
||||
//
|
||||
// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
|
||||
// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
|
||||
// requires no padding, meaning it can be indexed as a regular array of
|
||||
// Map_Hash directly, though for consistency sake it's written as if it were
|
||||
// an array of Map_Cell(Map_Hash).
|
||||
data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
len: int, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
|
||||
}
|
||||
|
||||
Raw_Any :: struct {
|
||||
@@ -403,6 +429,12 @@ Raw_Cstring :: struct {
|
||||
data: [^]byte,
|
||||
}
|
||||
|
||||
Raw_Soa_Pointer :: struct {
|
||||
data: rawptr,
|
||||
index: int,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// Defined internally by the compiler
|
||||
|
||||
+50
-125
@@ -143,7 +143,7 @@ free_all :: proc{mem_free_all}
|
||||
|
||||
@builtin
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(str), allocator, loc)
|
||||
return mem_free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
@@ -151,21 +151,15 @@ delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
@builtin
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), array.allocator, loc)
|
||||
return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), allocator, loc)
|
||||
return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
raw := transmute(Raw_Map)m
|
||||
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
err1 := mem_free(raw.entries.data, raw.entries.allocator, loc)
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +176,7 @@ delete :: proc{
|
||||
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
|
||||
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
|
||||
@builtin
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_allocator_error {
|
||||
return new_aligned(T, align_of(T), allocator, loc)
|
||||
}
|
||||
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
|
||||
@@ -192,7 +186,7 @@ new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator,
|
||||
}
|
||||
|
||||
@builtin
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_second {
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
|
||||
t_data := mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
|
||||
t = (^T)(raw_data(t_data))
|
||||
if t != nil {
|
||||
@@ -203,7 +197,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
make_slice_error_loc(loc, len)
|
||||
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc)
|
||||
if data == nil && size_of(E) != 0 {
|
||||
@@ -214,19 +208,19 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
}
|
||||
@(builtin)
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_dynamic_array_error_loc(loc, len, cap)
|
||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
||||
@@ -237,16 +231,16 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
return
|
||||
}
|
||||
@(builtin)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, cap)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, capacity, loc)
|
||||
return m
|
||||
}
|
||||
@(builtin)
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_second {
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_slice_error_loc(loc, len)
|
||||
data := mem_alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
|
||||
if data == nil && size_of(E) != 0 {
|
||||
@@ -278,35 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
raw_map := (^Raw_Map)(m)
|
||||
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
|
||||
entries.len = 0
|
||||
for _, i in raw_map.hashes {
|
||||
raw_map.hashes[i] = -1
|
||||
}
|
||||
map_clear_dynamic((^Raw_Map)(m), map_info(T))
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
|
||||
if m != nil {
|
||||
__dynamic_map_reserve(__get_map_header(m), capacity, loc)
|
||||
__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a map down to the current length, or the given capacity.
|
||||
|
||||
If `new_cap` is negative, then `len(m)` is used.
|
||||
|
||||
Returns false if `cap(m) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
|
||||
Shrinks the capacity of a map down to the current length.
|
||||
*/
|
||||
@builtin
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
|
||||
if m != nil {
|
||||
new_cap := new_cap if new_cap >= 0 else len(m)
|
||||
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
|
||||
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
|
||||
did_shrink = err == nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -317,30 +300,25 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
|
||||
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
|
||||
if m != nil {
|
||||
key := key
|
||||
h := __get_map_header(m)
|
||||
hash := __get_map_hash(&key)
|
||||
fr := __dynamic_map_find(h, hash)
|
||||
if fr.entry_index >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index)
|
||||
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
|
||||
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
|
||||
|
||||
__dynamic_map_erase(h, fr)
|
||||
old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
|
||||
if ok {
|
||||
deleted_key = (^K)(old_k)^
|
||||
deleted_value = (^V)(old_v)^
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
when size_of(E) == 0 {
|
||||
a.len += 1
|
||||
array.len += 1
|
||||
return 1
|
||||
} else {
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1)
|
||||
@@ -354,23 +332,26 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
|
||||
data[a.len] = arg
|
||||
}
|
||||
a.len += 1
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
arg_len := len(args)
|
||||
if arg_len <= 0 {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
when size_of(E) == 0 {
|
||||
a.len += arg_len
|
||||
array.len += arg_len
|
||||
return arg_len
|
||||
} else {
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
@@ -386,23 +367,25 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
||||
}
|
||||
a.len += arg_len
|
||||
}
|
||||
return arg_len
|
||||
}
|
||||
}
|
||||
|
||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
|
||||
args := transmute([]E)arg
|
||||
append_elems(array=array, args=args, loc=loc)
|
||||
return append_elems(array=array, args=args, loc=loc)
|
||||
}
|
||||
|
||||
|
||||
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
|
||||
for arg in args {
|
||||
append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
n += append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// The append built-in procedure appends elements to the end of a dynamic array
|
||||
@@ -410,11 +393,13 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
|
||||
|
||||
@builtin
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
prev_len := len(array)
|
||||
resize(array, len(array)+1)
|
||||
return len(array)-prev_len
|
||||
}
|
||||
|
||||
|
||||
@@ -559,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -593,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -636,15 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := new_cap * size_of(E)
|
||||
|
||||
new_data, err := a.allocator.procedure(
|
||||
a.allocator.data,
|
||||
.Resize,
|
||||
new_size,
|
||||
align_of(E),
|
||||
a.data,
|
||||
old_size,
|
||||
loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -658,11 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
@builtin
|
||||
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
|
||||
key, value := key, value
|
||||
h := __get_map_header(m)
|
||||
hash := __get_map_hash(&key)
|
||||
|
||||
data := uintptr(__dynamic_map_set(h, hash, &value, loc))
|
||||
return (^V)(data + h.value_offset)
|
||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||
}
|
||||
|
||||
|
||||
@@ -718,34 +685,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Slice)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Dynamic_Array)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
|
||||
@builtin
|
||||
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||
@@ -785,17 +724,3 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
}
|
||||
p("not yet implemented", message, loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
unreachable :: proc(message := "", loc := #caller_location) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
if message != "" {
|
||||
p("internal error", message, loc)
|
||||
} else {
|
||||
p("internal error", "entered unreachable code", loc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ raw_soa_footer :: proc{
|
||||
|
||||
|
||||
@builtin
|
||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
|
||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
if length <= 0 {
|
||||
return
|
||||
}
|
||||
@@ -138,7 +138,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
||||
}
|
||||
|
||||
@builtin
|
||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
|
||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
return make_soa_aligned(T, length, align_of(E), allocator, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
|
||||
@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = _windows_default_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
_windows_default_free(old_memory)
|
||||
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user