mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 11:41:37 -07:00
Compare commits
1396 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d71ab6f29 | |||
| 99e2f0c91e | |||
| 553f338f6f | |||
| bb72c804fb | |||
| ffc592c7cf | |||
| 3567c006e6 | |||
| b9db450a7d | |||
| 0d65c6dcf7 | |||
| dfee7c103e | |||
| 025fc2685d | |||
| 5b5154eda0 | |||
| ecf65303cd | |||
| 8ea6972496 | |||
| 9afd9f9bea | |||
| c8d3a9121b | |||
| f0950e2286 | |||
| edd78ae129 | |||
| 8738695bd8 | |||
| 76cb3b7874 | |||
| 02a098eb48 | |||
| 1f17a391c6 | |||
| a8fb346fd7 | |||
| 96fb5eb0ba | |||
| 9c7656d59a | |||
| a53bff5645 | |||
| a9182cfd8c | |||
| de6c0f682f | |||
| 9cfcb43725 | |||
| d9b590c76e | |||
| 450c535e9a | |||
| 0dc166e594 | |||
| 8ba080a66d | |||
| 7801582819 | |||
| bc882e678d | |||
| 58e173f279 | |||
| b7d7b9d6b3 | |||
| cf091a48b4 | |||
| d9bfe9e5d4 | |||
| 245a6697ef | |||
| 6226c2978d | |||
| 3d325e52c6 | |||
| 6a6d7701f9 | |||
| 50c688f0f7 | |||
| ef99d03f21 | |||
| af265250c2 | |||
| c6f463b8c9 | |||
| b7d75e2f1d | |||
| 6aa54cbe9a | |||
| 090e30f07b | |||
| f5d507a9b9 | |||
| 8e5e43f335 | |||
| b9f7b2fdfa | |||
| 59a601f2cf | |||
| b6a5c5f5d2 | |||
| a2f02b8b32 | |||
| ee4ed126e1 | |||
| c36dc91849 | |||
| 91dccf8d62 | |||
| 1fc3a25f47 | |||
| 7322b63991 | |||
| f860b09065 | |||
| 45b742be23 | |||
| d325ee4b91 | |||
| 87d6910bb8 | |||
| 9c9300ed58 | |||
| e559cf32fe | |||
| f2202db517 | |||
| fb735883be | |||
| 6a2ef1f4f3 | |||
| 051c9cb564 | |||
| eb60ec3899 | |||
| 233f47cc99 | |||
| c386c72d10 | |||
| 20eacc4a84 | |||
| a28699b42d | |||
| 4d74d5bc99 | |||
| ed371f2b0d | |||
| 66f2881a78 | |||
| 7d4e9497eb | |||
| c08809e29d | |||
| 99460c9e32 | |||
| d86df8321c | |||
| 806f56ca38 | |||
| c40b6c7c2f | |||
| 896b7145b3 | |||
| 8a2a70a3c2 | |||
| 97352538ad | |||
| c6c4ad6188 | |||
| 210f47b8ab | |||
| 94c1331c07 | |||
| d6407e9636 | |||
| df58a00564 | |||
| d546677ae7 | |||
| 04b1023988 | |||
| 9a81071687 | |||
| 48685e8bf1 | |||
| 0f697a0f26 | |||
| 8ddb493b96 | |||
| 039d9938b9 | |||
| f50ea649f6 | |||
| 6e647a88eb | |||
| 986cba584e | |||
| b427a4c8c9 | |||
| 133ee70a5b | |||
| 494612827a | |||
| 1113f23475 | |||
| 8626f58773 | |||
| 7032867421 | |||
| e6239ca3c2 | |||
| 162628000f | |||
| 55b79c078c | |||
| 570b127869 | |||
| 6179d4feb1 | |||
| 2ff5d016d5 | |||
| 854a95327a | |||
| 8a16fd7699 | |||
| 7bbcf22deb | |||
| 0324281634 | |||
| de0a3e0ab9 | |||
| d26110da7f | |||
| 60e73d91f6 | |||
| 5eeb436626 | |||
| 802333e454 | |||
| eb457d688d | |||
| fcc920ed39 | |||
| 4e70256109 | |||
| 2e4d6d2577 | |||
| 51ae21a029 | |||
| f59846377d | |||
| 8e8eb9e5cd | |||
| 88b578ca11 | |||
| 4cb16db4e9 | |||
| 338d483682 | |||
| 0ce59a9e2b | |||
| 8d43cc840a | |||
| 9ae1bfb69d | |||
| 6ec7284467 | |||
| 0ccc570ef2 | |||
| a3bb7d3028 | |||
| c45ca1bfcc | |||
| 94edf89b20 | |||
| 8d6ce0b693 | |||
| ccf4b48865 | |||
| 96eae94103 | |||
| db8b2e69dd | |||
| 82821580c7 | |||
| d23d7cf0f2 | |||
| 450a602230 | |||
| 36764779cf | |||
| 97595c4b50 | |||
| ea9fe397e5 | |||
| c482432966 | |||
| 55176e52fc | |||
| 4eb08bb096 | |||
| 881ef69063 | |||
| 761a19689d | |||
| f438153b81 | |||
| 117c0cceb1 | |||
| c949e404c3 | |||
| 3d2a6c5895 | |||
| 8f4ffbe1da | |||
| 8f3b6738ff | |||
| d50c6d72db | |||
| 15c5e92d63 | |||
| 041c7c8284 | |||
| f040ef41cb | |||
| 91ab184175 | |||
| 48a64a2c88 | |||
| 7f3795a231 | |||
| eb1d00ced6 | |||
| f41c91d36b | |||
| 6909e0d774 | |||
| d52921edd4 | |||
| dcca40033e | |||
| fed65742df | |||
| b918acd871 | |||
| a046c41c7c | |||
| 2513403014 | |||
| 4a8564aff7 | |||
| edb23db2ae | |||
| 0b01cfd853 | |||
| 0d059aa797 | |||
| 65c0255e7e | |||
| b289a27c4e | |||
| d085283f20 | |||
| b6ca10cd5e | |||
| 7416f72565 | |||
| b51be71a6f | |||
| e488cf4601 | |||
| 5d397804f7 | |||
| a5a7226885 | |||
| 2dca39b557 | |||
| b55fa268bf | |||
| c819c350d6 | |||
| d55248ab0f | |||
| 68b2d4b9e2 | |||
| 54f02f59db | |||
| 64047cbf05 | |||
| b0619980b2 | |||
| 9aa9429135 | |||
| 518f30e523 | |||
| 868aa4c14a | |||
| 1ab90de493 | |||
| 1064bcd060 | |||
| 1e21125527 | |||
| 4a8c37dd52 | |||
| 3b22c6620c | |||
| 402a165b60 | |||
| 34f9170189 | |||
| 38136e15fc | |||
| e97bf2ef35 | |||
| d6c54148d9 | |||
| cbe3791b42 | |||
| b470ceb470 | |||
| c15db05199 | |||
| 9428f792ed | |||
| 520ff731de | |||
| e9cfe698ba | |||
| 5fa66ac6a8 | |||
| 320062157f | |||
| d7d6608142 | |||
| 7f2ef2ac67 | |||
| 7124d541a1 | |||
| 3c7e45a46f | |||
| 6ec014e980 | |||
| 9b47a5eddb | |||
| 3e8c63ad31 | |||
| 15469758de | |||
| 86511d44e4 | |||
| fd4633eb25 | |||
| b0756f9e29 | |||
| c3ff1e9591 | |||
| dd3fac7523 | |||
| 13029d06b2 | |||
| 68173f4bc7 | |||
| c979c2fafa | |||
| 658435f1b9 | |||
| 3935957979 | |||
| a36640bcfc | |||
| 171d5b4012 | |||
| 1cc893f21c | |||
| 6ff2db47b4 | |||
| a11b6a9e5f | |||
| 978568684c | |||
| e8e7d3ea31 | |||
| c03cc21908 | |||
| 8ef406324b | |||
| 23d0c52bf4 | |||
| 5eee8077dd | |||
| 029cb6581b | |||
| 025e87d974 | |||
| 213a0499a1 | |||
| 1517f1d779 | |||
| 50a2493fd3 | |||
| b455ccd261 | |||
| a58650728e | |||
| b22ddb1453 | |||
| cb7dd12222 | |||
| 0484bdbb7e | |||
| 8f39c45e9b | |||
| 944396128b | |||
| bbb2164e38 | |||
| be23d83fc8 | |||
| 291ea33939 | |||
| 9455918eec | |||
| 8a99b8af3e | |||
| 12e42d92d3 | |||
| faa735d0c7 | |||
| d4e18109da | |||
| d06a0e7093 | |||
| b3a55b8b6f | |||
| ec69101101 | |||
| 17fa8cb6ef | |||
| 855ebceadc | |||
| 2720e98127 | |||
| bb80c1b059 | |||
| 85e390deba | |||
| dc317c8cd8 | |||
| 774fea1e63 | |||
| 485c606672 | |||
| 3dee3205b2 | |||
| c7a704d345 | |||
| 0fb3032b73 | |||
| 69934c3b0b | |||
| 7380b7e61b | |||
| 747a11a954 | |||
| 252be0fb41 | |||
| 600f2b7284 | |||
| 670274ad8f | |||
| e10fe91eba | |||
| fd62ee14cd | |||
| 8ece92f1f6 | |||
| 69b075782b | |||
| 6bd3a9d422 | |||
| bc9ee8e1a4 | |||
| d36c3c2590 | |||
| 52b319dbfd | |||
| 318d92f9a8 | |||
| 7ffffeeccc | |||
| f16d8e77b3 | |||
| 5b335bb88c | |||
| df2767311f | |||
| 09c26e6be0 | |||
| d2ec2d1606 | |||
| 0d87b2e8db | |||
| 1568971732 | |||
| 0e040be941 | |||
| 9737b65d9c | |||
| ad52003077 | |||
| c386509112 | |||
| c293f5b7eb | |||
| fa562ec5d6 | |||
| 529383f5b1 | |||
| f01cff7ff0 | |||
| 015fe924b8 | |||
| a5ce8a8c0b | |||
| bfdcf900ef | |||
| 54f89dd84b | |||
| da479c7628 | |||
| 3c90a05957 | |||
| d16ddf7926 | |||
| 5c519f0e8d | |||
| 74e6d9144e | |||
| 20d451396d | |||
| 60d0390ef8 | |||
| 782f1b4718 | |||
| 85f0a1067c | |||
| c08ff891ad | |||
| 168cec1e9d | |||
| 28fb35f2f7 | |||
| c1384afe2f | |||
| 547c7bce1b | |||
| 0bb93d40d3 | |||
| 27ba1d596c | |||
| 98e5523f2f | |||
| 223b66f422 | |||
| 04a4dbcdaf | |||
| ef9e31cb31 | |||
| e019673a18 | |||
| 5f27f2dd7f | |||
| cfccf73cdd | |||
| 465d003b1e | |||
| 1d6f7680a1 | |||
| 5d0f9c428a | |||
| d904ae5191 | |||
| 8a822bdd9a | |||
| d1a3842e39 | |||
| 00823ca88c | |||
| ffa14c3aad | |||
| 41b32f0da4 | |||
| c53b2198a8 | |||
| 9b278db993 | |||
| e98f1a28e6 | |||
| c8f05b7c0c | |||
| b00c4a6a8f | |||
| 84e1fb2cee | |||
| b983ac548c | |||
| fb562ea708 | |||
| cdeeeafc3f | |||
| b9a2426e57 | |||
| 81037b3091 | |||
| fc3c76f946 | |||
| 3fa971a510 | |||
| 63a0395a79 | |||
| 191223bb3c | |||
| 6f0bad816e | |||
| 94af3c2887 | |||
| e5d0417a6c | |||
| c02bda2427 | |||
| 385d2a143c | |||
| 67c1b364c4 | |||
| f029b4beb1 | |||
| 3040361fac | |||
| 44caa96d50 | |||
| 1bea0f3772 | |||
| eb0775ad53 | |||
| 8fc9566a83 | |||
| 134c7db4d2 | |||
| a0e3a99dd1 | |||
| 0edda2bea7 | |||
| ff7f139fd7 | |||
| 86a606e716 | |||
| 1e97588e7b | |||
| 3ccc0b5aa6 | |||
| 5464a605b1 | |||
| 5519749aa4 | |||
| 4a70265bfb | |||
| de0d860880 | |||
| a13e2f4578 | |||
| 01b508f182 | |||
| 2a8fa8612d | |||
| e27046098b | |||
| ca8b148fdc | |||
| c1f5be24e2 | |||
| 6cdec65ca1 | |||
| 967afd8bbb | |||
| 0ae1812f90 | |||
| eb5523d5d3 | |||
| 3f4bbbec29 | |||
| 70bd220f34 | |||
| bd3596f012 | |||
| 66ce990e0b | |||
| 690666537c | |||
| 056ba1ed13 | |||
| 93a1f2bf61 | |||
| ac5f5a33e9 | |||
| 0829ac30f7 | |||
| 9d50a04905 | |||
| 1ca7da6914 | |||
| 56e050fbc9 | |||
| 70e48e39a4 | |||
| 2b0c04f27e | |||
| 1ddbe16d28 | |||
| 09c1128d9e | |||
| 588c52a854 | |||
| 86ec3bcb44 | |||
| 9fc606de48 | |||
| 7fbee88061 | |||
| b3be2cdf9d | |||
| ff6b76986a | |||
| 5c3624eb86 | |||
| 144e357fd2 | |||
| be22f0d1e1 | |||
| 34a048f7da | |||
| ffe953b43d | |||
| b8eacfc7b6 | |||
| f8452bf1fc | |||
| 20943a81c1 | |||
| 1c4e75e83f | |||
| 9cb9964c2d | |||
| 1f8f94276e | |||
| 0d7c89e84a | |||
| a5bdb4a8e8 | |||
| 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 | |||
| d88b052d2d | |||
| 615eccb6d1 | |||
| d3c65b6ba5 | |||
| 90415e4a6e | |||
| 7352c312e0 | |||
| 0befadde1d | |||
| aef8b25a8e | |||
| ae81117f70 | |||
| d6cb105d5f | |||
| b7b9a016d3 | |||
| 5ac36b5f25 | |||
| 22bcf1ba70 | |||
| 51c705edf1 | |||
| 708a1b0cd3 | |||
| 7ab591667a | |||
| e45401bfb4 | |||
| 6b652afb8e | |||
| 0a0db23b17 | |||
| 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 | |||
| 73beed0477 | |||
| e0ecdd4b24 | |||
| 0cb1a578d0 | |||
| 5168cf03a9 | |||
| b886ae6515 | |||
| 277a973b98 | |||
| 0ec4d97bfd | |||
| e6236e5c3e | |||
| e201280844 | |||
| 8e50a6c61b | |||
| 7e6f5f89d0 | |||
| 97acc57649 | |||
| 83c8c48ed7 | |||
| 0815b4d59f | |||
| a0135080b3 | |||
| f45e8e5d47 | |||
| 98ba4beede | |||
| a9304f2fef | |||
| a0697ab057 | |||
| 2e895c72d3 | |||
| 674ebe395f | |||
| 96eecaab54 | |||
| b1ae5bc9fe | |||
| 7258588ed5 | |||
| d913155972 | |||
| 9746e25784 | |||
| d26cfd2141 | |||
| 21f2c06f4b | |||
| 727a25f41f | |||
| 3f27cb2309 | |||
| f213622982 | |||
| 4aad835a66 | |||
| 4af8a64580 | |||
| c9c3611b1d | |||
| 220dfd7440 | |||
| bce8819ed5 | |||
| 5f2b220a85 | |||
| f174c805a9 | |||
| 25869b7504 | |||
| ecd81e8a53 | |||
| d7f9f7f170 | |||
| 08f5259d77 | |||
| a9f744cb64 | |||
| b02e42c6dc | |||
| cb0273b5d7 | |||
| 8dbf45a65a | |||
| 9f64de9568 | |||
| 0ebe9ba487 | |||
| efe00e1aa6 | |||
| 2bdbce55f9 | |||
| 9614ca92f0 | |||
| d30e59f539 | |||
| a3afe617c2 | |||
| 69daac583e | |||
| b28d4b753b | |||
| e6ab4f4856 | |||
| c8ab1b7ee1 | |||
| 9f10487678 | |||
| 2542983d70 | |||
| d492fb3501 | |||
| 1829aa1638 | |||
| 4cb4173ced | |||
| 00e704b216 | |||
| 227ee0f705 | |||
| 17f47a7ab0 | |||
| 4e8bc0786d | |||
| 3d3ccf061f | |||
| 3a8adc6721 | |||
| e1748a5dd1 | |||
| 59b4c889d3 | |||
| b6408d1b3f | |||
| 3db3047f47 | |||
| 7420fbd95b | |||
| 7c990b3833 | |||
| 9c059f1a12 | |||
| fb167d1d0a | |||
| 0992239d86 | |||
| 9eb3da0474 | |||
| e91f8feedf | |||
| 22a0c3fce1 | |||
| 6c7e5748a8 | |||
| 0b0c6da8b0 | |||
| 78826071c0 | |||
| e61b73d7ad | |||
| f886632bf1 | |||
| eafa5098aa | |||
| 0571b80d37 | |||
| 80c10644dd | |||
| 041625381c | |||
| 48f56d728b | |||
| 872d391cfb | |||
| 3e6ec65dd9 | |||
| 157c87b2a2 | |||
| d3081bd889 | |||
| 2ae5bf4395 | |||
| f28547cae1 | |||
| 5332705e31 | |||
| bfb082cda4 | |||
| 37d04198ab | |||
| ae9d540c1c | |||
| c90b7c38f1 | |||
| 9e376fbda7 | |||
| 00739bf06d | |||
| e8148055ad | |||
| dd0a20ab45 | |||
| babbc304b8 | |||
| a8b44f33bd | |||
| bd48561688 | |||
| 23842a8950 | |||
| 1676c643df | |||
| e2bfb024de | |||
| 4d06a54c0c | |||
| 0a8e6169d7 | |||
| 04ae87eaef | |||
| b313d09c2c | |||
| c0d2359a91 | |||
| 51a2f09032 | |||
| f60e8031f2 | |||
| ea42613fec | |||
| b89bb87759 | |||
| 2cbb3d5a24 | |||
| 9e288b7ce8 | |||
| 776b48c10d | |||
| 199dae6cd5 | |||
| e58f45bef7 | |||
| abe122ecb7 | |||
| f8744d87b0 | |||
| 3e7f6b8751 | |||
| 6ffe814ca7 | |||
| eec9be71f6 | |||
| 888913c739 | |||
| 33d96fd28a | |||
| f1e8738af2 | |||
| 9c52a11b1b | |||
| d89c4606bd | |||
| c6903fbcd5 | |||
| 13c8149046 | |||
| 665db0f778 | |||
| 173286de65 | |||
| f2ecda8fec | |||
| 6f1222e9bf | |||
| 84a424f21e | |||
| 9b7710488b | |||
| 9f413862e9 | |||
| b8802d7df7 | |||
| b13dad02a4 | |||
| f045f8d805 | |||
| 30b7c8ad66 | |||
| 89222a0ab2 | |||
| 21e637d2b3 | |||
| 6c196931d2 | |||
| d7195b0798 | |||
| b40998de9e | |||
| 8c0c327df9 | |||
| 50cbb8a1fc | |||
| ff9d058392 | |||
| 1acc8f438b | |||
| c53426fcb4 | |||
| 5187db525f | |||
| a8bd340267 | |||
| ca1f419dc2 | |||
| fae60a6b88 | |||
| 108558ddfc | |||
| 65b8cfae82 | |||
| 4055c31cf0 | |||
| e88af4e458 | |||
| 79eb7b52d9 | |||
| 83b2bf44c4 | |||
| 670f18ad1b | |||
| 9a81716936 | |||
| f013499eea | |||
| d04f732e68 | |||
| 35fd8e7f68 | |||
| 38ff2a3ed9 | |||
| bd502d16bc | |||
| a11e17fbc3 | |||
| 8b3b659433 | |||
| f33228fd6e | |||
| df5b693de8 | |||
| ab98108441 | |||
| 6f80174f84 | |||
| 0ebe81fce2 | |||
| c75dd14308 | |||
| c166b6a21d | |||
| 6ed5cbee12 | |||
| 5b200ccdf8 | |||
| cf0f0c4b31 | |||
| 1399ecb41c | |||
| 339d6cfd41 | |||
| 7bded4f189 | |||
| c7269b9ef0 | |||
| 85688015aa | |||
| 3c6cc575c6 | |||
| 0564cb6483 | |||
| 4be92c7eb8 | |||
| 88e9eb7d0c | |||
| d19fc54c3d | |||
| 5d4291d9fa | |||
| b70cd03e9e | |||
| e91e5e1fe9 | |||
| ae57a49915 | |||
| bfcb527b42 | |||
| a343fb171d | |||
| fa2296a124 | |||
| a996cfc536 | |||
| c1d55b9296 | |||
| 5c18cca1ca | |||
| 78e6cd0c60 | |||
| 176954a6d8 | |||
| ad6b3bd95f | |||
| 762895bc45 | |||
| 592e9afa5f | |||
| a2e0373934 | |||
| fb49841b1d | |||
| 01ea0d6f1e | |||
| bb7f291f5f | |||
| 174fa9b490 | |||
| dda2ed290a | |||
| ee9908b09e | |||
| 66de1856e3 | |||
| e139d1cbe4 | |||
| cb04116caf | |||
| 62ff8daa78 | |||
| a315e7c962 | |||
| 42364f2fce | |||
| 8f600798ef | |||
| f28c268d97 | |||
| f1cff20249 | |||
| 865d88dd56 | |||
| 069c05669f | |||
| 516065d7c2 |
+14
-14
@@ -1,5 +1,5 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on: [push, pull_request]
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_linux:
|
build_linux:
|
||||||
@@ -38,10 +38,10 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin issues tests
|
- name: Odin internals tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/issues
|
cd tests/internal
|
||||||
./run.sh
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Linux i386
|
- name: Odin check examples/all for Linux i386
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||||
@@ -92,10 +92,10 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin issues tests
|
- name: Odin internals tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/issues
|
cd tests/internal
|
||||||
./run.sh
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
- name: Odin check examples/all for Darwin arm64
|
- name: Odin check examples/all for Darwin arm64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||||
@@ -156,6 +156,13 @@ jobs:
|
|||||||
cd tests\vendor
|
cd tests\vendor
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
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
|
- name: core:math/big tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -163,13 +170,6 @@ jobs:
|
|||||||
cd tests\core\math\big
|
cd tests\core\math\big
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
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
|
- name: Odin check examples/all for Windows 32bits
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mkdir dist
|
mkdir dist
|
||||||
cp odin dist
|
cp odin dist
|
||||||
|
cp libLLVM* dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: "Close Stale Issues & PRs"
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 21 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Close Stale Issues
|
||||||
|
uses: actions/stale@v7.0.0
|
||||||
|
with:
|
||||||
|
# stale-issue-message: |
|
||||||
|
# Hello!
|
||||||
|
#
|
||||||
|
# I am marking this issue as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||||
|
# - open a PR referencing and resolving the issue;
|
||||||
|
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||||
|
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||||
|
# - open a new issue with updated details and a plan on resolving the issue.
|
||||||
|
#
|
||||||
|
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||||
|
#
|
||||||
|
# stale-pr-message: |
|
||||||
|
# Hello!
|
||||||
|
#
|
||||||
|
# I am marking this PR as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||||
|
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||||
|
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||||
|
#
|
||||||
|
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||||
|
|
||||||
|
days-before-stale: 120
|
||||||
|
days-before-close: -1
|
||||||
|
exempt-draft-pr: true
|
||||||
|
ascending: true
|
||||||
|
operations-per-run: 1000
|
||||||
|
exempt-issue-labels: "ignore"
|
||||||
@@ -271,6 +271,7 @@ odin
|
|||||||
odin.dSYM
|
odin.dSYM
|
||||||
*.bin
|
*.bin
|
||||||
demo.bin
|
demo.bin
|
||||||
|
libLLVM*.so*
|
||||||
|
|
||||||
# shared collection
|
# shared collection
|
||||||
shared/
|
shared/
|
||||||
@@ -283,3 +284,4 @@ shared/
|
|||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
examples/bug/
|
examples/bug/
|
||||||
build.sh
|
build.sh
|
||||||
|
!core/debug/
|
||||||
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
|
|||||||
|
|
||||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
#### [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
|
### Articles
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,21 @@
|
|||||||
|
|
||||||
setlocal EnableDelayedExpansion
|
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 (
|
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||||
set CURR_DATE_TIME=%%j
|
set CURR_DATE_TIME=%%j
|
||||||
)
|
)
|
||||||
@@ -47,18 +62,20 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
set compiler_warnings= ^
|
set compiler_warnings= ^
|
||||||
-W4 -WX ^
|
-W4 -WX ^
|
||||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||||
|
-wd4505 ^
|
||||||
-wd4456 -wd4457
|
-wd4456 -wd4457
|
||||||
|
|
||||||
set compiler_includes= ^
|
set compiler_includes= ^
|
||||||
/Isrc\
|
/Isrc\
|
||||||
set libs= ^
|
set libs= ^
|
||||||
kernel32.lib ^
|
kernel32.lib ^
|
||||||
|
Synchronization.lib ^
|
||||||
bin\llvm\windows\LLVM-C.lib
|
bin\llvm\windows\LLVM-C.lib
|
||||||
|
|
||||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set linker_flags=%linker_flags% -debug
|
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||||
) else ( rem Release
|
) else ( rem Release
|
||||||
set linker_flags=%linker_flags% -debug
|
set linker_flags=%linker_flags% -debug
|
||||||
)
|
)
|
||||||
@@ -79,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
|
|||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
:end_of_build
|
:end_of_build
|
||||||
|
|||||||
+74
-25
@@ -1,12 +1,20 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
GIT_SHA=$(git rev-parse --short HEAD)
|
: ${CXX=clang++}
|
||||||
|
: ${CPPFLAGS=}
|
||||||
|
: ${CXXFLAGS=}
|
||||||
|
: ${LDFLAGS=}
|
||||||
|
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
|
||||||
|
|
||||||
|
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
|
||||||
|
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||||
|
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
|
||||||
|
|
||||||
|
GIT_SHA=$(git rev-parse --short HEAD || :)
|
||||||
|
if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
|
||||||
|
|
||||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||||
LDFLAGS="-pthread -lm -lstdc++"
|
|
||||||
CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
|
|
||||||
CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
|
||||||
CC=clang
|
|
||||||
OS=$(uname)
|
OS=$(uname)
|
||||||
|
|
||||||
panic() {
|
panic() {
|
||||||
@@ -18,13 +26,13 @@ version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
|
|||||||
|
|
||||||
config_darwin() {
|
config_darwin() {
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
LLVM_CONFIG=llvm-config
|
: ${LLVM_CONFIG=llvm-config}
|
||||||
|
|
||||||
# allow for arm only llvm's with version 13
|
# allow for arm only llvm's with version 13
|
||||||
if [ ARCH == arm64 ]; then
|
if [ ARCH == arm64 ]; then
|
||||||
MIN_LLVM_VERSION=("13.0.0")
|
MIN_LLVM_VERSION=("13.0.0")
|
||||||
else
|
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")
|
MIN_LLVM_VERSION=("11.1.0")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -36,35 +44,57 @@ config_darwin() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
MAX_LLVM_VERSION=("14.999.999")
|
||||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
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 -framework System"
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||||
}
|
}
|
||||||
|
|
||||||
config_freebsd() {
|
config_freebsd() {
|
||||||
LLVM_CONFIG=/usr/local/bin/llvm-config11
|
: ${LLVM_CONFIG=}
|
||||||
|
|
||||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
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)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
}
|
}
|
||||||
|
|
||||||
config_openbsd() {
|
config_openbsd() {
|
||||||
LLVM_CONFIG=/usr/local/bin/llvm-config
|
: ${LLVM_CONFIG=/usr/local/bin/llvm-config}
|
||||||
|
|
||||||
LDFLAGS="$LDFLAGS -liconv"
|
LDFLAGS="$LDFLAGS -liconv"
|
||||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
}
|
}
|
||||||
|
|
||||||
config_linux() {
|
config_linux() {
|
||||||
if which llvm-config > /dev/null 2>&1; then
|
: ${LLVM_CONFIG=}
|
||||||
LLVM_CONFIG=llvm-config
|
|
||||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
if [ ! "$LLVM_CONFIG" ]; then
|
||||||
LLVM_CONFIG=llvm-config-11
|
if which llvm-config > /dev/null 2>&1; then
|
||||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
LLVM_CONFIG=llvm-config
|
||||||
LLVM_CONFIG=llvm-config-11-64
|
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||||
else
|
LLVM_CONFIG=llvm-config-11
|
||||||
panic "Unable to find LLVM-config"
|
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||||
|
LLVM_CONFIG=llvm-config-11-64
|
||||||
|
else
|
||||||
|
panic "Unable to find LLVM-config"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MIN_LLVM_VERSION=("11.0.0")
|
MIN_LLVM_VERSION=("11.0.0")
|
||||||
@@ -73,9 +103,20 @@ config_linux() {
|
|||||||
panic "Requirement: llvm-config must be base version greater than 11"
|
panic "Requirement: llvm-config must be base version greater than 11"
|
||||||
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 -ldl"
|
LDFLAGS="$LDFLAGS -ldl"
|
||||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
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() {
|
build_odin() {
|
||||||
@@ -97,7 +138,7 @@ build_odin() {
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
set -x
|
set -x
|
||||||
$CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||||
set +x
|
set +x
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +146,14 @@ run_demo() {
|
|||||||
./odin run examples/demo/demo.odin -file
|
./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
|
case $OS in
|
||||||
Linux)
|
Linux)
|
||||||
config_linux
|
config_linux
|
||||||
|
|||||||
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
|
|||||||
call build.bat
|
call build.bat
|
||||||
popd
|
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) {
|
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||||
s.stream_data = rw
|
s.stream_data = rw
|
||||||
s.stream_vtable = _read_writer_vtable
|
s.stream_vtable = &_read_writer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(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) {
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
b := (^Read_Writer)(s.stream_data).r
|
b := (^Read_Writer)(s.stream_data).r
|
||||||
return reader_read(b, p)
|
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 {
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Read_Writer)(s.stream_data).r
|
b := (^Read_Writer)(s.stream_data).r
|
||||||
return reader_unread_byte(b)
|
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 converts a Reader into an io.Stream
|
||||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _reader_vtable
|
s.stream_vtable = &_reader_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_reader_vtable := &io.Stream_VTable{
|
_reader_vtable := io.Stream_VTable{
|
||||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Reader)(s.stream_data)
|
b := (^Reader)(s.stream_data)
|
||||||
reader_destroy(b)
|
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 {
|
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||||
switch s._err {
|
switch s._err {
|
||||||
case .EOF, nil:
|
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 converts a Writer into an io.Stream
|
||||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _writer_vtable
|
s.stream_vtable = &_writer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_writer_vtable := &io.Stream_VTable{
|
_writer_vtable := io.Stream_VTable{
|
||||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Writer)(s.stream_data)
|
b := (^Writer)(s.stream_data)
|
||||||
writer_destroy(b)
|
writer_destroy(b)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ jmag :: proc(value: Quaternion) -> Float ---
|
|||||||
kmag :: proc(value: Quaternion) -> Float ---
|
kmag :: proc(value: Quaternion) -> Float ---
|
||||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||||
|
|
||||||
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||||
|
|
||||||
min :: proc(values: ..T) -> T ---
|
min :: proc(values: ..T) -> T ---
|
||||||
max :: proc(values: ..T) -> T ---
|
max :: proc(values: ..T) -> T ---
|
||||||
|
|||||||
+12
-7
@@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return copy(b.buf[m:], p), nil
|
return copy(b.buf[m:], p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||||
|
return buffer_write(b, ([^]byte)(ptr)[:size])
|
||||||
|
}
|
||||||
|
|
||||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, len(s))
|
m, ok := _buffer_try_grow(b, len(s))
|
||||||
@@ -229,17 +233,18 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||||
|
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||||
|
}
|
||||||
|
|
||||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
|
|
||||||
if offset < 0 || offset >= len(b.buf) {
|
if uint(offset) >= len(b.buf) {
|
||||||
err = .Invalid_Offset
|
err = .Invalid_Offset
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
n = copy(p, b.buf[offset:])
|
||||||
if 0 <= offset && offset < len(b.buf) {
|
|
||||||
n = copy(p, b.buf[offset:])
|
|
||||||
}
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
b.last_read = .Read
|
b.last_read = .Read
|
||||||
}
|
}
|
||||||
@@ -366,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) {
|
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _buffer_vtable
|
s.stream_vtable = &_buffer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_buffer_vtable := &io.Stream_VTable{
|
_buffer_vtable := io.Stream_VTable{
|
||||||
impl_size = proc(s: io.Stream) -> i64 {
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
b := (^Buffer)(s.stream_data)
|
b := (^Buffer)(s.stream_data)
|
||||||
return i64(buffer_capacity(b))
|
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 {
|
index_rune :: proc(s: []byte, r: rune) -> int {
|
||||||
switch {
|
switch {
|
||||||
case 0 <= r && r < utf8.RUNE_SELF:
|
case u32(r) < utf8.RUNE_SELF:
|
||||||
return index_byte(s, byte(r))
|
return index_byte(s, byte(r))
|
||||||
|
|
||||||
case r == utf8.RUNE_ERROR:
|
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) {
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
s.stream_data = r
|
s.stream_data = r
|
||||||
s.stream_vtable = _reader_vtable
|
s.stream_vtable = &_reader_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
|||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_reader_vtable := &io.Stream_VTable{
|
_reader_vtable := io.Stream_VTable{
|
||||||
impl_size = proc(s: io.Stream) -> i64 {
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
r := (^Reader)(s.stream_data)
|
r := (^Reader)(s.stream_data)
|
||||||
return reader_size(r)
|
return reader_size(r)
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
|
|||||||
|
|
||||||
|
|
||||||
quote_string :: proc(s: string) -> []byte {
|
quote_string :: proc(s: string) -> []byte {
|
||||||
b := strings.make_builder(0, len(s)+2)
|
b := strings.builder_make(0, len(s)+2)
|
||||||
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
||||||
return b.buf[:]
|
return b.buf[:]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ foreign libc {
|
|||||||
// 7.3.8 Power and absolute-value functions
|
// 7.3.8 Power and absolute-value functions
|
||||||
cabs :: proc(z: complex_double) -> complex_double ---
|
cabs :: proc(z: complex_double) -> complex_double ---
|
||||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
cabsf :: proc(z: complex_float) -> complex_float ---
|
||||||
cpow :: proc(z: complex_double) -> complex_double ---
|
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||||
cpowf :: proc(z: complex_float) -> complex_float ---
|
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||||
csqrtf :: proc(z: complex_float) -> complex_float ---
|
csqrtf :: proc(z: complex_float) -> complex_float ---
|
||||||
|
|
||||||
|
|||||||
+15
-2
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
|
|||||||
// EDOM,
|
// EDOM,
|
||||||
// EILSEQ
|
// EILSEQ
|
||||||
// ERANGE
|
// ERANGE
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
when ODIN_OS == .Linux {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
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 ---
|
_get_errno :: proc() -> ^int ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
|
|||||||
fma :: proc{libc_fma, libc_fmaf}
|
fma :: proc{libc_fma, libc_fmaf}
|
||||||
|
|
||||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
// 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
|
// procedure. This is done because the foreign block is marked @(private) and
|
||||||
// aliasing functions does not remove privateness from the entity.
|
// aliasing functions does not remove privateness from the entity.
|
||||||
acosf :: proc{libc_acosf}
|
acosf :: proc{libc_acosf}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
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 {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
@@ -196,7 +199,7 @@ foreign libc {
|
|||||||
getc :: proc(stream: ^FILE) -> int ---
|
getc :: proc(stream: ^FILE) -> int ---
|
||||||
getchar :: proc() -> int ---
|
getchar :: proc() -> int ---
|
||||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||||
putchar :: proc() -> int ---
|
putchar :: proc(c: int) -> int ---
|
||||||
puts :: proc(s: cstring) -> int ---
|
puts :: proc(s: cstring) -> int ---
|
||||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||||
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||||
|
|||||||
+27
-1
@@ -88,7 +88,6 @@ foreign libc {
|
|||||||
srand :: proc(seed: uint) ---
|
srand :: proc(seed: uint) ---
|
||||||
|
|
||||||
// 7.22.3 Memory management functions
|
// 7.22.3 Memory management functions
|
||||||
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
|
|
||||||
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
||||||
free :: proc(ptr: rawptr) ---
|
free :: proc(ptr: rawptr) ---
|
||||||
malloc :: proc(size: size_t) -> 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 ---
|
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
||||||
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ tss_dtor_t :: proc "c" (rawptr)
|
|||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:libucrt.lib",
|
"system:libucrt.lib",
|
||||||
"system:msvcprt.lib"
|
"system:msvcprt.lib",
|
||||||
}
|
}
|
||||||
|
|
||||||
thrd_success :: 0 // _Thrd_success
|
thrd_success :: 0 // _Thrd_success
|
||||||
@@ -77,7 +77,7 @@ when ODIN_OS == .Windows {
|
|||||||
when ODIN_OS == .Linux {
|
when ODIN_OS == .Linux {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:c",
|
"system:c",
|
||||||
"system:pthread"
|
"system:pthread",
|
||||||
}
|
}
|
||||||
|
|
||||||
thrd_success :: 0
|
thrd_success :: 0
|
||||||
@@ -108,7 +108,7 @@ when ODIN_OS == .Linux {
|
|||||||
cnd_destroy :: proc(cond: ^cnd_t) ---
|
cnd_destroy :: proc(cond: ^cnd_t) ---
|
||||||
cnd_init :: proc(cond: ^cnd_t) -> int ---
|
cnd_init :: proc(cond: ^cnd_t) -> int ---
|
||||||
cnd_signal :: proc(cond: ^cnd_t) -> int ---
|
cnd_signal :: proc(cond: ^cnd_t) -> int ---
|
||||||
cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
|
cnd_timedwait :: proc(cond: ^cnd_t, mtx: ^mtx_t, ts: ^timespec) -> int ---
|
||||||
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
|
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
|
||||||
|
|
||||||
// 7.26.4 Mutex functions
|
// 7.26.4 Mutex functions
|
||||||
|
|||||||
@@ -212,6 +212,8 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
|
// the is literally no need for it
|
||||||
b := make([]u8, size, context.temp_allocator)
|
b := make([]u8, size, context.temp_allocator)
|
||||||
_, e := z.input->impl_read(b[:])
|
_, e := z.input->impl_read(b[:])
|
||||||
if e == .None {
|
if e == .None {
|
||||||
@@ -294,6 +296,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")
|
@(optimization_mode="speed")
|
||||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
@@ -321,7 +341,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
|||||||
return res, .None
|
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
|
package gzip
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+ignore
|
//+build ignore
|
||||||
package zlib
|
package zlib
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -185,6 +185,33 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||||
|
- index: The bit index. Can be an enum member.
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- ok: Whether or not we managed to unset requested bit.
|
||||||
|
|
||||||
|
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||||
|
*/
|
||||||
|
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||||
|
|
||||||
|
idx := int(index) - ba.bias
|
||||||
|
|
||||||
|
if ba == nil || int(index) < ba.bias { return false }
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
leg_index := idx >> INDEX_SHIFT
|
||||||
|
bit_index := idx & INDEX_MASK
|
||||||
|
|
||||||
|
resize_if_needed(ba, leg_index) or_return
|
||||||
|
|
||||||
|
ba.max_index = max(idx, ba.max_index)
|
||||||
|
ba.bits[leg_index] &= ~(1 << uint(bit_index))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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) {
|
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||||
j := j
|
j := j
|
||||||
queue := pq.queue[:]
|
queue := pq.queue[:]
|
||||||
n := builtin.len(queue)
|
|
||||||
for 0 <= j {
|
for 0 <= j {
|
||||||
i := (j-1)/2
|
i := (j-1)/2
|
||||||
if i == j || !pq.less(queue[j], queue[i]) {
|
if i == j || !pq.less(queue[j], queue[i]) {
|
||||||
|
|||||||
@@ -69,6 +69,23 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
|||||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||||
return q.data[idx]
|
return q.data[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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))
|
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||||
|
|
||||||
@@ -82,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
|||||||
return &q.data[idx]
|
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 an element to the back of the queue
|
||||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||||
if space(q^) == 0 {
|
if space(q^) == 0 {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package container_small_array
|
package container_small_array
|
||||||
|
|
||||||
import "core:builtin"
|
import "core:builtin"
|
||||||
|
import "core:runtime"
|
||||||
|
_ :: runtime
|
||||||
|
|
||||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||||
data: [N]T,
|
data: [N]T,
|
||||||
@@ -8,40 +10,54 @@ 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
|
return a.len
|
||||||
}
|
}
|
||||||
|
|
||||||
cap :: proc(a: $A/Small_Array) -> int {
|
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||||
return builtin.len(a.data)
|
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
|
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]
|
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]
|
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]
|
return &a.data[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
|
||||||
|
if index < 0 || index >= a.len {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
return a.data[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
|
||||||
|
if index < 0 || index >= a.len {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
return &a.data[index], true
|
||||||
|
}
|
||||||
|
|
||||||
|
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||||
a.data[index] = item
|
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))
|
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^) {
|
if a.len < cap(a^) {
|
||||||
a.data[a.len] = item
|
a.data[a.len] = item
|
||||||
a.len += 1
|
a.len += 1
|
||||||
@@ -50,7 +66,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
|||||||
return false
|
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^) {
|
if a.len < cap(a^) {
|
||||||
a.len += 1
|
a.len += 1
|
||||||
data := slice(a)
|
data := slice(a)
|
||||||
@@ -61,14 +77,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
|||||||
return false
|
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)
|
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||||
item := a.data[a.len-1]
|
item := a.data[a.len-1]
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
return item
|
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)
|
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||||
item := a.data[0]
|
item := a.data[0]
|
||||||
s := slice(a)
|
s := slice(a)
|
||||||
@@ -77,7 +93,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
|||||||
return item
|
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 {
|
if N > 0 && a.len > 0 {
|
||||||
item = a.data[a.len-1]
|
item = a.data[a.len-1]
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
@@ -86,31 +102,60 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
|||||||
return
|
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 {
|
if N > 0 && a.len > 0 {
|
||||||
item = a.data[0]
|
item = a.data[0]
|
||||||
s := slice(a)
|
s := slice(a)
|
||||||
copy(s[:], s[1:])
|
copy(s[:], s[1:])
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
return
|
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)
|
assert(condition=a.len >= count, loc=loc)
|
||||||
a.len -= count
|
a.len -= count
|
||||||
}
|
}
|
||||||
|
|
||||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
|
if index+1 < a.len {
|
||||||
|
copy(a.data[index:], a.data[index+1:])
|
||||||
|
}
|
||||||
|
a.len -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
|
n := a.len-1
|
||||||
|
if index != n {
|
||||||
|
a.data[index] = a.data[n]
|
||||||
|
}
|
||||||
|
a.len -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||||
resize(a, 0)
|
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[:])
|
n := copy(a.data[a.len:], items[:])
|
||||||
a.len += n
|
a.len += n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
||||||
|
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
|
||||||
|
a.len += 1
|
||||||
|
for i := a.len - 1; i >= index + 1; i -= 1 {
|
||||||
|
a.data[i] = a.data[i - 1]
|
||||||
|
}
|
||||||
|
a.data[index] = item
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
append_elem :: push_back
|
append_elem :: push_back
|
||||||
append_elems :: push_back_elems
|
append_elems :: push_back_elems
|
||||||
push :: proc{push_back, push_back_elems}
|
push :: proc{push_back, push_back_elems}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
|
|||||||
### Disclaimer
|
### Disclaimer
|
||||||
The algorithms were ported out of curiosity and due to interest in the field.
|
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.
|
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.
|
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||||
|
|
||||||
### ToDo
|
### ToDo
|
||||||
|
|||||||
@@ -30,6 +30,6 @@ equivalence.
|
|||||||
|
|
||||||
For the most part, alterations to the base fiat-crypto generated code was
|
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
|
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
|
[1]: https://github.com/mit-plv/fiat-crypto
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ _rand_bytes :: proc (dst: []byte) {
|
|||||||
|
|
||||||
for l > 0 {
|
for l > 0 {
|
||||||
to_read := min(l, _MAX_PER_CALL_BYTES)
|
to_read := min(l, _MAX_PER_CALL_BYTES)
|
||||||
ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
|
ret := unix.sys_getrandom(raw_data(dst), uint(to_read), 0)
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
switch os.Errno(-ret) {
|
switch os.Errno(-ret) {
|
||||||
case os.EINTR:
|
case os.EINTR:
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
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.last_block = len(data) / 8 * 8
|
||||||
ctx.buf = data
|
ctx.buf = data
|
||||||
i := 0
|
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,
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
Package core:dynlib implements loading of shared libraries/DLLs and their symbols.
|
||||||
|
|
||||||
|
The behaviour of dynamically loaded libraries is specific to the target platform of the program.
|
||||||
|
For in depth detail on the underlying behaviour please refer to your target platform's documentation.
|
||||||
|
*/
|
||||||
|
package dynlib
|
||||||
+81
-2
@@ -1,15 +1,94 @@
|
|||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
|
/*
|
||||||
|
A handle to a dynamically loaded library.
|
||||||
|
*/
|
||||||
Library :: distinct rawptr
|
Library :: distinct rawptr
|
||||||
|
|
||||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
/*
|
||||||
|
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
||||||
|
library available to resolve references in subsequently loaded libraries.
|
||||||
|
|
||||||
|
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
||||||
|
On `windows` this paramater is ignored.
|
||||||
|
|
||||||
|
The underlying behaviour is platform specific.
|
||||||
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
||||||
|
On `windows` refer to `LoadLibraryW`.
|
||||||
|
|
||||||
|
**Implicit Allocators**
|
||||||
|
`context.temp_allocator`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:dynlib"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
load_my_library :: proc() {
|
||||||
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
|
if ! ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
|
||||||
return _load_library(path, global_symbols)
|
return _load_library(path, global_symbols)
|
||||||
}
|
}
|
||||||
|
|
||||||
unload_library :: proc(library: Library) -> bool {
|
/*
|
||||||
|
Unloads a dynamic library.
|
||||||
|
|
||||||
|
The underlying behaviour is platform specific.
|
||||||
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlclose`.
|
||||||
|
On `windows` refer to `FreeLibrary`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:dynlib"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
load_then_unload_my_library :: proc() {
|
||||||
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
|
if ! ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
did_unload := dynlib.unload_library(library)
|
||||||
|
if ! did_unload {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
unload_library :: proc(library: Library) -> (did_unload: bool) {
|
||||||
return _unload_library(library)
|
return _unload_library(library)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loads the address of a procedure/variable from a dynamic library.
|
||||||
|
|
||||||
|
The underlying behaviour is platform specific.
|
||||||
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
||||||
|
On `windows` refer to `GetProcAddress`.
|
||||||
|
|
||||||
|
**Implicit Allocators**
|
||||||
|
`context.temp_allocator`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:dynlib"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
find_a_in_my_library :: proc() {
|
||||||
|
LIBRARY_PATH :: "my_library.dll"
|
||||||
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
||||||
|
if ! ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
a, found_a := dynlib.symbol_address(library, "a")
|
||||||
|
if found_a do fmt.printf("The symbol %q was found at the address %v", "a", a)
|
||||||
|
}
|
||||||
|
*/
|
||||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||||
return _symbol_address(library, symbol)
|
return _symbol_address(library, symbol)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,12 @@ package dynlib
|
|||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
||||||
return handle, handle != nil
|
return handle, handle != nil
|
||||||
@@ -19,6 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import "core:strings"
|
|||||||
|
|
||||||
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
|
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
|
||||||
|
|
||||||
write_rune :: strings.write_rune_builder
|
write_rune :: strings.write_rune
|
||||||
write_string :: strings.write_string_builder
|
write_string :: strings.write_string
|
||||||
|
|
||||||
Error :: enum u8 {
|
Error :: enum u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
@@ -94,8 +94,8 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
|||||||
l := len(input)
|
l := len(input)
|
||||||
if l == 0 { return "", .None }
|
if l == 0 { return "", .None }
|
||||||
|
|
||||||
builder := strings.make_builder()
|
builder := strings.builder_make()
|
||||||
defer strings.destroy_builder(&builder)
|
defer strings.builder_destroy(&builder)
|
||||||
|
|
||||||
t := Tokenizer{src=input}
|
t := Tokenizer{src=input}
|
||||||
in_data := false
|
in_data := false
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import "core:time"
|
|||||||
|
|
||||||
doc_print :: proc(doc: ^xml.Document) {
|
doc_print :: proc(doc: ^xml.Document) {
|
||||||
buf: strings.Builder
|
buf: strings.Builder
|
||||||
defer strings.destroy_builder(&buf)
|
defer strings.builder_destroy(&buf)
|
||||||
w := strings.to_writer(&buf)
|
w := strings.to_writer(&buf)
|
||||||
|
|
||||||
xml.print(w, doc)
|
xml.print(w, doc)
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ Node :: struct {
|
|||||||
/* Conventions */
|
/* 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.
|
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!
|
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!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
+235
-54
@@ -5,6 +5,7 @@ import "core:math/bits"
|
|||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
Marshal_Data_Error :: enum {
|
Marshal_Data_Error :: enum {
|
||||||
@@ -17,25 +18,54 @@ Marshal_Error :: union #shared_nil {
|
|||||||
io.Error,
|
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
|
||||||
b := strings.make_builder(allocator)
|
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 {
|
defer if err != nil {
|
||||||
strings.destroy_builder(&b)
|
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 {
|
if len(b.buf) != 0 {
|
||||||
data = b.buf[:]
|
data = b.buf[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||||
return marshal_to_writer(strings.to_writer(b), v)
|
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 {
|
if v == nil {
|
||||||
io.write_string(w, "null") or_return
|
io.write_string(w, "null") or_return
|
||||||
return
|
return
|
||||||
@@ -49,13 +79,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
case runtime.Type_Info_Integer:
|
case runtime.Type_Info_Integer:
|
||||||
buf: [21]byte
|
buf: [40]byte
|
||||||
u: u128
|
u: u128
|
||||||
switch i in a {
|
switch i in a {
|
||||||
case i8: u = u128(i)
|
case i8: u = u128(i)
|
||||||
case i16: u = u128(i)
|
case i16: u = u128(i)
|
||||||
case i32: u = u128(i)
|
case i32: u = u128(i)
|
||||||
case i64: u = u128(i)
|
case i64: u = u128(i)
|
||||||
|
case i128: u = u128(i)
|
||||||
case int: u = u128(i)
|
case int: u = u128(i)
|
||||||
case u8: u = u128(i)
|
case u8: u = u128(i)
|
||||||
case u16: 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)
|
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
|
io.write_string(w, s) or_return
|
||||||
|
|
||||||
|
|
||||||
@@ -147,10 +192,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
case runtime.Type_Info_Multi_Pointer:
|
case runtime.Type_Info_Multi_Pointer:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
|
case runtime.Type_Info_Soa_Pointer:
|
||||||
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Procedure:
|
case runtime.Type_Info_Procedure:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Tuple:
|
case runtime.Type_Info_Parameters:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Simd_Vector:
|
case runtime.Type_Info_Simd_Vector:
|
||||||
@@ -166,88 +214,108 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Array:
|
case runtime.Type_Info_Array:
|
||||||
io.write_byte(w, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
for i in 0..<info.count {
|
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)
|
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:
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
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 {
|
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)
|
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:
|
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
|
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||||
for i in 0..<array.len {
|
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)
|
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:
|
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
|
slice := cast(^mem.Raw_Slice)v.data
|
||||||
for i in 0..<slice.len {
|
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)
|
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:
|
case runtime.Type_Info_Map:
|
||||||
m := (^mem.Raw_Map)(v.data)
|
m := (^mem.Raw_Map)(v.data)
|
||||||
|
opt_write_start(w, opt, '{') or_return
|
||||||
|
|
||||||
io.write_byte(w, '{') or_return
|
|
||||||
if m != nil {
|
if m != nil {
|
||||||
if info.generated_struct == nil {
|
if info.map_info == nil {
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
}
|
}
|
||||||
entries := &m.entries
|
map_cap := uintptr(runtime.map_cap(m^))
|
||||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||||
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
|
|
||||||
|
|
||||||
for i in 0..<entries.len {
|
i := 0
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
for bucket_index in 0..<map_cap {
|
||||||
|
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
i += 1
|
||||||
|
|
||||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
key := rawptr(data + entry_type.offsets[2])
|
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||||
value := rawptr(data + entry_type.offsets[3])
|
|
||||||
|
|
||||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
// check for string type
|
||||||
io.write_string(w, ": ") or_return
|
{
|
||||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
v := any{key, info.key.id}
|
||||||
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
|
a := any{v.data, ti.id}
|
||||||
|
name: string
|
||||||
|
|
||||||
|
#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:
|
case runtime.Type_Info_Struct:
|
||||||
io.write_byte(w, '{') or_return
|
opt_write_start(w, opt, '{') or_return
|
||||||
|
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
io.write_quoted_string(w, name) or_return
|
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
|
||||||
io.write_string(w, ": ") or_return
|
opt_write_key(w, opt, json_name) or_return
|
||||||
|
} else {
|
||||||
|
opt_write_key(w, opt, name) or_return
|
||||||
|
}
|
||||||
|
|
||||||
id := info.types[i].id
|
id := info.types[i].id
|
||||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
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:
|
case runtime.Type_Info_Union:
|
||||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||||
@@ -270,11 +338,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
io.write_string(w, "null") or_return
|
io.write_string(w, "null") or_return
|
||||||
} else {
|
} else {
|
||||||
id := info.variants[tag-1].id
|
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:
|
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:
|
case runtime.Type_Info_Bit_Set:
|
||||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||||
@@ -330,3 +398,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
|
|
||||||
return
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
|||||||
return parse_object(&p)
|
return parse_object(&p)
|
||||||
case .JSON5:
|
case .JSON5:
|
||||||
return parse_value(&p)
|
return parse_value(&p)
|
||||||
case .MJSON:
|
case .SJSON:
|
||||||
#partial switch p.curr_token.kind {
|
#partial switch p.curr_token.kind {
|
||||||
case .Ident, .String:
|
case .Ident, .String:
|
||||||
return parse_object_body(&p, .EOF)
|
return parse_object_body(&p, .EOF)
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ package json
|
|||||||
Specification :: enum {
|
Specification :: enum {
|
||||||
JSON,
|
JSON,
|
||||||
JSON5, // https://json5.org/
|
JSON5, // https://json5.org/
|
||||||
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||||
Bitsquid = MJSON,
|
Bitsquid = SJSON,
|
||||||
|
MJSON = SJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
||||||
|
|
||||||
if end_token == .Close_Brace {
|
if end_token == .Close_Brace {
|
||||||
assert(expect_token(p, .Open_Brace) == nil)
|
unmarshal_expect_token(p, .Open_Brace)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := v
|
v := v
|
||||||
@@ -346,6 +346,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
|
|
||||||
fields := reflect.struct_fields_zipped(ti.id)
|
fields := reflect.struct_fields_zipped(ti.id)
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||||
|
|
||||||
field_used := make([]bool, len(fields), context.temp_allocator)
|
field_used := make([]bool, len(fields), context.temp_allocator)
|
||||||
|
|
||||||
use_field_idx := -1
|
use_field_idx := -1
|
||||||
@@ -380,13 +382,18 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
field := any{field_ptr, type.id}
|
field := any{field_ptr, type.id}
|
||||||
unmarshal_value(p, field) or_return
|
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) {
|
if parse_comma(p) {
|
||||||
break struct_loop
|
break struct_loop
|
||||||
}
|
}
|
||||||
continue struct_loop
|
continue struct_loop
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unsupported_Type_Error{v.id, p.curr_token}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Type_Info_Map:
|
case reflect.Type_Info_Map:
|
||||||
@@ -394,12 +401,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
return UNSUPPORTED_TYPE
|
return UNSUPPORTED_TYPE
|
||||||
}
|
}
|
||||||
raw_map := (^mem.Raw_Map)(v.data)
|
raw_map := (^mem.Raw_Map)(v.data)
|
||||||
if raw_map.entries.allocator.procedure == nil {
|
if raw_map.allocator.procedure == nil {
|
||||||
raw_map.entries.allocator = p.allocator
|
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
|
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||||
defer delete(elem_backing, p.allocator)
|
defer delete(elem_backing, p.allocator)
|
||||||
|
|
||||||
@@ -415,19 +420,16 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
delete(key, p.allocator)
|
delete(key, p.allocator)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := runtime.Map_Hash {
|
key_ptr := rawptr(&key)
|
||||||
hash = runtime.default_hasher_string(&key, 0),
|
|
||||||
key_ptr = &key,
|
|
||||||
}
|
|
||||||
|
|
||||||
key_cstr: cstring
|
key_cstr: cstring
|
||||||
if reflect.is_cstring(t.key) {
|
if reflect.is_cstring(t.key) {
|
||||||
key_cstr = cstring(raw_data(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 {
|
if set_ptr == nil {
|
||||||
delete(key, p.allocator)
|
delete(key, p.allocator)
|
||||||
}
|
}
|
||||||
@@ -473,7 +475,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if end_token == .Close_Brace {
|
if end_token == .Close_Brace {
|
||||||
assert(expect_token(p, .Close_Brace) == nil)
|
unmarshal_expect_token(p, .Close_Brace)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ example :: proc() {
|
|||||||
|
|
||||||
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
|
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
|
||||||
buf: strings.Builder
|
buf: strings.Builder
|
||||||
defer strings.destroy_builder(&buf)
|
defer strings.builder_destroy(&buf)
|
||||||
w := strings.to_writer(&buf)
|
w := strings.to_writer(&buf)
|
||||||
|
|
||||||
xml.print(w, doc)
|
xml.print(w, doc)
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import "core:intrinsics"
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
likely :: intrinsics.expect
|
likely :: intrinsics.expect
|
||||||
|
|
||||||
@@ -408,7 +409,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
next := scan(t)
|
next := scan(t)
|
||||||
#partial switch next.kind {
|
#partial switch next.kind {
|
||||||
case .Ident:
|
case .Ident:
|
||||||
if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
|
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
|
||||||
parse_prologue(doc) or_return
|
parse_prologue(doc) or_return
|
||||||
} else if len(doc.prologue) > 0 {
|
} else if len(doc.prologue) > 0 {
|
||||||
/*
|
/*
|
||||||
@@ -614,6 +615,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "encoding":
|
case "encoding":
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
switch strings.to_lower(attr.val, context.temp_allocator) {
|
switch strings.to_lower(attr.val, context.temp_allocator) {
|
||||||
case "utf-8", "utf8":
|
case "utf-8", "utf8":
|
||||||
doc.encoding = .UTF_8
|
doc.encoding = .UTF_8
|
||||||
|
|||||||
+691
-579
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ foreign odin_env {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private="file")
|
@(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) {
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
fd := u32(uintptr(s.stream_data))
|
fd := u32(uintptr(s.stream_data))
|
||||||
write(fd, p)
|
write(fd, p)
|
||||||
@@ -22,14 +22,14 @@ write_vtable := &io.Stream_VTable{
|
|||||||
@(private="file")
|
@(private="file")
|
||||||
stdout := io.Writer{
|
stdout := io.Writer{
|
||||||
stream = {
|
stream = {
|
||||||
stream_vtable = write_vtable,
|
stream_vtable = &write_vtable,
|
||||||
stream_data = rawptr(uintptr(1)),
|
stream_data = rawptr(uintptr(1)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@(private="file")
|
@(private="file")
|
||||||
stderr := io.Writer{
|
stderr := io.Writer{
|
||||||
stream = {
|
stream = {
|
||||||
stream_vtable = write_vtable,
|
stream_vtable = &write_vtable,
|
||||||
stream_data = rawptr(uintptr(2)),
|
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))
|
w := io.to_writer(os.stream_from_handle(fd))
|
||||||
return wprintln(w=w, args=args, sep=sep)
|
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 {
|
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||||
w := io.to_writer(os.stream_from_handle(fd))
|
w := io.to_writer(os.stream_from_handle(fd))
|
||||||
return wprintf(w, fmt, ..args)
|
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) }
|
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 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) }
|
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) }
|
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 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) }
|
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 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) }
|
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) }
|
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||||
result := u64(seed)
|
result = seed
|
||||||
#no_bounds_check for b in data {
|
#no_bounds_check for b in data {
|
||||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||||
}
|
}
|
||||||
|
|||||||
+108
-98
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you have a choice, prefer fnv32a
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
h = (h * 0x01000193) ~ u32(b)
|
h = (h * 0x01000193) ~ u32(b)
|
||||||
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
|||||||
return h
|
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")
|
@(optimization_mode="speed")
|
||||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||||
h: u64 = seed
|
h: u64 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
h = (h * 0x100000001b3) ~ u64(b)
|
h = (h * 0x100000001b3) ~ u64(b)
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
@@ -172,108 +176,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
|||||||
return h1
|
return h1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
when size_of(int) == 8 {
|
m :: 0xc6a4a7935bd1e995
|
||||||
m :: 0xc6a4a7935bd1e995
|
r :: 47
|
||||||
r :: 47
|
|
||||||
|
|
||||||
h: u64 = seed ~ (u64(len(data)) * m)
|
h: u64 = seed ~ (u64(len(data)) * m)
|
||||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
|
data64 := mem.slice_data_cast([]u64, data)
|
||||||
|
|
||||||
for _, i in data64 {
|
for _, i in data64 {
|
||||||
k := data64[i]
|
k := data64[i]
|
||||||
|
|
||||||
k *= m
|
k *= m
|
||||||
k ~= k>>r
|
k ~= k>>r
|
||||||
k *= m
|
k *= m
|
||||||
|
|
||||||
h ~= k
|
h ~= k
|
||||||
h *= m
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(data)&7 {
|
|
||||||
case 7: h ~= u64(data[6]) << 48; fallthrough
|
|
||||||
case 6: h ~= u64(data[5]) << 40; fallthrough
|
|
||||||
case 5: h ~= u64(data[4]) << 32; fallthrough
|
|
||||||
case 4: h ~= u64(data[3]) << 24; fallthrough
|
|
||||||
case 3: h ~= u64(data[2]) << 16; fallthrough
|
|
||||||
case 2: h ~= u64(data[1]) << 8; fallthrough
|
|
||||||
case 1:
|
|
||||||
h ~= u64(data[0])
|
|
||||||
h *= m
|
|
||||||
}
|
|
||||||
|
|
||||||
h ~= h>>r
|
|
||||||
h *= m
|
h *= m
|
||||||
h ~= h>>r
|
|
||||||
|
|
||||||
return h
|
|
||||||
} else {
|
|
||||||
m :: 0x5bd1e995
|
|
||||||
r :: 24
|
|
||||||
|
|
||||||
h1 := u32(seed) ~ u32(len(data))
|
|
||||||
h2 := u32(seed) >> 32
|
|
||||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
|
||||||
len := len(data)
|
|
||||||
i := 0
|
|
||||||
|
|
||||||
for len >= 8 {
|
|
||||||
k1, k2: u32
|
|
||||||
k1 = data32[i]; i += 1
|
|
||||||
k1 *= m
|
|
||||||
k1 ~= k1>>r
|
|
||||||
k1 *= m
|
|
||||||
h1 *= m
|
|
||||||
h1 ~= k1
|
|
||||||
len -= 4
|
|
||||||
|
|
||||||
k2 = data32[i]; i += 1
|
|
||||||
k2 *= m
|
|
||||||
k2 ~= k2>>r
|
|
||||||
k2 *= m
|
|
||||||
h2 *= m
|
|
||||||
h2 ~= k2
|
|
||||||
len -= 4
|
|
||||||
}
|
|
||||||
|
|
||||||
if len >= 4 {
|
|
||||||
k1: u32
|
|
||||||
k1 = data32[i]; i += 1
|
|
||||||
k1 *= m
|
|
||||||
k1 ~= k1>>r
|
|
||||||
k1 *= m
|
|
||||||
h1 *= m
|
|
||||||
h1 ~= k1
|
|
||||||
len -= 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bill): Fix this
|
|
||||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
|
||||||
switch len {
|
|
||||||
case 3:
|
|
||||||
h2 ~= u32(data8[2]) << 16
|
|
||||||
fallthrough
|
|
||||||
case 2:
|
|
||||||
h2 ~= u32(data8[1]) << 8
|
|
||||||
fallthrough
|
|
||||||
case 1:
|
|
||||||
h2 ~= u32(data8[0])
|
|
||||||
h2 *= m
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 ~= h2>>18
|
|
||||||
h1 *= m
|
|
||||||
h2 ~= h1>>22
|
|
||||||
h2 *= m
|
|
||||||
h1 ~= h2>>17
|
|
||||||
h1 *= m
|
|
||||||
h2 ~= h1>>19
|
|
||||||
h2 *= m
|
|
||||||
|
|
||||||
return u64(h1)<<32 | u64(h2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset := len(data64) * size_of(u64)
|
||||||
|
|
||||||
|
switch len(data)&7 {
|
||||||
|
case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
|
||||||
|
case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
|
||||||
|
case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
|
||||||
|
case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
|
||||||
|
case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
|
||||||
|
case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
|
||||||
|
case 1:
|
||||||
|
h ~= u64(data[offset + 0])
|
||||||
|
h *= m
|
||||||
|
}
|
||||||
|
|
||||||
|
h ~= h>>r
|
||||||
|
h *= m
|
||||||
|
h ~= h>>r
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||||
|
@(optimization_mode="speed")
|
||||||
|
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
|
m :: 0x5bd1e995
|
||||||
|
r :: 24
|
||||||
|
|
||||||
|
h1 := u32(seed) ~ u32(len(data))
|
||||||
|
h2 := u32(seed) >> 32
|
||||||
|
|
||||||
|
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||||
|
len := len(data)
|
||||||
|
i := 0
|
||||||
|
|
||||||
|
for len >= 8 {
|
||||||
|
k1, k2: u32
|
||||||
|
k1 = data32[i]; i += 1
|
||||||
|
k1 *= m
|
||||||
|
k1 ~= k1>>r
|
||||||
|
k1 *= m
|
||||||
|
h1 *= m
|
||||||
|
h1 ~= k1
|
||||||
|
len -= 4
|
||||||
|
|
||||||
|
k2 = data32[i]; i += 1
|
||||||
|
k2 *= m
|
||||||
|
k2 ~= k2>>r
|
||||||
|
k2 *= m
|
||||||
|
h2 *= m
|
||||||
|
h2 ~= k2
|
||||||
|
len -= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
if len >= 4 {
|
||||||
|
k1: u32
|
||||||
|
k1 = data32[i]; i += 1
|
||||||
|
k1 *= m
|
||||||
|
k1 ~= k1>>r
|
||||||
|
k1 *= m
|
||||||
|
h1 *= m
|
||||||
|
h1 ~= k1
|
||||||
|
len -= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bill): Fix this
|
||||||
|
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
||||||
|
switch len {
|
||||||
|
case 3:
|
||||||
|
h2 ~= u32(data8[2]) << 16
|
||||||
|
fallthrough
|
||||||
|
case 2:
|
||||||
|
h2 ~= u32(data8[1]) << 8
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
h2 ~= u32(data8[0])
|
||||||
|
h2 *= m
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 ~= h2>>18
|
||||||
|
h1 *= m
|
||||||
|
h2 ~= h1>>22
|
||||||
|
h2 *= m
|
||||||
|
h1 ~= h2>>17
|
||||||
|
h1 *= m
|
||||||
|
h2 ~= h1>>19
|
||||||
|
h2 *= m
|
||||||
|
|
||||||
|
return u64(h1)<<32 | u64(h2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, auto_cast shift: uint) -> (res: xxh_u64) {
|
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
|
||||||
return v ~ (v >> shift)
|
return v ~ (v >> shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+65
-7
@@ -46,7 +46,7 @@ Image :: struct {
|
|||||||
height: int,
|
height: int,
|
||||||
channels: int,
|
channels: int,
|
||||||
depth: int, // Channel depth in bits, typically 8 or 16
|
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.
|
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
|
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,
|
^Netpbm_Info,
|
||||||
^PNG_Info,
|
^PNG_Info,
|
||||||
^QOI_Info,
|
^QOI_Info,
|
||||||
|
^TGA_Info,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
|
|||||||
|
|
||||||
General_Image_Error :: enum {
|
General_Image_Error :: enum {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
Unsupported_Option,
|
||||||
// File I/O
|
// File I/O
|
||||||
Unable_To_Read_File,
|
Unable_To_Read_File,
|
||||||
Unable_To_Write_File,
|
Unable_To_Write_File,
|
||||||
@@ -376,10 +378,20 @@ QOI_Info :: struct {
|
|||||||
header: QOI_Header,
|
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 {
|
TGA_Header :: struct #packed {
|
||||||
id_length: u8,
|
id_length: u8,
|
||||||
color_map_type: u8,
|
color_map_type: u8,
|
||||||
data_type_code: u8,
|
data_type_code: TGA_Data_Type,
|
||||||
color_map_origin: u16le,
|
color_map_origin: u16le,
|
||||||
color_map_length: u16le,
|
color_map_length: u16le,
|
||||||
color_map_depth: u8,
|
color_map_depth: u8,
|
||||||
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
|
|||||||
}
|
}
|
||||||
#assert(size_of(TGA_Header) == 18)
|
#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
|
// Function to help with image buffer calculations
|
||||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
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
|
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),
|
// 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) {
|
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||||
// Were we actually given a valid image?
|
// Were we actually given a valid image?
|
||||||
if img == nil {
|
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.
|
// This returns 0 if any of the inputs is zero.
|
||||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
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 {
|
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||||
return false
|
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),
|
// 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) {
|
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||||
// Were we actually given a valid image?
|
// Were we actually given a valid image?
|
||||||
if img == nil {
|
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.
|
// This returns 0 if any of the inputs is zero.
|
||||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
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 {
|
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||||
return false
|
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),
|
// 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) {
|
is_valid_image :: proc(img: ^Image) -> (ok: bool) {
|
||||||
// Were we actually given a valid image?
|
// Were we actually given a valid image?
|
||||||
if img == nil {
|
if img == nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import "core:os"
|
|||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
Image :: image.Image
|
Image :: image.Image
|
||||||
Format :: image.Netpbm_Format
|
Format :: image.Netpbm_Format
|
||||||
@@ -130,7 +131,7 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
|||||||
|
|
||||||
// we will write to a string builder
|
// we will write to a string builder
|
||||||
data: strings.Builder
|
data: strings.Builder
|
||||||
strings.init_builder(&data)
|
strings.builder_init(&data)
|
||||||
|
|
||||||
// all PNM headers start with the format
|
// all PNM headers start with the format
|
||||||
fmt.sbprintf(&data, "%s\n", header.format)
|
fmt.sbprintf(&data, "%s\n", header.format)
|
||||||
@@ -407,9 +408,11 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
|
|||||||
}
|
}
|
||||||
length = header_end_index + len(HEADER_END)
|
length = header_end_index + len(HEADER_END)
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||||
|
|
||||||
// string buffer for the tupltype
|
// string buffer for the tupltype
|
||||||
tupltype: strings.Builder
|
tupltype: strings.Builder
|
||||||
strings.init_builder(&tupltype, context.temp_allocator); defer strings.destroy_builder(&tupltype)
|
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
|
||||||
fmt.sbprint(&tupltype, "")
|
fmt.sbprint(&tupltype, "")
|
||||||
|
|
||||||
// PAM uses actual lines, so we can iterate easily
|
// PAM uses actual lines, so we can iterate easily
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
An example of how to use `load`.
|
An example of how to use `load`.
|
||||||
*/
|
*/
|
||||||
//+ignore
|
//+build ignore
|
||||||
package png
|
package png
|
||||||
|
|
||||||
import "core:image"
|
import "core:image"
|
||||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
|||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|
||||||
write_string(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 {
|
if channels == 3 {
|
||||||
|
|||||||
+17
-14
@@ -16,6 +16,7 @@ import coretime "core:time"
|
|||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Cleanup of image-specific data.
|
Cleanup of image-specific data.
|
||||||
@@ -91,6 +92,8 @@ core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
|
text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||||
|
|
||||||
assert(len(c.data) == int(c.header.length))
|
assert(len(c.data) == int(c.header.length))
|
||||||
#partial switch c.header.type {
|
#partial switch c.header.type {
|
||||||
case .tEXt:
|
case .tEXt:
|
||||||
@@ -194,18 +197,18 @@ text_destroy :: proc(text: Text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
|
iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
|
||||||
ok = true
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||||
|
|
||||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
|
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
|
||||||
|
|
||||||
if len(fields[0]) < 1 || len(fields[0]) > 79 {
|
if len(fields[0]) < 1 || len(fields[0]) > 79 {
|
||||||
// Invalid profile name
|
// Invalid profile name
|
||||||
ok = false; return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fields[1]) != 0 {
|
if len(fields[1]) != 0 {
|
||||||
// Compression method should be a zero, which the split turned into an empty slice.
|
// Compression method should be a zero, which the split turned into an empty slice.
|
||||||
ok = false; return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up ZLIB context and decompress iCCP payload
|
// Set up ZLIB context and decompress iCCP payload
|
||||||
@@ -213,12 +216,12 @@ iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
|
|||||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
|
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
|
||||||
if zlib_error != nil {
|
if zlib_error != nil {
|
||||||
bytes.buffer_destroy(&buf)
|
bytes.buffer_destroy(&buf)
|
||||||
ok = false; return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.name = strings.clone(string(fields[0]))
|
res.name = strings.clone(string(fields[0]))
|
||||||
res.profile = bytes.buffer_to_bytes(&buf)
|
res.profile = bytes.buffer_to_bytes(&buf)
|
||||||
|
ok = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,18 +259,18 @@ plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
|
|||||||
|
|
||||||
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
||||||
if c.header.type != .sPLT {
|
if c.header.type != .sPLT {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
ok = true
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||||
|
|
||||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
|
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
|
||||||
if len(fields) != 2 {
|
if len(fields) != 2 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.depth = fields[1][0]
|
res.depth = fields[1][0]
|
||||||
if res.depth != 8 && res.depth != 16 {
|
if res.depth != 8 && res.depth != 16 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data := fields[1][1:]
|
data := fields[1][1:]
|
||||||
@@ -275,21 +278,21 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
|||||||
|
|
||||||
if res.depth == 8 {
|
if res.depth == 8 {
|
||||||
if len(data) % 6 != 0 {
|
if len(data) % 6 != 0 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
count = len(data) / 6
|
count = len(data) / 6
|
||||||
if count > 256 {
|
if count > 256 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.entries = mem.slice_data_cast([][4]u8, data)
|
res.entries = mem.slice_data_cast([][4]u8, data)
|
||||||
} else { // res.depth == 16
|
} else { // res.depth == 16
|
||||||
if len(data) % 10 != 0 {
|
if len(data) % 10 != 0 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
count = len(data) / 10
|
count = len(data) / 10
|
||||||
if count > 256 {
|
if count > 256 {
|
||||||
return {}, false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res.entries = mem.slice_data_cast([][4]u16, data)
|
res.entries = mem.slice_data_cast([][4]u16, data)
|
||||||
@@ -297,7 +300,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
|||||||
|
|
||||||
res.name = strings.clone(string(fields[0]))
|
res.name = strings.clone(string(fields[0]))
|
||||||
res.used = u16(count)
|
res.used = u16(count)
|
||||||
|
ok = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-10
@@ -23,6 +23,7 @@ import "core:bytes"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
// Limit chunk sizes.
|
// Limit chunk sizes.
|
||||||
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
|
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
|
||||||
@@ -1002,7 +1003,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
o16 = o16[out_image_channels:]
|
o16 = o16[out_image_channels:]
|
||||||
}
|
}
|
||||||
case:
|
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
|
img.pixels = t
|
||||||
@@ -1195,7 +1196,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
o = o[out_image_channels:]
|
o = o[out_image_channels:]
|
||||||
}
|
}
|
||||||
case:
|
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
|
img.pixels = t
|
||||||
@@ -1206,7 +1207,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
|
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.
|
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
|
return img, nil
|
||||||
@@ -1247,6 +1248,8 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
|||||||
|
|
||||||
// TODO: See about doing a Duff's #unroll where practicable
|
// TODO: See about doing a Duff's #unroll where practicable
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
|
|
||||||
// Apron so we don't need to special case first rows.
|
// Apron so we don't need to special case first rows.
|
||||||
up := make([]u8, row_stride, context.temp_allocator)
|
up := make([]u8, row_stride, context.temp_allocator)
|
||||||
ok = true
|
ok = true
|
||||||
@@ -1299,10 +1302,9 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @(optimization_mode="speed")
|
// @(optimization_mode="speed")
|
||||||
defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_check {
|
defilter_less_than_8 :: proc(params: ^Filter_Params) -> bool #no_bounds_check {
|
||||||
|
|
||||||
using params
|
using params
|
||||||
ok = true
|
|
||||||
|
|
||||||
row_stride_in := ((channels * width * depth) + 7) >> 3
|
row_stride_in := ((channels * width * depth) + 7) >> 3
|
||||||
row_stride_out := channels * width
|
row_stride_out := channels * width
|
||||||
@@ -1314,6 +1316,8 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
|
|||||||
|
|
||||||
// TODO: See about doing a Duff's #unroll where practicable
|
// TODO: See about doing a Duff's #unroll where practicable
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
|
|
||||||
// Apron so we don't need to special case first rows.
|
// Apron so we don't need to special case first rows.
|
||||||
up := make([]u8, row_stride_out, context.temp_allocator)
|
up := make([]u8, row_stride_out, context.temp_allocator)
|
||||||
|
|
||||||
@@ -1457,18 +1461,18 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// @(optimization_mode="speed")
|
// @(optimization_mode="speed")
|
||||||
defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
defilter_16 :: proc(params: ^Filter_Params) -> bool {
|
||||||
|
|
||||||
using params
|
using params
|
||||||
ok = true
|
|
||||||
|
|
||||||
stride := channels * 2
|
stride := channels * 2
|
||||||
row_stride := width * stride
|
row_stride := width * stride
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
|
|
||||||
// TODO: See about doing a Duff's #unroll where practicable
|
// TODO: See about doing a Duff's #unroll where practicable
|
||||||
// Apron so we don't need to special case first rows.
|
// Apron so we don't need to special case first rows.
|
||||||
up := make([]u8, row_stride, context.temp_allocator)
|
up := make([]u8, row_stride, context.temp_allocator)
|
||||||
@@ -1518,7 +1522,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
|||||||
dest = dest[row_stride:]
|
dest = dest[row_stride:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
|
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
|
||||||
|
|||||||
+341
-2
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
Benoit Jacquier: tga loader
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -14,11 +15,16 @@ import "core:mem"
|
|||||||
import "core:image"
|
import "core:image"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
import "core:compress"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
// TODO: alpha_premultiply support
|
||||||
|
|
||||||
Error :: image.Error
|
Error :: image.Error
|
||||||
Image :: image.Image
|
Image :: image.Image
|
||||||
Options :: image.Options
|
Options :: image.Options
|
||||||
|
|
||||||
|
GA_Pixel :: image.GA_Pixel
|
||||||
RGB_Pixel :: image.RGB_Pixel
|
RGB_Pixel :: image.RGB_Pixel
|
||||||
RGBA_Pixel :: image.RGBA_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{
|
header := image.TGA_Header{
|
||||||
data_type_code = 0x02, // Color, uncompressed.
|
data_type_code = .Uncompressed_RGB,
|
||||||
dimensions = {u16le(img.width), u16le(img.height)},
|
dimensions = {u16le(img.width), u16le(img.height)},
|
||||||
bits_per_pixel = u8(img.depth * img.channels),
|
bits_per_pixel = u8(img.depth * img.channels),
|
||||||
image_descriptor = 1 << 5, // Origin is top left.
|
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
|
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
|
// This is purely for documentation
|
||||||
//+ignore
|
//+build ignore
|
||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
// Package-Related
|
// Package-Related
|
||||||
@@ -188,14 +188,18 @@ 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_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_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 ---
|
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||||
|
|
||||||
// SIMD related
|
// SIMD related
|
||||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||||
simd_rem :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
|
||||||
|
|
||||||
// Keeps Odin's Behaviour
|
// Keeps Odin's Behaviour
|
||||||
// (x << y) if y <= mask else 0
|
// (x << y) if y <= mask else 0
|
||||||
@@ -279,7 +283,7 @@ wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -
|
|||||||
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
||||||
|
|
||||||
// x86 Targets (i386, amd64)
|
// x86 Targets (i386, amd64)
|
||||||
x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
|
x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
|
||||||
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||||
|
|
||||||
|
|
||||||
@@ -296,6 +300,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
|
|||||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||||
objc_register_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
|
// Internal compiler use only
|
||||||
|
|
||||||
__entry_point :: proc() ---
|
__entry_point :: proc() ---
|
||||||
|
|||||||
@@ -122,73 +122,3 @@ to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #option
|
|||||||
}
|
}
|
||||||
return
|
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}
|
Reader_From :: struct {using stream: Stream}
|
||||||
Writer_To :: 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 {
|
destroy :: proc(s: Stream) -> Error {
|
||||||
close_err := close({s})
|
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
|
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
|
||||||
// bytes read along with the error.
|
// bytes read along with the error.
|
||||||
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
|
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
|
||||||
if s.stream_vtable != nil && s.impl_read != nil {
|
if s.stream_vtable != nil {
|
||||||
n, err = s->impl_read(p)
|
if s.impl_read != nil {
|
||||||
if n_read != nil {
|
n, err = s->impl_read(p)
|
||||||
n_read^ += n
|
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
|
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 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) {
|
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||||
if s.stream_vtable != nil && s.impl_write != nil {
|
if s.stream_vtable != nil {
|
||||||
n, err = s->impl_write(p)
|
if s.impl_write != nil {
|
||||||
if n_written != nil {
|
n, err = s->impl_write(p)
|
||||||
n_written^ += n
|
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
|
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 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 {
|
defer if err == nil && n_read != nil {
|
||||||
n_read^ += 1
|
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
|
return buf[0], err
|
||||||
}
|
}
|
||||||
|
|
||||||
write_byte :: proc{
|
write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||||
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 {
|
|
||||||
return _write_byte(auto_cast w, c, n_written)
|
return _write_byte(auto_cast w, c, n_written)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(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 {
|
defer if err == nil && n_written != nil {
|
||||||
n_written^ += 1
|
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 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 {
|
defer if err == nil && n_read != nil {
|
||||||
n_read^ += size
|
n_read^ += size
|
||||||
}
|
}
|
||||||
@@ -417,13 +425,21 @@ read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
unread_byte :: proc(s: Byte_Scanner) -> Error {
|
unread_byte :: proc(s: Stream) -> Error {
|
||||||
if s.stream_vtable != nil && s.impl_unread_byte != nil {
|
if s.stream_vtable == nil {
|
||||||
|
return .Empty
|
||||||
|
}
|
||||||
|
if s.impl_unread_byte != nil {
|
||||||
return s->impl_unread_byte()
|
return s->impl_unread_byte()
|
||||||
}
|
}
|
||||||
|
if s.impl_seek != nil {
|
||||||
|
_, err := s->impl_seek(-1, .Current)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return .Empty
|
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 {
|
if s.stream_vtable != nil && s.impl_unread_rune != nil {
|
||||||
return s->impl_unread_rune()
|
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
|
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)
|
return s->impl_write_rune(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writer append a quoted rune into the byte buffer, return the written size
|
||||||
|
write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
|
||||||
|
_write_byte :: #force_inline proc(w: Writer, c: byte) -> int {
|
||||||
|
err := write_byte(w, c)
|
||||||
|
return 1 if err == nil else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
quote := byte('\'')
|
||||||
|
n += _write_byte(w, quote)
|
||||||
|
buf, width := utf8.encode_rune(r)
|
||||||
|
if width == 1 && r == utf8.RUNE_ERROR {
|
||||||
|
n += _write_byte(w, '\\')
|
||||||
|
n += _write_byte(w, 'x')
|
||||||
|
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
|
||||||
|
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
|
||||||
|
} else {
|
||||||
|
i, _ := write_escaped_rune(w, r, quote)
|
||||||
|
n += i
|
||||||
|
}
|
||||||
|
n += _write_byte(w, quote)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Tee_Reader :: struct {
|
Tee_Reader :: struct {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
|
|||||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_console_logger :: proc(log: ^Logger) {
|
destroy_console_logger :: proc(log: Logger) {
|
||||||
free(log.data)
|
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)
|
fmt.sbprintf(&buf, "[%s] ", data.ident)
|
||||||
}
|
}
|
||||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
//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) {
|
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.
|
// 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 {
|
Logger_Level :: enum {
|
||||||
Debug = 0,
|
Debug = 0,
|
||||||
@@ -16,8 +15,8 @@ Logger_Level :: enum {
|
|||||||
Fatal = 40,
|
Fatal = 40,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
Level :: runtime.Logger_Level
|
||||||
|
|
||||||
Option :: runtime.Logger_Option
|
|
||||||
/*
|
/*
|
||||||
Option :: enum {
|
Option :: enum {
|
||||||
Level,
|
Level,
|
||||||
@@ -30,11 +29,12 @@ Option :: enum {
|
|||||||
Terminal_Color
|
Terminal_Color
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
Option :: runtime.Logger_Option
|
||||||
|
|
||||||
Options :: runtime.Logger_Options
|
|
||||||
/*
|
/*
|
||||||
Options :: bit_set[Option];
|
Options :: bit_set[Option];
|
||||||
*/
|
*/
|
||||||
|
Options :: runtime.Logger_Options
|
||||||
|
|
||||||
Full_Timestamp_Opts :: Options{
|
Full_Timestamp_Opts :: Options{
|
||||||
.Date,
|
.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 :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||||
*/
|
*/
|
||||||
|
Logger_Proc :: runtime.Logger_Proc
|
||||||
|
|
||||||
Logger :: runtime.Logger
|
|
||||||
/*
|
/*
|
||||||
Logger :: struct {
|
Logger :: struct {
|
||||||
procedure: Logger_Proc,
|
procedure: Logger_Proc,
|
||||||
@@ -66,6 +65,7 @@ Logger :: struct {
|
|||||||
options: Logger_Options,
|
options: Logger_Options,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
Logger :: runtime.Logger
|
||||||
|
|
||||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
@@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger {
|
|||||||
return Logger{nil_logger_proc, nil, Level.Debug, nil}
|
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) {
|
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=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.
|
TODO: Handle +/- Infinity and NaN.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
//+ignore
|
|
||||||
package math_big
|
package math_big
|
||||||
|
|
||||||
import "core:mem"
|
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.
|
in the loop is non-zero, although very low.
|
||||||
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
|
-- 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,
|
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.
|
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.
|
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
|
package math_big
|
||||||
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|||||||
+23
-11
@@ -325,9 +325,9 @@ ease :: proc "contextless" (type: Ease, p: $T) -> T
|
|||||||
// in case type was invalid
|
// in case type was invalid
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Flux_Map :: struct($T: typeid) {
|
Flux_Map :: struct($T: typeid) {
|
||||||
values: map[^T]Flux_Tween(T),
|
values: map[^T]Flux_Tween(T),
|
||||||
|
keys_to_be_deleted: [dynamic]^T,
|
||||||
}
|
}
|
||||||
|
|
||||||
Flux_Tween :: struct($T: typeid) {
|
Flux_Tween :: struct($T: typeid) {
|
||||||
@@ -353,15 +353,17 @@ Flux_Tween :: struct($T: typeid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init flux map to a float type and a wanted cap
|
// init flux map to a float type and a wanted cap
|
||||||
flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||||
return {
|
return {
|
||||||
make(map[^T]Flux_Tween(T), cap),
|
values = make(map[^T]Flux_Tween(T), value_capacity),
|
||||||
|
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete map content
|
// delete map content
|
||||||
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
|
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
|
||||||
delete(flux.values)
|
delete(flux.values)
|
||||||
|
delete(flux.keys_to_be_deleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear map content, stops all animations
|
// clear map content, stops all animations
|
||||||
@@ -374,8 +376,8 @@ flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
|
|||||||
// return value can be used to set callbacks
|
// return value can be used to set callbacks
|
||||||
flux_to :: proc(
|
flux_to :: proc(
|
||||||
flux: ^Flux_Map($T),
|
flux: ^Flux_Map($T),
|
||||||
value: ^f32,
|
value: ^T,
|
||||||
goal: f32,
|
goal: T,
|
||||||
type: Ease = .Quadratic_Out,
|
type: Ease = .Quadratic_Out,
|
||||||
duration: time.Duration = time.Second,
|
duration: time.Duration = time.Second,
|
||||||
delay: f64 = 0,
|
delay: f64 = 0,
|
||||||
@@ -413,6 +415,8 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i
|
|||||||
// calls callbacks in all stages, when they're filled
|
// calls callbacks in all stages, when they're filled
|
||||||
// deletes tween from the map after completion
|
// deletes tween from the map after completion
|
||||||
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
|
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
|
||||||
|
clear(&flux.keys_to_be_deleted)
|
||||||
|
|
||||||
for key, tween in &flux.values {
|
for key, tween in &flux.values {
|
||||||
delay_remainder := f64(0)
|
delay_remainder := f64(0)
|
||||||
|
|
||||||
@@ -451,7 +455,8 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tween.progress >= 1 {
|
if tween.progress >= 1 {
|
||||||
delete_key(&flux.values, key)
|
// append keys to array that will be deleted after the loop
|
||||||
|
append(&flux.keys_to_be_deleted, key)
|
||||||
|
|
||||||
if tween.on_complete != nil {
|
if tween.on_complete != nil {
|
||||||
tween.on_complete(flux, tween.data)
|
tween.on_complete(flux, tween.data)
|
||||||
@@ -459,17 +464,24 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loop through keys that should be deleted from the map
|
||||||
|
if len(flux.keys_to_be_deleted) != 0 {
|
||||||
|
for key in flux.keys_to_be_deleted {
|
||||||
|
delete_key(&flux.values, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop a specific key inside the map
|
// stop a specific key inside the map
|
||||||
// returns true when it successfully removed the key
|
// returns true when it successfully removed the key
|
||||||
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
|
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
|
||||||
if key in flux.values {
|
if key in flux.values {
|
||||||
delete_key(&flux.values, key)
|
delete_key(&flux.values, key)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the amount of time left for the tween animation, if the key exists in the map
|
// returns the amount of time left for the tween animation, if the key exists in the map
|
||||||
|
|||||||
@@ -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 {
|
} else when N == 2 {
|
||||||
out = builtin.max(a[0], a[1])
|
out = builtin.max(a[0], a[1])
|
||||||
} else when N == 3 {
|
} else when N == 3 {
|
||||||
out = builtin.max(a[0], a[1], a[3])
|
out = builtin.max(a[0], a[1], a[2])
|
||||||
}else {
|
}else {
|
||||||
out = builtin.max(a[0], a[1])
|
out = builtin.max(a[0], a[1])
|
||||||
for i in 2..<N {
|
for i in 2..<N {
|
||||||
@@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||||
b := n * (2 * dot(n, i))
|
b := N * (2 * dot(N, I))
|
||||||
return i - b
|
return I - b
|
||||||
}
|
}
|
||||||
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||||
dv := dot(n, i)
|
dv := dot(N, I)
|
||||||
k := 1 - eta*eta - (1 - dv*dv)
|
k := 1 - eta*eta - (1 - dv*dv)
|
||||||
a := i * eta
|
a := I * eta
|
||||||
b := n * eta*dv*math.sqrt(k)
|
b := N * eta*dv*math.sqrt(k)
|
||||||
return (a - b) * E(int(k >= 0))
|
return (a - b) * E(int(k >= 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ not_equal :: proc{not_equal_single, not_equal_array}
|
|||||||
|
|
||||||
any :: proc(x: $A/[$N]bool) -> (out: bool) {
|
any :: proc(x: $A/[$N]bool) -> (out: bool) {
|
||||||
for e in x {
|
for e in x {
|
||||||
if x {
|
if e {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}
|
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
|
||||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
|
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)
|
return v / length(v)
|
||||||
}
|
}
|
||||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
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}
|
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)
|
m := length(v)
|
||||||
return 0 if m == 0 else v/m
|
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}
|
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))
|
return math.sqrt(dot(v, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -476,21 +476,21 @@ quaternion_angle_axis :: proc{
|
|||||||
|
|
||||||
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
||||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.acos(q.w) * 2
|
return math.acos(q.w) * 2
|
||||||
}
|
}
|
||||||
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
||||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.acos(q.w) * 2
|
return math.acos(q.w) * 2
|
||||||
}
|
}
|
||||||
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
||||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.acos(q.w) * 2
|
return math.acos(q.w) * 2
|
||||||
|
|||||||
+379
-29
@@ -1,6 +1,7 @@
|
|||||||
package math
|
package math
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:builtin"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
Float_Class :: enum {
|
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
|
RAD_PER_DEG :: TAU/360.0
|
||||||
DEG_PER_RAD :: 360.0/TAU
|
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_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_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))) }
|
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,
|
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 {
|
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
|
||||||
@@ -185,16 +277,23 @@ log :: proc{
|
|||||||
log_f64, log_f64le, log_f64be,
|
log_f64, log_f64le, log_f64be,
|
||||||
}
|
}
|
||||||
|
|
||||||
log2_f16 :: logb_f16
|
log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
|
||||||
log2_f16le :: logb_f16le
|
log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
|
||||||
log2_f16be :: logb_f16be
|
log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
|
||||||
log2_f32 :: logb_f32
|
|
||||||
log2_f32le :: logb_f32le
|
log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
|
||||||
log2_f32be :: logb_f32be
|
log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
|
||||||
log2_f64 :: logb_f64
|
log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
|
||||||
log2_f64le :: logb_f64le
|
|
||||||
log2_f64be :: logb_f64be
|
log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
|
||||||
log2 :: logb
|
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_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
|
||||||
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
|
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
|
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) {
|
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
|
||||||
shift :: F16_SHIFT
|
shift :: F16_SHIFT
|
||||||
mask :: F16_MASK
|
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 negative infinity.
|
||||||
// If sign == 0, is_inf reports whether f is either infinity.
|
// If sign == 0, is_inf reports whether f is either infinity.
|
||||||
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
||||||
class := classify(abs(x))
|
class := classify(x)
|
||||||
switch {
|
switch {
|
||||||
case sign > 0:
|
case sign > 0:
|
||||||
return class == .Inf
|
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 {
|
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
|
||||||
class := classify(abs(x))
|
class := classify(x)
|
||||||
switch {
|
switch {
|
||||||
case sign > 0:
|
case sign > 0:
|
||||||
return class == .Inf
|
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 {
|
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
|
||||||
class := classify(abs(x))
|
class := classify(x)
|
||||||
switch {
|
switch {
|
||||||
case sign > 0:
|
case sign > 0:
|
||||||
return class == .Inf
|
return class == .Inf
|
||||||
@@ -1312,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
|
|||||||
}
|
}
|
||||||
return copy_sign(PI, y)
|
return copy_sign(PI, y)
|
||||||
case x == 0:
|
case x == 0:
|
||||||
return copy_sign(PI*0.5, y)
|
return copy_sign(PI/2, y)
|
||||||
case is_inf(x, 0):
|
case is_inf(x, 0):
|
||||||
if is_inf(x, 1) {
|
if is_inf(x, 1) {
|
||||||
if is_inf(y, 0) {
|
if is_inf(y, 0) {
|
||||||
return copy_sign(PI*0.25, y)
|
return copy_sign(PI/4, y)
|
||||||
}
|
}
|
||||||
return copy_sign(0, y)
|
return copy_sign(0, y)
|
||||||
}
|
}
|
||||||
if is_inf(y, 0) {
|
if is_inf(y, 0) {
|
||||||
return copy_sign(PI*0.75, y)
|
return copy_sign(3*PI/4, y)
|
||||||
}
|
}
|
||||||
return copy_sign(PI, y)
|
return copy_sign(PI, y)
|
||||||
case is_inf(y, 0):
|
case is_inf(y, 0):
|
||||||
return copy_sign(PI*0.5, y)
|
return copy_sign(PI/2, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
q := atan(y / x)
|
q := atan(y / x)
|
||||||
@@ -1347,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atan2 :: proc{
|
atan2 :: proc{
|
||||||
atan2_f16, atan2_f16le, atan2_f16be,
|
atan2_f64, atan2_f32, atan2_f16,
|
||||||
atan2_f32, atan2_f32le, atan2_f32be,
|
atan2_f64le, atan2_f64be,
|
||||||
atan2_f64, atan2_f64le, atan2_f64be,
|
atan2_f32le, atan2_f32be,
|
||||||
|
atan2_f16le, atan2_f16be,
|
||||||
}
|
}
|
||||||
|
|
||||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||||
return atan2(x, 1)
|
return atan2(x, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
|
||||||
return atan2(x, 1 + 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) {
|
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) {
|
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) {
|
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||||
t := exp(2*x)
|
P0 :: -9.64399179425052238628e-1
|
||||||
return (t - 1) / (t + 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) {
|
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]
|
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
|
4 * 1024
|
||||||
|
|
||||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||||
if size == 0 {
|
data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if allocator.procedure == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
|
||||||
_ = err
|
|
||||||
return raw_data(data)
|
return raw_data(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
if size == 0 {
|
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||||
return nil, nil
|
}
|
||||||
}
|
|
||||||
if allocator.procedure == nil {
|
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
return nil, nil
|
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
|
||||||
}
|
|
||||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
if allocator.procedure == nil {
|
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
if bytes == nil {
|
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
if allocator.procedure != nil {
|
return runtime.mem_free_all(allocator, loc)
|
||||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||||
if allocator.procedure == nil {
|
data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
|
||||||
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)
|
|
||||||
}
|
|
||||||
return raw_data(data)
|
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) {
|
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 runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||||
if allocator.procedure != nil {
|
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 set
|
||||||
}
|
}
|
||||||
return nil
|
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) {
|
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||||
props.pointer = pointer
|
props.pointer = pointer
|
||||||
if allocator.procedure != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
free(raw_data(str), allocator, loc)
|
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||||
}
|
}
|
||||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
free((^byte)(str), allocator, loc)
|
return free((^byte)(str), allocator, loc)
|
||||||
}
|
}
|
||||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||||
free(raw_data(array), array.allocator, loc)
|
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) {
|
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
free(raw_data(array), allocator, loc)
|
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) {
|
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||||
raw := transmute(Raw_Map)m
|
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||||
delete_slice(raw.hashes, raw.entries.allocator, loc)
|
|
||||||
free(raw.entries.data, raw.entries.allocator, loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -230,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
|||||||
return nil, .Out_Of_Memory
|
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) {
|
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)
|
runtime.make_slice_error_loc(loc, len)
|
||||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
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)
|
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) {
|
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) {
|
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)
|
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
|
array = transmute(T)s
|
||||||
return
|
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)
|
runtime.make_map_expr_error_loc(loc, cap)
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
m: T
|
m: T
|
||||||
reserve_map(&m, cap)
|
reserve_map(&m, cap, loc)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||||
|
|||||||
+99
-50
@@ -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) {
|
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||||
a.data = data
|
a.data = data
|
||||||
a.offset = 0
|
a.offset = 0
|
||||||
@@ -51,19 +59,22 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
arena := cast(^Arena)allocator_data
|
arena := cast(^Arena)allocator_data
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
total_size := size + alignment
|
#no_bounds_check end := &arena.data[arena.offset]
|
||||||
|
|
||||||
|
ptr := align_forward(end, uintptr(alignment))
|
||||||
|
|
||||||
|
total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
|
||||||
|
|
||||||
if arena.offset + total_size > len(arena.data) {
|
if arena.offset + total_size > len(arena.data) {
|
||||||
return nil, .Out_Of_Memory
|
return nil, .Out_Of_Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
#no_bounds_check end := &arena.data[arena.offset]
|
|
||||||
|
|
||||||
ptr := align_forward(end, uintptr(alignment))
|
|
||||||
arena.offset += total_size
|
arena.offset += total_size
|
||||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
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
|
return byte_slice(ptr, size), nil
|
||||||
|
|
||||||
case .Free:
|
case .Free:
|
||||||
@@ -78,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -142,7 +153,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
s := (^Scratch_Allocator)(allocator_data)
|
s := (^Scratch_Allocator)(allocator_data)
|
||||||
|
|
||||||
if s.data == nil {
|
if s.data == nil {
|
||||||
DEFAULT_BACKING_SIZE :: 1<<22
|
DEFAULT_BACKING_SIZE :: 4 * Megabyte
|
||||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||||
context.allocator.data != allocator_data) {
|
context.allocator.data != allocator_data) {
|
||||||
panic("cyclic initialization of the scratch allocator with itself")
|
panic("cyclic initialization of the scratch allocator with itself")
|
||||||
@@ -153,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
size := size
|
size := size
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
size = align_forward_int(size, alignment)
|
size = align_forward_int(size, alignment)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
@@ -161,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
start := uintptr(raw_data(s.data))
|
start := uintptr(raw_data(s.data))
|
||||||
ptr := start + uintptr(s.curr_offset)
|
ptr := start + uintptr(s.curr_offset)
|
||||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
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)
|
s.prev_allocation = rawptr(ptr)
|
||||||
offset := int(ptr - start)
|
offset := int(ptr - start)
|
||||||
@@ -171,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case size <= len(s.data):
|
case size <= len(s.data):
|
||||||
start := uintptr(raw_data(s.data))
|
start := uintptr(raw_data(s.data))
|
||||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
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)
|
s.prev_allocation = rawptr(ptr)
|
||||||
offset := int(ptr - start)
|
offset := int(ptr - start)
|
||||||
@@ -202,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return ptr, err
|
return ptr, err
|
||||||
|
|
||||||
case .Free:
|
case .Free:
|
||||||
|
if old_memory == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
start := uintptr(raw_data(s.data))
|
start := uintptr(raw_data(s.data))
|
||||||
end := start + uintptr(len(s.data))
|
end := start + uintptr(len(s.data))
|
||||||
old_ptr := uintptr(old_memory)
|
old_ptr := uintptr(old_memory)
|
||||||
@@ -257,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
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
|
return nil, nil
|
||||||
|
|
||||||
@@ -292,6 +310,14 @@ Stack :: struct {
|
|||||||
peak_used: int,
|
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) {
|
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||||
s.data = data
|
s.data = data
|
||||||
s.prev_offset = 0
|
s.prev_offset = 0
|
||||||
@@ -316,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .Invalid_Argument
|
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)
|
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))
|
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
|
||||||
if s.curr_offset + padding + size > len(s.data) {
|
if s.curr_offset + padding + size > len(s.data) {
|
||||||
@@ -334,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
s.peak_used = max(s.peak_used, s.curr_offset)
|
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
|
return byte_slice(rawptr(next_addr), size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return raw_alloc(s, size, alignment)
|
return raw_alloc(s, size, alignment, mode == .Alloc)
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -375,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return raw_alloc(s, size, alignment)
|
return raw_alloc(s, size, alignment, true)
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -401,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)))
|
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||||
|
|
||||||
if old_offset != header.prev_offset {
|
if old_offset != header.prev_offset {
|
||||||
data, err := raw_alloc(s, size, alignment)
|
data, err := raw_alloc(s, size, alignment, true)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||||
}
|
}
|
||||||
@@ -422,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
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
|
return nil, nil
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
@@ -444,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 is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
|
||||||
Small_Stack :: struct {
|
Small_Stack :: struct {
|
||||||
data: []byte,
|
data: []byte,
|
||||||
offset: int,
|
offset: int,
|
||||||
peak_used: 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) {
|
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||||
s.data = data
|
s.data = data
|
||||||
s.offset = 0
|
s.offset = 0
|
||||||
s.peak_used = 0
|
s.peak_used = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||||
return Allocator{
|
return Allocator{
|
||||||
procedure = small_stack_allocator_proc,
|
procedure = small_stack_allocator_proc,
|
||||||
data = stack,
|
data = stack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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
|
s := cast(^Small_Stack)allocator_data
|
||||||
|
|
||||||
if s.data == nil {
|
if s.data == nil {
|
||||||
@@ -473,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)
|
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)
|
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))
|
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
|
||||||
if s.offset + padding + size > len(s.data) {
|
if s.offset + padding + size > len(s.data) {
|
||||||
@@ -489,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
s.peak_used = max(s.peak_used, s.offset)
|
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
|
return byte_slice(rawptr(next_addr), size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return raw_alloc(s, size, align)
|
return raw_alloc(s, size, align, mode == .Alloc)
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -524,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return raw_alloc(s, size, align)
|
return raw_alloc(s, size, align, true)
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -547,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return byte_slice(old_memory, size), nil
|
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 {
|
if err == nil {
|
||||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||||
}
|
}
|
||||||
@@ -556,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
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
|
return nil, nil
|
||||||
|
|
||||||
@@ -599,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
|||||||
pool := (^Dynamic_Pool)(allocator_data)
|
pool := (^Dynamic_Pool)(allocator_data)
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return dynamic_pool_alloc_bytes(pool, size)
|
return dynamic_pool_alloc_bytes(pool, size)
|
||||||
case .Free:
|
case .Free:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
@@ -619,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
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
|
return nil, nil
|
||||||
|
|
||||||
@@ -770,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("mem: panic allocator, .Alloc called")
|
panic("mem: panic allocator, .Alloc called")
|
||||||
}
|
}
|
||||||
|
case .Alloc_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
|
||||||
|
}
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("mem: panic allocator, .Resize called")
|
panic("mem: panic allocator, .Resize called")
|
||||||
@@ -807,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
|
|||||||
memory: rawptr,
|
memory: rawptr,
|
||||||
size: int,
|
size: int,
|
||||||
alignment: int,
|
alignment: int,
|
||||||
|
mode: Allocator_Mode,
|
||||||
err: Allocator_Error,
|
err: Allocator_Error,
|
||||||
location: runtime.Source_Code_Location,
|
location: runtime.Source_Code_Location,
|
||||||
}
|
}
|
||||||
@@ -825,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
|
|||||||
t.backing = backing_allocator
|
t.backing = backing_allocator
|
||||||
t.allocation_map.allocator = internals_allocator
|
t.allocation_map.allocator = internals_allocator
|
||||||
t.bad_free_array.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) {
|
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||||
@@ -832,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
|||||||
delete(t.bad_free_array)
|
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 {
|
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||||
return Allocator{
|
return Allocator{
|
||||||
data = data,
|
data = data,
|
||||||
@@ -841,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
|||||||
|
|
||||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
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)
|
data := (^Tracking_Allocator)(allocator_data)
|
||||||
if mode == .Query_Info {
|
if mode == .Query_Info {
|
||||||
info := (^Allocator_Query_Info)(old_memory)
|
info := (^Allocator_Query_Info)(old_memory)
|
||||||
@@ -853,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
info.pointer = nil
|
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 {
|
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||||
memory = old_memory,
|
memory = old_memory,
|
||||||
location = loc,
|
location = loc,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
|
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result_ptr := raw_data(result)
|
result_ptr := raw_data(result)
|
||||||
|
|
||||||
@@ -876,16 +924,21 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||||
memory = result_ptr,
|
memory = result_ptr,
|
||||||
size = size,
|
size = size,
|
||||||
|
mode = mode,
|
||||||
alignment = alignment,
|
alignment = alignment,
|
||||||
err = err,
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
}
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
delete_key(&data.allocation_map, old_memory)
|
delete_key(&data.allocation_map, old_memory)
|
||||||
|
case .Free_All:
|
||||||
|
if data.clear_on_free_all {
|
||||||
|
clear_map(&data.allocation_map)
|
||||||
|
}
|
||||||
case .Resize:
|
case .Resize:
|
||||||
if old_memory != result_ptr {
|
if old_memory != result_ptr {
|
||||||
delete_key(&data.allocation_map, old_memory)
|
delete_key(&data.allocation_map, old_memory)
|
||||||
@@ -893,20 +946,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||||
memory = result_ptr,
|
memory = result_ptr,
|
||||||
size = size,
|
size = size,
|
||||||
|
mode = mode,
|
||||||
alignment = alignment,
|
alignment = alignment,
|
||||||
err = err,
|
err = err,
|
||||||
location = loc,
|
location = loc,
|
||||||
}
|
}
|
||||||
|
|
||||||
case .Free_All:
|
|
||||||
if data.clear_on_free_all {
|
|
||||||
clear_map(&data.allocation_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
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
|
return nil, nil
|
||||||
|
|
||||||
@@ -914,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-48
@@ -3,11 +3,11 @@ package mem
|
|||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
|
||||||
Byte :: 1
|
Byte :: runtime.Byte
|
||||||
Kilobyte :: 1024 * Byte
|
Kilobyte :: runtime.Kilobyte
|
||||||
Megabyte :: 1024 * Kilobyte
|
Megabyte :: runtime.Megabyte
|
||||||
Gigabyte :: 1024 * Megabyte
|
Gigabyte :: runtime.Gigabyte
|
||||||
Terabyte :: 1024 * Gigabyte
|
Terabyte :: runtime.Terabyte
|
||||||
|
|
||||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||||
return runtime.memset(data, i32(value), len)
|
return runtime.memset(data, i32(value), len)
|
||||||
@@ -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 {
|
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||||
switch {
|
return runtime.memory_compare(a, b, n)
|
||||||
case a == b:
|
|
||||||
return 0
|
|
||||||
case a == nil:
|
|
||||||
return -1
|
|
||||||
case b == nil:
|
|
||||||
return -1
|
|
||||||
case n == 0:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
x := slice_ptr(a, n)
|
|
||||||
y := slice_ptr(b, n)
|
|
||||||
|
|
||||||
SU :: size_of(uintptr)
|
|
||||||
fast := n/SU + 1
|
|
||||||
offset := (fast-1)*SU
|
|
||||||
curr_block := 0
|
|
||||||
if n < SU {
|
|
||||||
fast = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
la := slice_ptr((^uintptr)(a), fast)
|
|
||||||
lb := slice_ptr((^uintptr)(b), fast)
|
|
||||||
|
|
||||||
for /**/; curr_block < fast; curr_block += 1 {
|
|
||||||
if la[curr_block] ~ lb[curr_block] != 0 {
|
|
||||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
|
||||||
if x[pos] ~ y[pos] != 0 {
|
|
||||||
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for /**/; offset < n; offset += 1 {
|
|
||||||
if x[offset] ~ y[offset] != 0 {
|
|
||||||
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check_zero :: proc(data: []byte) -> bool {
|
check_zero :: proc(data: []byte) -> bool {
|
||||||
@@ -109,6 +68,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
|
|||||||
case ptr == nil:
|
case ptr == nil:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
switch len {
|
||||||
|
case 1: return (^u8)(ptr)^ == 0
|
||||||
|
case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0
|
||||||
|
case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0
|
||||||
|
case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
start := uintptr(ptr)
|
start := uintptr(ptr)
|
||||||
start_aligned := align_forward_uintptr(start, align_of(uintptr))
|
start_aligned := align_forward_uintptr(start, align_of(uintptr))
|
||||||
@@ -152,7 +117,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
|||||||
return ([^]T)(ptr)[:len]
|
return ([^]T)(ptr)[:len]
|
||||||
}
|
}
|
||||||
|
|
||||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
|
||||||
return ([^]u8)(data)[:max(len, 0)]
|
return ([^]u8)(data)[:max(len, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-19
@@ -1,5 +1,6 @@
|
|||||||
package mem
|
package mem
|
||||||
|
|
||||||
|
import "core:builtin"
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
|
|
||||||
Raw_Any :: runtime.Raw_Any
|
Raw_Any :: runtime.Raw_Any
|
||||||
@@ -8,6 +9,7 @@ Raw_Cstring :: runtime.Raw_Cstring
|
|||||||
Raw_Slice :: runtime.Raw_Slice
|
Raw_Slice :: runtime.Raw_Slice
|
||||||
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
|
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
|
||||||
Raw_Map :: runtime.Raw_Map
|
Raw_Map :: runtime.Raw_Map
|
||||||
|
Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
|
||||||
|
|
||||||
Raw_Complex64 :: struct {real, imag: f32}
|
Raw_Complex64 :: struct {real, imag: f32}
|
||||||
Raw_Complex128 :: struct {real, imag: f64}
|
Raw_Complex128 :: struct {real, imag: f64}
|
||||||
@@ -20,22 +22,4 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
|||||||
return transmute(any)Raw_Any{data, id}
|
return transmute(any)Raw_Any{data, id}
|
||||||
}
|
}
|
||||||
|
|
||||||
raw_array_data :: runtime.raw_array_data
|
raw_data :: builtin.raw_data
|
||||||
raw_simd_data :: runtime.raw_simd_data
|
|
||||||
raw_string_data :: runtime.raw_string_data
|
|
||||||
raw_slice_data :: runtime.raw_slice_data
|
|
||||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
|
||||||
raw_data :: runtime.raw_data
|
|
||||||
|
|
||||||
|
|
||||||
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
|
|
||||||
hash: uintptr,
|
|
||||||
next: int,
|
|
||||||
key: Key,
|
|
||||||
value: Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
Poly_Raw_Map :: struct($Key, $Value: typeid) {
|
|
||||||
hashes: []int,
|
|
||||||
entries: [dynamic]Poly_Raw_Map_Entry(Key, Value),
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,401 @@
|
|||||||
|
package mem_virtual
|
||||||
|
|
||||||
|
import "core:mem"
|
||||||
|
import "core:sync"
|
||||||
|
|
||||||
|
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 is a generalized arena allocator that supports 3 different variants.
|
||||||
|
|
||||||
|
Growing: A linked list of `Memory_Block`s allocated with virtual memory.
|
||||||
|
Static: A single `Memory_Block` allocated with virtual memory.
|
||||||
|
Buffer: A single `Memory_Block` created from a user provided []byte.
|
||||||
|
*/
|
||||||
|
Arena :: struct {
|
||||||
|
kind: Arena_Kind,
|
||||||
|
curr_block: ^Memory_Block,
|
||||||
|
total_used: uint,
|
||||||
|
total_reserved: uint,
|
||||||
|
minimum_block_size: uint,
|
||||||
|
temp_count: uint,
|
||||||
|
mutex: sync.Mutex,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 1 MiB should be enough to start with
|
||||||
|
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: mem.Megabyte
|
||||||
|
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 :: mem.Gigabyte when size_of(uintptr) == 8 else 128 * mem.Megabyte
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Initialization of an `Arena` to be a `.Growing` variant.
|
||||||
|
// A growing arena is a linked list of `Memory_Block`s allocated with virtual memory.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialization of an `Arena` to be a `.Static` variant.
|
||||||
|
// A static arena contains a single `Memory_Block` allocated with virtual memory.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization of an `Arena` to be a `.Buffer` variant.
|
||||||
|
// A buffer arena contains single `Memory_Block` created from a user provided []byte.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates memory from the provided arena.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resets the memory of a Static or Buffer arena to a specific `pos`ition (offset) and zeroes the previously used memory.
|
||||||
|
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees the last memory block of a Growing Arena
|
||||||
|
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.total_used -= free_block.used
|
||||||
|
arena.total_reserved -= free_block.reserved
|
||||||
|
|
||||||
|
arena.curr_block = free_block.prev
|
||||||
|
memory_block_dealloc(free_block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deallocates all but the first memory block of the arena and resets the allocator's usage to 0.
|
||||||
|
arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
|
||||||
|
switch arena.kind {
|
||||||
|
case .Growing:
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
// NOTE(bill): Free all but the first memory block (if it exists)
|
||||||
|
for arena.curr_block != nil && arena.curr_block.prev != nil {
|
||||||
|
arena_growing_free_last_memory_block(arena, loc)
|
||||||
|
}
|
||||||
|
// Zero the first block's memory
|
||||||
|
if arena.curr_block != nil {
|
||||||
|
mem.zero(arena.curr_block.base, int(arena.curr_block.used))
|
||||||
|
arena.curr_block.used = 0
|
||||||
|
}
|
||||||
|
arena.total_used = 0
|
||||||
|
case .Static, .Buffer:
|
||||||
|
arena_static_reset_to(arena, 0)
|
||||||
|
}
|
||||||
|
arena.total_used = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frees all of the memory allocated by the arena and zeros all of the values of an arena.
|
||||||
|
// A buffer based arena does not `delete` the provided `[]byte` bufffer.
|
||||||
|
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
switch arena.kind {
|
||||||
|
case .Growing:
|
||||||
|
for arena.curr_block != nil {
|
||||||
|
arena_growing_free_last_memory_block(arena, loc)
|
||||||
|
}
|
||||||
|
case .Static:
|
||||||
|
memory_block_dealloc(arena.curr_block)
|
||||||
|
case .Buffer:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
arena.curr_block = nil
|
||||||
|
arena.total_used = 0
|
||||||
|
arena.total_reserved = 0
|
||||||
|
arena.temp_count = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
||||||
|
arena_growing_bootstrap_new :: proc{
|
||||||
|
arena_growing_bootstrap_new_by_offset,
|
||||||
|
arena_growing_bootstrap_new_by_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
|
||||||
|
arena_static_bootstrap_new :: proc{
|
||||||
|
arena_static_bootstrap_new_by_offset,
|
||||||
|
arena_static_bootstrap_new_by_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
||||||
|
@(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
||||||
|
@(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
||||||
|
@(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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create an `Allocator` from the provided `Arena`
|
||||||
|
@(require_results)
|
||||||
|
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
|
||||||
|
return mem.Allocator{arena_allocator_proc, arena}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The allocator procedured by an `Allocator` produced by `arena_allocator`
|
||||||
|
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, location)
|
||||||
|
case .Free:
|
||||||
|
err = .Mode_Not_Implemented
|
||||||
|
case .Free_All:
|
||||||
|
arena_free_all(arena, location)
|
||||||
|
case .Resize:
|
||||||
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case old_data == nil:
|
||||||
|
return arena_alloc(arena, size, alignment, location)
|
||||||
|
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, location) 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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state.
|
||||||
|
// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`.
|
||||||
|
Arena_Temp :: struct {
|
||||||
|
arena: ^Arena,
|
||||||
|
block: ^Memory_Block,
|
||||||
|
used: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begins the section of temporary arena memory.
|
||||||
|
@(require_results)
|
||||||
|
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
|
||||||
|
assert(arena != nil, "nil arena", loc)
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
|
||||||
|
temp.arena = arena
|
||||||
|
temp.block = arena.curr_block
|
||||||
|
if arena.curr_block != nil {
|
||||||
|
temp.used = arena.curr_block.used
|
||||||
|
}
|
||||||
|
arena.temp_count += 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ends the section of temporary arena memory by resetting the memory to the stored position.
|
||||||
|
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||||
|
assert(temp.arena != nil, "nil arena", loc)
|
||||||
|
arena := temp.arena
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
|
||||||
|
if temp.block != nil {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position.
|
||||||
|
arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||||
|
assert(temp.arena != nil, "nil arena", loc)
|
||||||
|
arena := temp.arena
|
||||||
|
sync.mutex_guard(&arena.mutex)
|
||||||
|
|
||||||
|
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
|
||||||
|
arena.temp_count -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asserts that all uses of `Arena_Temp` has been used by an `Arena`
|
||||||
|
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,11 +1,13 @@
|
|||||||
package mem_virtual
|
package mem_virtual
|
||||||
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:intrinsics"
|
||||||
|
|
||||||
DEFAULT_PAGE_SIZE := uint(4096)
|
DEFAULT_PAGE_SIZE := uint(4096)
|
||||||
|
|
||||||
Allocator_Error :: mem.Allocator_Error
|
Allocator_Error :: mem.Allocator_Error
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
return _reserve(size)
|
return _reserve(size)
|
||||||
}
|
}
|
||||||
@@ -14,6 +16,7 @@ commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
|||||||
return _commit(data, size)
|
return _commit(data, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
data = reserve(size) or_return
|
data = reserve(size) or_return
|
||||||
commit(raw_data(data), size) or_return
|
commit(raw_data(data), size) or_return
|
||||||
@@ -56,6 +59,7 @@ Memory_Block_Flag :: enum u32 {
|
|||||||
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
|
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
|
||||||
|
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
|
memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||||
align_formula :: proc "contextless" (size, align: uint) -> uint {
|
align_formula :: proc "contextless" (size, align: uint) -> uint {
|
||||||
result := size + align-1
|
result := size + align-1
|
||||||
@@ -63,6 +67,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
page_size := DEFAULT_PAGE_SIZE
|
page_size := DEFAULT_PAGE_SIZE
|
||||||
|
assert(mem.is_power_of_two(uintptr(page_size)))
|
||||||
committed := committed
|
committed := committed
|
||||||
committed = clamp(committed, 0, reserved)
|
committed = clamp(committed, 0, reserved)
|
||||||
|
|
||||||
@@ -82,8 +87,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
|||||||
pmblock := platform_memory_alloc(0, total_size) or_return
|
pmblock := platform_memory_alloc(0, total_size) or_return
|
||||||
|
|
||||||
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||||
commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
|
platform_memory_commit(pmblock, uint(base_offset) + committed) or_return
|
||||||
assert(commit_err == nil)
|
|
||||||
|
|
||||||
// Should be zeroed
|
// Should be zeroed
|
||||||
assert(pmblock.block.used == 0)
|
assert(pmblock.block.used == 0)
|
||||||
@@ -95,18 +99,12 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
|||||||
pmblock.block.committed = committed
|
pmblock.block.committed = committed
|
||||||
pmblock.block.reserved = reserved
|
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
|
return &pmblock.block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
@(require_results)
|
||||||
|
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 {
|
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||||
alignment_offset := uint(0)
|
alignment_offset := uint(0)
|
||||||
ptr := uintptr(block.base[block.used:])
|
ptr := uintptr(block.base[block.used:])
|
||||||
@@ -134,11 +132,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if block == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
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
|
err = .Out_Of_Memory
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -153,12 +158,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
|||||||
|
|
||||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
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)
|
platform_memory_free(block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(private, require_results)
|
||||||
|
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
||||||
|
z, did_overflow := intrinsics.overflow_add(x, y)
|
||||||
|
return z, !did_overflow
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
//+build darwin
|
||||||
|
//+private
|
||||||
|
package mem_virtual
|
||||||
|
|
||||||
|
foreign import libc "System.framework"
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
PROT_NONE :: 0x0 /* [MC2] no permissions */
|
||||||
|
PROT_READ :: 0x1 /* [MC2] pages can be read */
|
||||||
|
PROT_WRITE :: 0x2 /* [MC2] pages can be written */
|
||||||
|
PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
|
||||||
|
|
||||||
|
// Sharing options
|
||||||
|
MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
|
||||||
|
MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
|
||||||
|
|
||||||
|
// Other flags
|
||||||
|
MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
|
||||||
|
MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
|
||||||
|
MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
|
||||||
|
MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
|
||||||
|
MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
|
||||||
|
MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
|
||||||
|
MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
|
||||||
|
MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
|
||||||
|
|
||||||
|
// Mapping type
|
||||||
|
MAP_FILE :: 0x0000 /* map from file (default) */
|
||||||
|
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The MAP_RESILIENT_* flags can be used when the caller wants to map some
|
||||||
|
* possibly unreliable memory and be able to access it safely, possibly
|
||||||
|
* getting the wrong contents rather than raising any exception.
|
||||||
|
* For safety reasons, such mappings have to be read-only (PROT_READ access
|
||||||
|
* only).
|
||||||
|
*
|
||||||
|
* MAP_RESILIENT_CODESIGN:
|
||||||
|
* accessing this mapping will not generate code-signing violations,
|
||||||
|
* even if the contents are tainted.
|
||||||
|
* MAP_RESILIENT_MEDIA:
|
||||||
|
* accessing this mapping will not generate an exception if the contents
|
||||||
|
* are not available (unreachable removable or remote media, access beyond
|
||||||
|
* end-of-file, ...). Missing contents will be replaced with zeroes.
|
||||||
|
*/
|
||||||
|
MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
|
||||||
|
MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
|
||||||
|
|
||||||
|
MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
|
||||||
|
|
||||||
|
// Flags used to support translated processes.
|
||||||
|
MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
|
||||||
|
MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
|
||||||
|
|
||||||
|
// Process memory locking
|
||||||
|
MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
|
||||||
|
MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
|
||||||
|
|
||||||
|
MADV_NORMAL :: 0 /* [MC1] no further special treatment */
|
||||||
|
MADV_RANDOM :: 1 /* [MC1] expect random page refs */
|
||||||
|
MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
|
||||||
|
MADV_WILLNEED :: 3 /* [MC1] will need these pages */
|
||||||
|
MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
|
||||||
|
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||||
|
MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
|
||||||
|
MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
|
||||||
|
MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
|
||||||
|
MADV_CAN_REUSE :: 9
|
||||||
|
MADV_PAGEOUT :: 10 /* page out now (internal only) */
|
||||||
|
|
||||||
|
// msync() flags
|
||||||
|
MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
|
||||||
|
MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
|
||||||
|
MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
|
||||||
|
MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
|
||||||
|
MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
|
||||||
|
|
||||||
|
// Return bits from mincore
|
||||||
|
MINCORE_INCORE :: 0x1 /* Page is incore */
|
||||||
|
MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
|
||||||
|
MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
|
||||||
|
MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
|
||||||
|
MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
|
||||||
|
MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
|
||||||
|
MINCORE_COPIED :: 0x40 /* Page has been copied */
|
||||||
|
MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
|
||||||
|
|
||||||
|
// Allocation failure result
|
||||||
|
MAP_FAILED : rawptr = rawptr(~uintptr(0))
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
|
||||||
|
@(link_name="munlockall") _munlockall :: proc() -> c.int ---
|
||||||
|
@(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||||
|
@(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
|
||||||
|
@(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
|
||||||
|
@(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||||
|
@(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||||
|
@(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||||
|
@(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
|
||||||
|
@(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
|
||||||
|
@(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||||
|
@(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||||
|
@(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
|
||||||
|
@(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||||
|
if result == MAP_FAILED {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
return ([^]byte)(uintptr(result))[:size], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||||
|
result := _mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||||
|
if result != 0 {
|
||||||
|
return .Out_Of_Memory
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
|
_mprotect(data, size, PROT_NONE)
|
||||||
|
_madvise(data, size, MADV_FREE)
|
||||||
|
}
|
||||||
|
_release :: proc "contextless" (data: rawptr, size: uint) {
|
||||||
|
_munmap(data, size)
|
||||||
|
}
|
||||||
|
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||||
|
pflags: c.int
|
||||||
|
pflags = PROT_NONE
|
||||||
|
if .Read in flags { pflags |= PROT_READ }
|
||||||
|
if .Write in flags { pflags |= PROT_WRITE }
|
||||||
|
if .Execute in flags { pflags |= PROT_EXEC }
|
||||||
|
err := _mprotect(data, size, pflags)
|
||||||
|
return err != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_platform_memory_init :: proc() {
|
||||||
|
DEFAULT_PAGE_SIZE = 4096
|
||||||
|
|
||||||
|
// is power of two
|
||||||
|
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||||
|
}
|
||||||
@@ -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 {
|
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)
|
return c.int(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
//+private
|
//+private
|
||||||
package mem_virtual
|
package mem_virtual
|
||||||
|
|
||||||
import "core:sync"
|
|
||||||
|
|
||||||
Platform_Memory_Block :: struct {
|
Platform_Memory_Block :: struct {
|
||||||
block: Memory_Block,
|
block: Memory_Block,
|
||||||
committed: uint,
|
committed: uint,
|
||||||
reserved: 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) {
|
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) {
|
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||||
if to_commit < block.committed {
|
if to_commit < block.committed {
|
||||||
return nil
|
return nil
|
||||||
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
|
|||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
commit(block, to_commit) or_return
|
commit(block, to_commit) or_return
|
||||||
block.committed = to_commit
|
block.committed = to_commit
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ PAGE_TARGETS_INVALID :: 0x40000000
|
|||||||
PAGE_TARGETS_NO_UPDATE :: 0x40000000
|
PAGE_TARGETS_NO_UPDATE :: 0x40000000
|
||||||
|
|
||||||
ERROR_INVALID_ADDRESS :: 487
|
ERROR_INVALID_ADDRESS :: 487
|
||||||
|
ERROR_COMMITMENT_LIMIT :: 1455
|
||||||
|
|
||||||
@(default_calling_convention="stdcall")
|
@(default_calling_convention="stdcall")
|
||||||
foreign Kernel32 {
|
foreign Kernel32 {
|
||||||
@@ -76,12 +77,13 @@ _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
|||||||
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
||||||
if result == nil {
|
if result == nil {
|
||||||
switch err := GetLastError(); err {
|
switch err := GetLastError(); err {
|
||||||
case ERROR_INVALID_ADDRESS:
|
case 0:
|
||||||
|
return .Invalid_Argument
|
||||||
|
case ERROR_INVALID_ADDRESS, ERROR_COMMITMENT_LIMIT:
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bill): Handle errors correctly
|
return .Out_Of_Memory
|
||||||
return .Invalid_Argument
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-2
@@ -6,7 +6,7 @@ Proc_Tag :: enum {
|
|||||||
Bounds_Check,
|
Bounds_Check,
|
||||||
No_Bounds_Check,
|
No_Bounds_Check,
|
||||||
Optional_Ok,
|
Optional_Ok,
|
||||||
Optional_Second,
|
Optional_Allocator_Error,
|
||||||
}
|
}
|
||||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32]
|
Proc_Tags :: distinct bit_set[Proc_Tag; u32]
|
||||||
|
|
||||||
@@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Field_Flags :: distinct bit_set[Field_Flag]
|
||||||
|
|
||||||
Field_Flag :: enum {
|
Field_Flag :: enum {
|
||||||
|
Invalid,
|
||||||
|
Unknown,
|
||||||
|
|
||||||
Ellipsis,
|
Ellipsis,
|
||||||
Using,
|
Using,
|
||||||
No_Alias,
|
No_Alias,
|
||||||
C_Vararg,
|
C_Vararg,
|
||||||
Auto_Cast,
|
Auto_Cast,
|
||||||
Any_Int,
|
Any_Int,
|
||||||
|
Subtype,
|
||||||
|
By_Ptr,
|
||||||
|
|
||||||
Results,
|
Results,
|
||||||
Tags,
|
Tags,
|
||||||
@@ -566,11 +573,38 @@ Field_Flag :: enum {
|
|||||||
Typeid_Token,
|
Typeid_Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
Field_Flags :: distinct bit_set[Field_Flag]
|
field_flag_strings := [Field_Flag]string{
|
||||||
|
.Invalid = "",
|
||||||
|
.Unknown = "",
|
||||||
|
|
||||||
|
.Ellipsis = "..",
|
||||||
|
.Using = "using",
|
||||||
|
.No_Alias = "#no_alias",
|
||||||
|
.C_Vararg = "#c_vararg",
|
||||||
|
.Auto_Cast = "auto_cast",
|
||||||
|
.Any_Int = "#any_int",
|
||||||
|
.Subtype = "#subtype",
|
||||||
|
.By_Ptr = "#by_ptr",
|
||||||
|
|
||||||
|
.Results = "results",
|
||||||
|
.Tags = "field tag",
|
||||||
|
.Default_Parameters = "default parameters",
|
||||||
|
.Typeid_Token = "typeid",
|
||||||
|
}
|
||||||
|
|
||||||
|
field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
||||||
|
{"no_alias", .No_Alias},
|
||||||
|
{"c_vararg", .C_Vararg},
|
||||||
|
{"any_int", .Any_Int},
|
||||||
|
{"subtype", .Subtype},
|
||||||
|
{"by_ptr", .By_Ptr},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Field_Flags_Struct :: Field_Flags{
|
Field_Flags_Struct :: Field_Flags{
|
||||||
.Using,
|
.Using,
|
||||||
.Tags,
|
.Tags,
|
||||||
|
.Subtype,
|
||||||
}
|
}
|
||||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||||
.Typeid_Token,
|
.Typeid_Token,
|
||||||
@@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{
|
|||||||
.C_Vararg,
|
.C_Vararg,
|
||||||
.Auto_Cast,
|
.Auto_Cast,
|
||||||
.Any_Int,
|
.Any_Int,
|
||||||
|
.By_Ptr,
|
||||||
.Default_Parameters,
|
.Default_Parameters,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -665,6 +700,7 @@ Proc_Type :: struct {
|
|||||||
|
|
||||||
Pointer_Type :: struct {
|
Pointer_Type :: struct {
|
||||||
using node: Expr,
|
using node: Expr,
|
||||||
|
tag: ^Expr,
|
||||||
pointer: tokenizer.Pos,
|
pointer: tokenizer.Pos,
|
||||||
elem: ^Expr,
|
elem: ^Expr,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
|||||||
r.results = auto_cast clone(r.results)
|
r.results = auto_cast clone(r.results)
|
||||||
case ^Pointer_Type:
|
case ^Pointer_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
|
r.tag = clone(r.tag)
|
||||||
case ^Multi_Pointer_Type:
|
case ^Multi_Pointer_Type:
|
||||||
r.elem = clone(r.elem)
|
r.elem = clone(r.elem)
|
||||||
case ^Array_Type:
|
case ^Array_Type:
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
|
|||||||
Relative_Slice = 21,
|
Relative_Slice = 21,
|
||||||
Multi_Pointer = 22,
|
Multi_Pointer = 22,
|
||||||
Matrix = 23,
|
Matrix = 23,
|
||||||
|
Soa_Pointer = 24,
|
||||||
}
|
}
|
||||||
|
|
||||||
Type_Elems_Cap :: 4
|
Type_Elems_Cap :: 4
|
||||||
@@ -245,6 +246,7 @@ Type :: struct {
|
|||||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||||
// .Multi_Pointer - 1 type: 0=element
|
// .Multi_Pointer - 1 type: 0=element
|
||||||
// .Matrix - 1 type: 0=element
|
// .Matrix - 1 type: 0=element
|
||||||
|
// .Soa_Pointer - 1 type: 0=element
|
||||||
types: Array(Type_Index),
|
types: Array(Type_Index),
|
||||||
|
|
||||||
// Used by:
|
// Used by:
|
||||||
|
|||||||
@@ -183,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
|||||||
pd.name = pkg_name.text
|
pd.name = pkg_name.text
|
||||||
pd.comment = p.line_comment
|
pd.comment = p.line_comment
|
||||||
p.file.pkg_decl = pd
|
p.file.pkg_decl = pd
|
||||||
|
p.file.docs = docs
|
||||||
|
|
||||||
expect_semicolon(p, pd)
|
expect_semicolon(p, pd)
|
||||||
|
|
||||||
@@ -244,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||||
if .Optional_Semicolons not_in p.flags {
|
if tokenizer.is_newline(p.curr_tok) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
prev := p.curr_tok
|
|
||||||
if tokenizer.is_newline(prev) {
|
|
||||||
advance_token(p)
|
advance_token(p)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -1610,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E
|
|||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Field_Prefix :: enum {
|
|
||||||
Invalid,
|
|
||||||
Unknown,
|
|
||||||
|
|
||||||
Using,
|
|
||||||
No_Alias,
|
|
||||||
C_Vararg,
|
|
||||||
Auto_Cast,
|
|
||||||
Any_Int,
|
|
||||||
}
|
|
||||||
|
|
||||||
Field_Prefixes :: distinct bit_set[Field_Prefix]
|
|
||||||
|
|
||||||
Expr_And_Flags :: struct {
|
Expr_And_Flags :: struct {
|
||||||
expr: ^ast.Expr,
|
expr: ^ast.Expr,
|
||||||
flags: ast.Field_Flags,
|
flags: ast.Field_Flags,
|
||||||
@@ -1665,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
|
|||||||
return idents[:]
|
return idents[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag {
|
||||||
#partial switch p.curr_tok.kind {
|
#partial switch p.curr_tok.kind {
|
||||||
case .EOF:
|
case .EOF:
|
||||||
return .Invalid
|
return .Invalid
|
||||||
@@ -1676,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
|||||||
advance_token(p)
|
advance_token(p)
|
||||||
return .Auto_Cast
|
return .Auto_Cast
|
||||||
case .Hash:
|
case .Hash:
|
||||||
|
tok: tokenizer.Token
|
||||||
advance_token(p)
|
advance_token(p)
|
||||||
defer advance_token(p)
|
tok = p.curr_tok
|
||||||
#partial switch p.curr_tok.kind {
|
advance_token(p)
|
||||||
case .Ident:
|
if tok.kind == .Ident {
|
||||||
switch p.curr_tok.text {
|
for kf in ast.field_hash_flag_strings {
|
||||||
case "no_alias":
|
if kf.key == tok.text {
|
||||||
return .No_Alias
|
return kf.flag
|
||||||
case "c_vararg":
|
}
|
||||||
return .C_Vararg
|
|
||||||
case "any_int":
|
|
||||||
return .Any_Int
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .Unknown
|
return .Unknown
|
||||||
@@ -1694,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
|||||||
return .Invalid
|
return .Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) {
|
||||||
counts: [len(Field_Prefix)]int
|
counts: [len(ast.Field_Flag)]int
|
||||||
|
|
||||||
for {
|
for {
|
||||||
kind := is_token_field_prefix(p)
|
kind := is_token_field_prefix(p)
|
||||||
@@ -1711,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
|||||||
counts[kind] += 1
|
counts[kind] += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
flags: ast.Field_Flags
|
for kind in ast.Field_Flag {
|
||||||
|
|
||||||
for kind in Field_Prefix {
|
|
||||||
count := counts[kind]
|
count := counts[kind]
|
||||||
switch kind {
|
if kind == .Invalid || kind == .Unknown {
|
||||||
case .Invalid, .Unknown: // Ignore
|
// Ignore
|
||||||
case .Using:
|
} else {
|
||||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") }
|
if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) }
|
||||||
if count > 0 { flags += {.Using} }
|
if count > 0 { flags += {kind} }
|
||||||
case .No_Alias:
|
|
||||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") }
|
|
||||||
if count > 0 { flags += {.No_Alias} }
|
|
||||||
case .C_Vararg:
|
|
||||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") }
|
|
||||||
if count > 0 { flags += {.C_Vararg} }
|
|
||||||
case .Auto_Cast:
|
|
||||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") }
|
|
||||||
if count > 0 { flags += {.Auto_Cast} }
|
|
||||||
case .Any_Int:
|
|
||||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") }
|
|
||||||
if count > 0 { flags += {.Any_Int} }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
|
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
|
||||||
@@ -1747,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
|
|||||||
|
|
||||||
for flag in ast.Field_Flag {
|
for flag in ast.Field_Flag {
|
||||||
if flag not_in allowed_flags && flag in flags {
|
if flag not_in allowed_flags && flag in flags {
|
||||||
switch flag {
|
#partial switch flag {
|
||||||
case .Using:
|
case .Unknown, .Invalid:
|
||||||
error(p, p.curr_tok.pos, "'using' is not allowed within this field list")
|
// ignore
|
||||||
case .No_Alias:
|
|
||||||
error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list")
|
|
||||||
case .C_Vararg:
|
|
||||||
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list")
|
|
||||||
case .Auto_Cast:
|
|
||||||
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list")
|
|
||||||
case .Any_Int:
|
|
||||||
error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list")
|
|
||||||
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
|
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
|
||||||
panic("Impossible prefixes")
|
panic("Impossible prefixes")
|
||||||
|
case:
|
||||||
|
error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag])
|
||||||
}
|
}
|
||||||
flags -= {flag}
|
flags -= {flag}
|
||||||
}
|
}
|
||||||
@@ -2102,7 +2062,7 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
|||||||
case "bounds_check": tags += {.Bounds_Check}
|
case "bounds_check": tags += {.Bounds_Check}
|
||||||
case "no_bounds_check": tags += {.No_Bounds_Check}
|
case "no_bounds_check": tags += {.No_Bounds_Check}
|
||||||
case "optional_ok": tags += {.Optional_Ok}
|
case "optional_ok": tags += {.Optional_Ok}
|
||||||
case "optional_second": tags += {.Optional_Second}
|
case "optional_allocator_error": tags += {.Optional_Allocator_Error}
|
||||||
case:
|
case:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2270,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
return parse_call_expr(p, bd)
|
return parse_call_expr(p, bd)
|
||||||
|
|
||||||
|
|
||||||
case "soa", "simd":
|
case "soa":
|
||||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||||
bd.tok = tok
|
bd.tok = tok
|
||||||
bd.name = name.text
|
bd.name = name.text
|
||||||
@@ -2279,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
#partial switch t in type.derived_expr {
|
#partial switch t in type.derived_expr {
|
||||||
case ^ast.Array_Type: t.tag = bd
|
case ^ast.Array_Type: t.tag = bd
|
||||||
case ^ast.Dynamic_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:
|
case:
|
||||||
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
||||||
}
|
}
|
||||||
@@ -2630,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
tok := expect_token(p, .Union)
|
tok := expect_token(p, .Union)
|
||||||
poly_params: ^ast.Field_List
|
poly_params: ^ast.Field_List
|
||||||
align: ^ast.Expr
|
align: ^ast.Expr
|
||||||
is_maybe: bool
|
|
||||||
is_no_nil: bool
|
is_no_nil: bool
|
||||||
is_shared_nil: bool
|
is_shared_nil: bool
|
||||||
|
|
||||||
@@ -2655,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
}
|
}
|
||||||
align = parse_expr(p, true)
|
align = parse_expr(p, true)
|
||||||
case "maybe":
|
case "maybe":
|
||||||
if is_maybe {
|
error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text)
|
||||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
|
||||||
}
|
|
||||||
is_maybe = true
|
|
||||||
case "no_nil":
|
case "no_nil":
|
||||||
if is_no_nil {
|
if is_no_nil {
|
||||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
||||||
@@ -2675,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
}
|
}
|
||||||
p.expr_level = prev_level
|
p.expr_level = prev_level
|
||||||
|
|
||||||
if is_no_nil && is_maybe {
|
|
||||||
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
|
|
||||||
}
|
|
||||||
if is_no_nil && is_shared_nil {
|
if is_no_nil && is_shared_nil {
|
||||||
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
|
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
|
||||||
}
|
}
|
||||||
if is_shared_nil && is_maybe {
|
|
||||||
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
|
|
||||||
}
|
|
||||||
|
|
||||||
union_kind := ast.Union_Type_Kind.Normal
|
union_kind := ast.Union_Type_Kind.Normal
|
||||||
switch {
|
switch {
|
||||||
case is_maybe: union_kind = .maybe
|
|
||||||
case is_no_nil: union_kind = .no_nil
|
case is_no_nil: union_kind = .no_nil
|
||||||
case is_shared_nil: union_kind = .shared_nil
|
case is_shared_nil: union_kind = .shared_nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
|
|||||||
|
|
||||||
fix_lines(p)
|
fix_lines(p)
|
||||||
|
|
||||||
builder := strings.make_builder(0, 5 * mem.Megabyte, p.allocator)
|
builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator)
|
||||||
|
|
||||||
last_line := 0
|
last_line := 0
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
|||||||
|
|
||||||
return 0
|
return 0
|
||||||
} else {
|
} else {
|
||||||
builder := strings.make_builder(context.temp_allocator)
|
builder := strings.builder_make(context.temp_allocator)
|
||||||
|
|
||||||
c_len := len(comment.text)
|
c_len := len(comment.text)
|
||||||
trim_space := true
|
trim_space := true
|
||||||
@@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
|||||||
continue
|
continue
|
||||||
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
|
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
|
||||||
append(&multilines, strings.to_string(builder))
|
append(&multilines, strings.to_string(builder))
|
||||||
builder = strings.make_builder(context.temp_allocator)
|
builder = strings.builder_make(context.temp_allocator)
|
||||||
trim_space = true
|
trim_space = true
|
||||||
i += 1
|
i += 1
|
||||||
case c == '\n':
|
case c == '\n':
|
||||||
append(&multilines, strings.to_string(builder))
|
append(&multilines, strings.to_string(builder))
|
||||||
builder = strings.make_builder(context.temp_allocator)
|
builder = strings.builder_make(context.temp_allocator)
|
||||||
trim_space = true
|
trim_space = true
|
||||||
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
|
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
|
||||||
strings.write_string(&builder, "/*")
|
strings.write_string(&builder, "/*")
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
|
|
||||||
dirpath: string
|
dirpath: string
|
||||||
dirpath, err = absolute_path_from_handle(fd)
|
dirpath, err = absolute_path_from_handle(fd)
|
||||||
|
|
||||||
if err != ERROR_NONE {
|
if err != ERROR_NONE {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer delete(dirpath)
|
||||||
|
|
||||||
n := n
|
n := n
|
||||||
size := n
|
size := n
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package os
|
package os
|
||||||
|
|
||||||
import "core:strings"
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||||
@@ -51,10 +50,13 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
|
||||||
|
copy(fullpath, dirpath)
|
||||||
|
copy(fullpath[len(dirpath):], "/")
|
||||||
|
copy(fullpath[len(dirpath)+1:], filename)
|
||||||
defer delete(fullpath, context.temp_allocator)
|
defer delete(fullpath, context.temp_allocator)
|
||||||
|
|
||||||
fi_, err = stat(fullpath, allocator)
|
fi_, err = stat(string(fullpath), allocator)
|
||||||
if err != ERROR_NONE {
|
if err != ERROR_NONE {
|
||||||
for fi__ in dfi {
|
for fi__ in dfi {
|
||||||
file_info_delete(fi__, allocator)
|
file_info_delete(fi__, allocator)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package os
|
|||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:runtime"
|
||||||
|
|
||||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||||
dirp: Dir
|
dirp: Dir
|
||||||
@@ -51,6 +52,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
||||||
defer delete(fullpath, context.temp_allocator)
|
defer delete(fullpath, context.temp_allocator)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user