mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 03:31:38 -07:00
Compare commits
1169 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 | |||
| 0cb1a578d0 | |||
| e139d1cbe4 | |||
| cb04116caf | |||
| 62ff8daa78 |
+13
-13
@@ -38,10 +38,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
@@ -92,10 +92,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
@@ -156,6 +156,13 @@ jobs:
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\internal
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -163,13 +170,6 @@ jobs:
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -50,6 +50,7 @@ jobs:
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp libLLVM* dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v4.1.0
|
||||
uses: actions/stale@v7.0.0
|
||||
with:
|
||||
# stale-issue-message: |
|
||||
# Hello!
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
# 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: 30
|
||||
days-before-close: -1
|
||||
exempt-draft-pr: true
|
||||
ascending: true
|
||||
operations-per-run: 1000
|
||||
|
||||
@@ -271,6 +271,7 @@ odin
|
||||
odin.dSYM
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
@@ -283,3 +284,4 @@ shared/
|
||||
*.sublime-workspace
|
||||
examples/bug/
|
||||
build.sh
|
||||
!core/debug/
|
||||
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
Get live support and talk with other Odin programmers on the Odin Discord.
|
||||
|
||||
### Articles
|
||||
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
where /Q cl.exe || (
|
||||
set __VSCMD_ARG_NO_LOGO=1
|
||||
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
|
||||
if "!VS!" equ "" (
|
||||
echo ERROR: Visual Studio installation not found
|
||||
exit /b 1
|
||||
)
|
||||
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
|
||||
)
|
||||
|
||||
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
@@ -47,12 +62,14 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||
-wd4505 ^
|
||||
-wd4456 -wd4457
|
||||
|
||||
set compiler_includes= ^
|
||||
/Isrc\
|
||||
set libs= ^
|
||||
kernel32.lib ^
|
||||
Synchronization.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
@@ -79,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
|
||||
+41
-4
@@ -32,7 +32,7 @@ config_darwin() {
|
||||
if [ ARCH == arm64 ]; then
|
||||
MIN_LLVM_VERSION=("13.0.0")
|
||||
else
|
||||
# allow for x86 / amd64 all llvm versions begining from 11
|
||||
# allow for x86 / amd64 all llvm versions beginning from 11
|
||||
MIN_LLVM_VERSION=("11.1.0")
|
||||
fi
|
||||
|
||||
@@ -44,13 +44,31 @@ config_darwin() {
|
||||
fi
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
|
||||
config_freebsd() {
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
|
||||
: ${LLVM_CONFIG=}
|
||||
|
||||
if [ ! "$LLVM_CONFIG" ]; then
|
||||
if which llvm-config11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config11
|
||||
elif which llvm-config12 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config12
|
||||
elif which llvm-config13 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config13
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
fi
|
||||
fi
|
||||
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
@@ -85,9 +103,20 @@ config_linux() {
|
||||
panic "Requirement: llvm-config must be base version greater than 11"
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
|
||||
|
||||
# Creates a copy of the llvm library in the build dir, this is meant to support compiler explorer.
|
||||
# The annoyance is that this copy can be cluttering the development folder. TODO: split staging folders
|
||||
# for development and compiler explorer builds
|
||||
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||
}
|
||||
|
||||
build_odin() {
|
||||
@@ -117,6 +146,14 @@ run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
}
|
||||
|
||||
have_which() {
|
||||
if ! which which > /dev/null 2>&1; then
|
||||
panic "Could not find \`which\`"
|
||||
fi
|
||||
}
|
||||
|
||||
have_which
|
||||
|
||||
case $OS in
|
||||
Linux)
|
||||
config_linux
|
||||
|
||||
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
|
||||
if not exist "vendor\cgltf\lib\*.lib" (
|
||||
pushd vendor\cgltf\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
@@ -15,20 +15,16 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
||||
|
||||
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||
s.stream_data = rw
|
||||
s.stream_vtable = _read_writer_vtable
|
||||
s.stream_vtable = &_read_writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_read_writer_vtable := &io.Stream_VTable{
|
||||
_read_writer_vtable := io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read(b, p)
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read_byte(b)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_unread_byte(b)
|
||||
|
||||
@@ -353,14 +353,14 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
// reader_to_stream converts a Reader into an io.Stream
|
||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _reader_vtable
|
||||
s.stream_vtable = &_reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
_reader_vtable := io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data)
|
||||
reader_destroy(b)
|
||||
|
||||
@@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) {
|
||||
}
|
||||
|
||||
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
// Returns the first non-EOF error that was encountered by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, nil:
|
||||
|
||||
@@ -223,14 +223,14 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
// writer_to_stream converts a Writer into an io.Stream
|
||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _writer_vtable
|
||||
s.stream_vtable = &_writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_writer_vtable := &io.Stream_VTable{
|
||||
_writer_vtable := io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data)
|
||||
writer_destroy(b)
|
||||
|
||||
@@ -109,7 +109,7 @@ jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
|
||||
min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
|
||||
@@ -240,14 +240,11 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
|
||||
if offset < 0 || offset >= len(b.buf) {
|
||||
if uint(offset) >= len(b.buf) {
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
|
||||
if 0 <= offset && offset < len(b.buf) {
|
||||
n = copy(p, b.buf[offset:])
|
||||
}
|
||||
n = copy(p, b.buf[offset:])
|
||||
if n > 0 {
|
||||
b.last_read = .Read
|
||||
}
|
||||
@@ -374,12 +371,12 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
|
||||
|
||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _buffer_vtable
|
||||
s.stream_vtable = &_buffer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_vtable := &io.Stream_VTable{
|
||||
_buffer_vtable := io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return i64(buffer_capacity(b))
|
||||
|
||||
@@ -638,7 +638,7 @@ trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
|
||||
|
||||
index_rune :: proc(s: []byte, r: rune) -> int {
|
||||
switch {
|
||||
case 0 <= r && r < utf8.RUNE_SELF:
|
||||
case u32(r) < utf8.RUNE_SELF:
|
||||
return index_byte(s, byte(r))
|
||||
|
||||
case r == utf8.RUNE_ERROR:
|
||||
|
||||
@@ -17,7 +17,7 @@ reader_init :: proc(r: ^Reader, s: []byte) {
|
||||
|
||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = r
|
||||
s.stream_vtable = _reader_vtable
|
||||
s.stream_vtable = &_reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
_reader_vtable := io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_size(r)
|
||||
|
||||
@@ -49,8 +49,8 @@ foreign libc {
|
||||
// 7.3.8 Power and absolute-value functions
|
||||
cabs :: proc(z: complex_double) -> complex_double ---
|
||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
||||
cpow :: proc(z: complex_double) -> complex_double ---
|
||||
cpowf :: proc(z: complex_float) -> complex_float ---
|
||||
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||
csqrtf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
|
||||
+15
-2
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
when ODIN_OS == .Linux {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__libc_errno_location")
|
||||
@(link_name="__errno_location")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
|
||||
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
|
||||
fma :: proc{libc_fma, libc_fmaf}
|
||||
|
||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
||||
// a trick is used here where we use explicit procedrual overloading of one
|
||||
// a trick is used here where we use explicit procedural overloading of one
|
||||
// procedure. This is done because the foreign block is marked @(private) and
|
||||
// aliasing functions does not remove privateness from the entity.
|
||||
acosf :: proc{libc_acosf}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package libc
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:legacy_stdio_definitions.lib",
|
||||
}
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
|
||||
+27
-1
@@ -88,7 +88,6 @@ foreign libc {
|
||||
srand :: proc(seed: uint) ---
|
||||
|
||||
// 7.22.3 Memory management functions
|
||||
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
|
||||
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
||||
free :: proc(ptr: rawptr) ---
|
||||
malloc :: proc(size: size_t) -> rawptr ---
|
||||
@@ -125,3 +124,30 @@ foreign libc {
|
||||
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
||||
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
|
||||
}
|
||||
|
||||
|
||||
aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign libc {
|
||||
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||
}
|
||||
return _aligned_malloc(size=size, alignment=alignment)
|
||||
} else {
|
||||
foreign libc {
|
||||
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||
}
|
||||
return aligned_alloc(alignment=alignment, size=size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign libc {
|
||||
_aligned_free :: proc(ptr: rawptr) ---
|
||||
}
|
||||
_aligned_free(ptr)
|
||||
} else {
|
||||
free(ptr)
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ when ODIN_OS == .Linux {
|
||||
cnd_destroy :: proc(cond: ^cnd_t) ---
|
||||
cnd_init :: 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 ---
|
||||
|
||||
// 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")
|
||||
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)
|
||||
_, e := z.input->impl_read(b[:])
|
||||
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")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
@@ -321,7 +341,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
// Get current position to return to.
|
||||
cur_pos, e1 := z.input->impl_seek(0, .Current)
|
||||
if e1 != .None {
|
||||
return T{}, e1
|
||||
}
|
||||
|
||||
// Seek to offset.
|
||||
pos, e2 := z.input->impl_seek(offset, .Start)
|
||||
if e2 != .None {
|
||||
return T{}, e2
|
||||
}
|
||||
|
||||
r, e3 := io.to_reader_at(z.input)
|
||||
if !e3 {
|
||||
return T{}, .Empty
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
}
|
||||
_, e4 := io.read_at(r, b[:], pos)
|
||||
if e4 != .None {
|
||||
return T{}, .Empty
|
||||
}
|
||||
|
||||
// Return read head to original position.
|
||||
z.input->impl_seek(cur_pos, .Start)
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
|
||||
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
|
||||
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||
j := j
|
||||
queue := pq.queue[:]
|
||||
n := builtin.len(queue)
|
||||
for 0 <= j {
|
||||
i := (j-1)/2
|
||||
if i == j || !pq.less(queue[j], queue[i]) {
|
||||
|
||||
@@ -73,11 +73,18 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
@@ -92,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
|
||||
idx := q.offset%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
|
||||
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
// Push an element to the back of the queue
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||
if space(q^) == 0 {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package container_small_array
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
_ :: runtime
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
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
|
||||
}
|
||||
|
||||
cap :: proc(a: $A/Small_Array) -> int {
|
||||
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data)
|
||||
}
|
||||
|
||||
space :: proc(a: $A/Small_Array) -> int {
|
||||
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data) - a.len
|
||||
}
|
||||
|
||||
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
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
|
||||
}
|
||||
|
||||
resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, builtin.len(a.data))
|
||||
}
|
||||
|
||||
|
||||
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.data[a.len] = item
|
||||
a.len += 1
|
||||
@@ -50,7 +66,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.len += 1
|
||||
data := slice(a)
|
||||
@@ -61,14 +77,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[0]
|
||||
s := slice(a)
|
||||
@@ -77,7 +93,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
return item
|
||||
}
|
||||
|
||||
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[a.len-1]
|
||||
a.len -= 1
|
||||
@@ -86,31 +102,60 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[0]
|
||||
s := slice(a)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
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)
|
||||
}
|
||||
|
||||
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
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_elems :: 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
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
||||
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
|
||||
@@ -30,6 +30,6 @@ equivalence.
|
||||
|
||||
For the most part, alterations to the base fiat-crypto generated code was
|
||||
kept to a minimum, to aid auditability. This results in a somewhat
|
||||
ideosyncratic style, and in some cases minor performance penalties.
|
||||
idiosyncratic style, and in some cases minor performance penalties.
|
||||
|
||||
[1]: https://github.com/mit-plv/fiat-crypto
|
||||
|
||||
@@ -12,7 +12,7 @@ _rand_bytes :: proc (dst: []byte) {
|
||||
|
||||
for l > 0 {
|
||||
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 {
|
||||
switch os.Errno(-ret) {
|
||||
case os.EINTR:
|
||||
|
||||
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
|
||||
ctx.last_block = len(data) / 8 * 8
|
||||
ctx.buf = data
|
||||
i := 0
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
package debug_pe
|
||||
|
||||
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c
|
||||
PE_SIGNATURE :: u32le(0x0000_4550) // "PE\x00\x00"
|
||||
PE_SIGNATURE_STRING :: "PE\x00\x00"
|
||||
|
||||
OPTIONAL_HEADER_MAGIC :: enum u16le {
|
||||
PE32 = 0x010b,
|
||||
PE32_PLUS = 0x020b,
|
||||
}
|
||||
|
||||
Optional_Header_Base :: struct #packed {
|
||||
magic: OPTIONAL_HEADER_MAGIC,
|
||||
major_linker_version: u8,
|
||||
minor_linker_version: u8,
|
||||
size_of_code: u32le,
|
||||
size_of_initialized_data: u32le,
|
||||
size_of_uninitialized_data: u32le,
|
||||
address_of_entry_point: u32le,
|
||||
base_of_code: u32le,
|
||||
}
|
||||
|
||||
File_Header :: struct #packed {
|
||||
machine: IMAGE_FILE_MACHINE,
|
||||
number_of_sections: u16le,
|
||||
time_date_stamp: u32le,
|
||||
pointer_to_symbol_table: u32le,
|
||||
number_of_symbols: u32le,
|
||||
size_of_optional_header: u16le,
|
||||
characteristics: IMAGE_FILE_CHARACTERISTICS,
|
||||
}
|
||||
|
||||
Data_Directory :: struct #packed {
|
||||
virtual_address: u32le,
|
||||
size: u32le,
|
||||
}
|
||||
|
||||
Optional_Header32 :: struct #packed {
|
||||
using base: Optional_Header_Base,
|
||||
base_of_data: u32le,
|
||||
image_base: u32le,
|
||||
section_alignment: u32le,
|
||||
file_alignment: u32le,
|
||||
major_operating_system_version: u16le,
|
||||
minor_operating_system_version: u16le,
|
||||
major_image_version: u16le,
|
||||
minor_image_version: u16le,
|
||||
major_subsystem_version: u16le,
|
||||
minor_subsystem_version: u16le,
|
||||
win32_version_value: u32le,
|
||||
size_of_image: u32le,
|
||||
size_of_headers: u32le,
|
||||
check_sum: u32le,
|
||||
subsystem: IMAGE_SUBSYSTEM,
|
||||
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||
size_of_stack_reserve: u32le,
|
||||
size_of_stack_commit: u32le,
|
||||
size_of_heap_reserve: u32le,
|
||||
size_of_heap_commit: u32le,
|
||||
loader_flags: u32le,
|
||||
number_of_rva_and_sizes: u32le,
|
||||
data_directory: [16]Data_Directory,
|
||||
}
|
||||
|
||||
Optional_Header64 :: struct #packed {
|
||||
using base: Optional_Header_Base,
|
||||
image_base: u64le,
|
||||
section_alignment: u32le,
|
||||
file_alignment: u32le,
|
||||
major_operating_system_version: u16le,
|
||||
minor_operating_system_version: u16le,
|
||||
major_image_version: u16le,
|
||||
minor_image_version: u16le,
|
||||
major_subsystem_version: u16le,
|
||||
minor_subsystem_version: u16le,
|
||||
win32_version_value: u32le,
|
||||
size_of_image: u32le,
|
||||
size_of_headers: u32le,
|
||||
check_sum: u32le,
|
||||
subsystem: IMAGE_SUBSYSTEM,
|
||||
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||
size_of_stack_reserve: u64le,
|
||||
size_of_stack_commit: u64le,
|
||||
size_of_heap_reserve: u64le,
|
||||
size_of_heap_commit: u64le,
|
||||
loader_flags: u32le,
|
||||
number_of_rva_and_sizes: u32le,
|
||||
data_directory: [16]Data_Directory,
|
||||
}
|
||||
|
||||
// .debug section
|
||||
Debug_Directory_Entry :: struct {
|
||||
characteristics: u32le,
|
||||
time_date_stamp: u32le,
|
||||
major_version: u16le,
|
||||
minor_version: u16le,
|
||||
type: IMAGE_DEBUG_TYPE,
|
||||
size_of_data: u32le,
|
||||
address_of_raw_data: u32le,
|
||||
pointer_to_raw_data: u32le,
|
||||
}
|
||||
|
||||
|
||||
IMAGE_FILE_MACHINE :: enum u16le {
|
||||
UNKNOWN = 0x0,
|
||||
AM33 = 0x1d3,
|
||||
AMD64 = 0x8664,
|
||||
ARM = 0x1c0,
|
||||
ARMNT = 0x1c4,
|
||||
ARM64 = 0xaa64,
|
||||
EBC = 0xebc,
|
||||
I386 = 0x14c,
|
||||
IA64 = 0x200,
|
||||
LOONGARCH32 = 0x6232,
|
||||
LOONGARCH64 = 0x6264,
|
||||
M32R = 0x9041,
|
||||
MIPS16 = 0x266,
|
||||
MIPSFPU = 0x366,
|
||||
MIPSFPU16 = 0x466,
|
||||
POWERPC = 0x1f0,
|
||||
POWERPCFP = 0x1f1,
|
||||
R4000 = 0x166,
|
||||
SH3 = 0x1a2,
|
||||
SH3DSP = 0x1a3,
|
||||
SH4 = 0x1a6,
|
||||
SH5 = 0x1a8,
|
||||
THUMB = 0x1c2,
|
||||
WCEMIPSV2 = 0x169,
|
||||
}
|
||||
|
||||
// IMAGE_DIRECTORY_ENTRY constants
|
||||
IMAGE_DIRECTORY_ENTRY :: enum u8 {
|
||||
EXPORT = 0,
|
||||
IMPORT = 1,
|
||||
RESOURCE = 2,
|
||||
EXCEPTION = 3,
|
||||
SECURITY = 4,
|
||||
BASERELOC = 5,
|
||||
DEBUG = 6,
|
||||
ARCHITECTURE = 7, // reserved
|
||||
GLOBALPTR = 8,
|
||||
TLS = 9,
|
||||
LOAD_CONFIG = 10,
|
||||
BOUND_IMPORT = 11,
|
||||
IAT = 12,
|
||||
DELAY_IMPORT = 13,
|
||||
COM_DESCRIPTOR = 14, // DLR Runtime headers
|
||||
_RESERVED = 15,
|
||||
}
|
||||
#assert(len(IMAGE_DIRECTORY_ENTRY) == 16)
|
||||
|
||||
|
||||
IMAGE_FILE_CHARACTERISTICS :: distinct bit_set[IMAGE_FILE_CHARACTERISTIC; u16le]
|
||||
IMAGE_FILE_CHARACTERISTIC :: enum u16le {
|
||||
RELOCS_STRIPPED = 0,
|
||||
EXECUTABLE_IMAGE = 1,
|
||||
LINE_NUMS_STRIPPED = 2,
|
||||
LOCAL_SYMS_STRIPPED = 3,
|
||||
AGGRESIVE_WS_TRIM = 4,
|
||||
LARGE_ADDRESS_AWARE = 5,
|
||||
|
||||
BYTES_REVERSED_LO = 7,
|
||||
MACHINE_32BIT = 8, // IMAGE_FILE_32BIT_MACHINE originally
|
||||
DEBUG_STRIPPED = 9,
|
||||
REMOVABLE_RUN_FROM_SWAP = 10,
|
||||
NET_RUN_FROM_SWAP = 11,
|
||||
SYSTEM = 12,
|
||||
DLL = 13,
|
||||
UP_SYSTEM_ONLY = 14,
|
||||
BYTES_REVERSED_HI = 15,
|
||||
}
|
||||
|
||||
IMAGE_SUBSYSTEM :: enum u16le {
|
||||
UNKNOWN = 0,
|
||||
NATIVE = 1,
|
||||
WINDOWS_GUI = 2,
|
||||
WINDOWS_CUI = 3,
|
||||
OS2_CUI = 5,
|
||||
POSIX_CUI = 7,
|
||||
NATIVE_WINDOWS = 8,
|
||||
WINDOWS_CE_GUI = 9,
|
||||
EFI_APPLICATION = 10,
|
||||
EFI_BOOT_SERVICE_DRIVER = 11,
|
||||
EFI_RUNTIME_DRIVER = 12,
|
||||
EFI_ROM = 13,
|
||||
XBOX = 14,
|
||||
WINDOWS_BOOT_APPLICATION = 16,
|
||||
}
|
||||
|
||||
IMAGE_DLLCHARACTERISTICS :: distinct bit_set[IMAGE_DLLCHARACTERISTIC; u16le]
|
||||
IMAGE_DLLCHARACTERISTIC :: enum u16le {
|
||||
HIGH_ENTROPY_VA = 5,
|
||||
DYNAMIC_BASE = 6,
|
||||
FORCE_INTEGRITY = 7,
|
||||
NX_COMPAT = 8,
|
||||
NO_ISOLATION = 9,
|
||||
NO_SEH = 10,
|
||||
NO_BIND = 11,
|
||||
APPCONTAINER = 12,
|
||||
WDM_DRIVER = 13,
|
||||
GUARD_CF = 14,
|
||||
TERMINAL_SERVER_AWARE = 15,
|
||||
}
|
||||
|
||||
IMAGE_DEBUG_TYPE :: enum u32le {
|
||||
UNKNOWN = 0, // An unknown value that is ignored by all tools.
|
||||
COFF = 1, // The COFF debug information (line numbers, symbol table, and string table). This type of debug information is also pointed to by fields in the file headers.
|
||||
CODEVIEW = 2, // The Visual C++ debug information.
|
||||
FPO = 3, // The frame pointer omission (FPO) information. This information tells the debugger how to interpret nonstandard stack frames, which use the EBP register for a purpose other than as a frame pointer.
|
||||
MISC = 4, // The location of DBG file.
|
||||
EXCEPTION = 5, // A copy of .pdata section.
|
||||
FIXUP = 6, // Reserved.
|
||||
OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image.
|
||||
OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image.
|
||||
BORLAND = 9, // Reserved for Borland.
|
||||
RESERVED10 = 10, // Reserved.
|
||||
CLSID = 11, // Reserved.
|
||||
REPRO = 16, // PE determinism or reproducibility.
|
||||
EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package debug_pe
|
||||
|
||||
import "core:runtime"
|
||||
import "core:io"
|
||||
|
||||
Section_Header32 :: struct {
|
||||
name: [8]u8,
|
||||
virtual_size: u32le,
|
||||
virtual_address: u32le,
|
||||
size_of_raw_data: u32le,
|
||||
pointer_to_raw_data: u32le,
|
||||
pointer_to_relocations: u32le,
|
||||
pointer_to_line_numbers: u32le,
|
||||
number_of_relocations: u16le,
|
||||
number_of_line_numbers: u16le,
|
||||
characteristics: IMAGE_SCN_CHARACTERISTICS,
|
||||
}
|
||||
|
||||
Reloc :: struct {
|
||||
virtual_address: u32le,
|
||||
symbol_table_index: u32le,
|
||||
type: IMAGE_REL,
|
||||
}
|
||||
|
||||
IMAGE_SCN_CHARACTERISTICS :: enum u32le {
|
||||
TYPE_NO_PAD = 0x00000008, // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. = 0x00000010, // Reserved for future use.
|
||||
CNT_CODE = 0x00000020, // The section contains executable code.
|
||||
CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data.
|
||||
CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data.
|
||||
LNK_OTHER = 0x00000100, // Reserved for future use.
|
||||
LNK_INFO = 0x00000200, // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. = 0x00000400, // Reserved for future use.
|
||||
LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files.
|
||||
LNK_COMDAT = 0x00001000, // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files.
|
||||
GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP).
|
||||
MEM_PURGEABLE = 0x00020000, // Reserved for future use.
|
||||
MEM_16BIT = 0x00020000, // Reserved for future use.
|
||||
MEM_LOCKED = 0x00040000, // Reserved for future use.
|
||||
MEM_PRELOAD = 0x00080000, // Reserved for future use.
|
||||
ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. Valid only for object files.
|
||||
ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. Valid only for object files.
|
||||
ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. Valid only for object files.
|
||||
ALIGN_8BYTES = 0x00400000, // Align data on an 8-byte boundary. Valid only for object files.
|
||||
ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. Valid only for object files.
|
||||
ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. Valid only for object files.
|
||||
ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. Valid only for object files.
|
||||
ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. Valid only for object files.
|
||||
ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. Valid only for object files.
|
||||
ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. Valid only for object files.
|
||||
ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. Valid only for object files.
|
||||
ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. Valid only for object files.
|
||||
ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. Valid only for object files.
|
||||
ALIGN_8192BYTES = 0x00E00000, // Align data on an 8192-byte boundary. Valid only for object files.
|
||||
LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations.
|
||||
MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed.
|
||||
MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
|
||||
MEM_NOT_PAGED = 0x08000000, // The section is not pageable.
|
||||
MEM_SHARED = 0x10000000, // The section can be shared in memory.
|
||||
MEM_EXECUTE = 0x20000000, // The section can be executed as code.
|
||||
MEM_READ = 0x40000000, // The section can be read.
|
||||
MEM_WRITE = 0x80000000, // The section can be written to.
|
||||
}
|
||||
|
||||
|
||||
IMAGE_REL :: enum u16le {
|
||||
I386_ABSOLUTE = 0x0000,
|
||||
I386_DIR16 = 0x0001,
|
||||
I386_REL16 = 0x0002,
|
||||
I386_DIR32 = 0x0006,
|
||||
I386_DIR32NB = 0x0007,
|
||||
I386_SEG12 = 0x0009,
|
||||
I386_SECTION = 0x000A,
|
||||
I386_SECREL = 0x000B,
|
||||
I386_TOKEN = 0x000C,
|
||||
I386_SECREL7 = 0x000D,
|
||||
I386_REL32 = 0x0014,
|
||||
|
||||
AMD64_ABSOLUTE = 0x0000,
|
||||
AMD64_ADDR64 = 0x0001,
|
||||
AMD64_ADDR32 = 0x0002,
|
||||
AMD64_ADDR32NB = 0x0003,
|
||||
AMD64_REL32 = 0x0004,
|
||||
AMD64_REL32_1 = 0x0005,
|
||||
AMD64_REL32_2 = 0x0006,
|
||||
AMD64_REL32_3 = 0x0007,
|
||||
AMD64_REL32_4 = 0x0008,
|
||||
AMD64_REL32_5 = 0x0009,
|
||||
AMD64_SECTION = 0x000A,
|
||||
AMD64_SECREL = 0x000B,
|
||||
AMD64_SECREL7 = 0x000C,
|
||||
AMD64_TOKEN = 0x000D,
|
||||
AMD64_SREL32 = 0x000E,
|
||||
AMD64_PAIR = 0x000F,
|
||||
AMD64_SSPAN32 = 0x0010,
|
||||
|
||||
ARM_ABSOLUTE = 0x0000,
|
||||
ARM_ADDR32 = 0x0001,
|
||||
ARM_ADDR32NB = 0x0002,
|
||||
ARM_BRANCH24 = 0x0003,
|
||||
ARM_BRANCH11 = 0x0004,
|
||||
ARM_SECTION = 0x000E,
|
||||
ARM_SECREL = 0x000F,
|
||||
ARM_MOV32 = 0x0010,
|
||||
|
||||
THUMB_MOV32 = 0x0011,
|
||||
THUMB_BRANCH20 = 0x0012,
|
||||
THUMB_BRANCH24 = 0x0014,
|
||||
THUMB_BLX23 = 0x0015,
|
||||
|
||||
ARM_PAIR = 0x0016,
|
||||
|
||||
ARM64_ABSOLUTE = 0x0000,
|
||||
ARM64_ADDR32 = 0x0001,
|
||||
ARM64_ADDR32NB = 0x0002,
|
||||
ARM64_BRANCH26 = 0x0003,
|
||||
ARM64_PAGEBASE_REL21 = 0x0004,
|
||||
ARM64_REL21 = 0x0005,
|
||||
ARM64_PAGEOFFSET_12A = 0x0006,
|
||||
ARM64_PAGEOFFSET_12L = 0x0007,
|
||||
ARM64_SECREL = 0x0008,
|
||||
ARM64_SECREL_LOW12A = 0x0009,
|
||||
ARM64_SECREL_HIGH12A = 0x000A,
|
||||
ARM64_SECREL_LOW12L = 0x000B,
|
||||
ARM64_TOKEN = 0x000C,
|
||||
ARM64_SECTION = 0x000D,
|
||||
ARM64_ADDR64 = 0x000E,
|
||||
ARM64_BRANCH19 = 0x000F,
|
||||
ARM64_BRANCH14 = 0x0010,
|
||||
ARM64_REL32 = 0x0011,
|
||||
}
|
||||
|
||||
PE_CODE_VIEW_SIGNATURE_RSDS :: u32le(0x5344_5352)
|
||||
@@ -0,0 +1,108 @@
|
||||
package debug_pe
|
||||
|
||||
COFF_SYMBOL_SIZE :: 18
|
||||
|
||||
COFF_Symbol :: struct {
|
||||
name: [8]u8,
|
||||
value: u32le,
|
||||
section_number: i16le,
|
||||
type: IMAGE_SYM_TYPE,
|
||||
storage_class: IMAGE_SYM_CLASS,
|
||||
number_of_aux_symbols: u8,
|
||||
}
|
||||
|
||||
// COFF_Symbol_Aux_Format5 describes the expected form of an aux symbol
|
||||
// attached to a section definition symbol. The PE format defines a
|
||||
// number of different aux symbol formats: format 1 for function
|
||||
// definitions, format 2 for .be and .ef symbols, and so on. Format 5
|
||||
// holds extra info associated with a section definition, including
|
||||
// number of relocations + line numbers, as well as COMDAT info. See
|
||||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
|
||||
// for more on what's going on here.
|
||||
COFF_Symbol_Aux_Format5 :: struct {
|
||||
size: u32le,
|
||||
num_relocs: u16le,
|
||||
num_line_numbers: u16le,
|
||||
checksum: u32le,
|
||||
sec_num: u16le,
|
||||
selection: IMAGE_COMDAT_SELECT,
|
||||
_: [3]u8, // padding
|
||||
}
|
||||
|
||||
IMAGE_COMDAT_SELECT :: enum u8 {
|
||||
NODUPLICATES = 1,
|
||||
ANY = 2,
|
||||
SAME_SIZE = 3,
|
||||
EXACT_MATCH = 4,
|
||||
ASSOCIATIVE = 5,
|
||||
LARGEST = 6,
|
||||
}
|
||||
|
||||
|
||||
// The symbol record is not yet assigned a section. A value of zero indicates
|
||||
// that a reference to an external symbol is defined elsewhere. A value of
|
||||
// non-zero is a common symbol with a size that is specified by the value.
|
||||
IMAGE_SYM_UNDEFINED :: 0
|
||||
// The symbol has an absolute (non-relocatable) value and is not an address.
|
||||
IMAGE_SYM_ABSOLUTE :: -1
|
||||
// The symbol provides general type or debugging information but does not
|
||||
// correspond to a section. Microsoft tools use this setting along
|
||||
// with .file records (storage class FILE).
|
||||
IMAGE_SYM_DEBUG :: -2
|
||||
|
||||
IMAGE_SYM_TYPE :: enum u16le {
|
||||
NULL = 0,
|
||||
VOID = 1,
|
||||
CHAR = 2,
|
||||
SHORT = 3,
|
||||
INT = 4,
|
||||
LONG = 5,
|
||||
FLOAT = 6,
|
||||
DOUBLE = 7,
|
||||
STRUCT = 8,
|
||||
UNION = 9,
|
||||
ENUM = 10,
|
||||
MOE = 11,
|
||||
BYTE = 12,
|
||||
WORD = 13,
|
||||
UINT = 14,
|
||||
DWORD = 15,
|
||||
PCODE = 32768,
|
||||
|
||||
DTYPE_NULL = 0,
|
||||
DTYPE_POINTER = 0x10,
|
||||
DTYPE_FUNCTION = 0x20,
|
||||
DTYPE_ARRAY = 0x30,
|
||||
}
|
||||
|
||||
IMAGE_SYM_CLASS :: enum u8 {
|
||||
NULL = 0,
|
||||
AUTOMATIC = 1,
|
||||
EXTERNAL = 2,
|
||||
STATIC = 3,
|
||||
REGISTER = 4,
|
||||
EXTERNAL_DEF = 5,
|
||||
LABEL = 6,
|
||||
UNDEFINED_LABEL = 7,
|
||||
MEMBER_OF_STRUCT = 8,
|
||||
ARGUMENT = 9,
|
||||
STRUCT_TAG = 10,
|
||||
MEMBER_OF_UNION = 11,
|
||||
UNION_TAG = 12,
|
||||
TYPE_DEFINITION = 13,
|
||||
UNDEFINED_STATIC = 14,
|
||||
ENUM_TAG = 15,
|
||||
MEMBER_OF_ENUM = 16,
|
||||
REGISTER_PARAM = 17,
|
||||
BIT_FIELD = 18,
|
||||
FAR_EXTERNAL = 68, // Not in PECOFF v8 spec
|
||||
BLOCK = 100,
|
||||
FUNCTION = 101,
|
||||
END_OF_STRUCT = 102,
|
||||
FILE = 103,
|
||||
SECTION = 104,
|
||||
WEAK_EXTERNAL = 105,
|
||||
CLR_TOKEN = 107,
|
||||
|
||||
END_OF_FUNCTION = 255,
|
||||
}
|
||||
@@ -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
|
||||
|
||||
/*
|
||||
A handle to a dynamically loaded library.
|
||||
*/
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
/*
|
||||
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 {
|
||||
return _symbol_address(library, symbol)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ package dynlib
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
|
||||
_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
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
found = ptr != nil
|
||||
|
||||
@@ -107,7 +107,7 @@ Node :: struct {
|
||||
/* Conventions */
|
||||
/* ------------
|
||||
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
|
||||
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
|
||||
*/
|
||||
|
||||
|
||||
+232
-51
@@ -5,6 +5,7 @@ import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
import "core:io"
|
||||
|
||||
Marshal_Data_Error :: enum {
|
||||
@@ -17,25 +18,54 @@ Marshal_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
||||
Marshal_Options :: struct {
|
||||
// output based on spec
|
||||
spec: Specification,
|
||||
|
||||
// use line breaks & tab|spaces
|
||||
pretty: bool,
|
||||
|
||||
// spacing
|
||||
use_spaces: bool,
|
||||
spaces: int,
|
||||
|
||||
// state
|
||||
indentation: int,
|
||||
|
||||
// option to output uint in JSON5 & MJSON
|
||||
write_uint_as_hex: bool,
|
||||
|
||||
// mjson output options
|
||||
mjson_keys_use_quotes: bool,
|
||||
mjson_keys_use_equal_sign: bool,
|
||||
|
||||
// mjson state
|
||||
mjson_skipped_first_braces_start: bool,
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator)
|
||||
defer if err != nil {
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
marshal_to_builder(&b, v) or_return
|
||||
opt := opt
|
||||
marshal_to_builder(&b, v, &opt) or_return
|
||||
|
||||
if len(b.buf) != 0 {
|
||||
data = b.buf[:]
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v)
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v, opt)
|
||||
}
|
||||
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
if v == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return
|
||||
@@ -56,6 +86,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case i16: u = u128(i)
|
||||
case i32: u = u128(i)
|
||||
case i64: u = u128(i)
|
||||
case i128: u = u128(i)
|
||||
case int: u = u128(i)
|
||||
case u8: u = u128(i)
|
||||
case u16: u = u128(i)
|
||||
@@ -82,7 +113,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case u128be: u = u128(i)
|
||||
}
|
||||
|
||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s: string
|
||||
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
|
||||
|
||||
@@ -147,10 +192,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Procedure:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Tuple:
|
||||
case runtime.Type_Info_Parameters:
|
||||
return .Unsupported_Type
|
||||
|
||||
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
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
for i in 0..<array.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
io.write_byte(w, '{') or_return
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return .Unsupported_Type
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
i := 0
|
||||
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(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||
|
||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
case: return .Unsupported_Type
|
||||
}
|
||||
}
|
||||
|
||||
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||
}
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
io.write_byte(w, '{') or_return
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
for name, i in info.names {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
opt_write_key(w, opt, name) or_return
|
||||
}
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
marshal_to_writer(w, any{data, id}) or_return
|
||||
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||
@@ -270,11 +338,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
io.write_string(w, "null") or_return
|
||||
} else {
|
||||
id := info.variants[tag-1].id
|
||||
return marshal_to_writer(w, any{v.data, id})
|
||||
return marshal_to_writer(w, any{v.data, id}, opt)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_to_writer(w, any{v.data, info.base.id})
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
@@ -330,3 +398,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// write key as quoted string or with optional quotes in mjson
|
||||
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
|
||||
case .MJSON:
|
||||
if opt.mjson_keys_use_quotes {
|
||||
io.write_quoted_string(w, name) or_return
|
||||
} else {
|
||||
io.write_string(w, name) or_return
|
||||
}
|
||||
|
||||
if opt.mjson_keys_use_equal_sign {
|
||||
io.write_string(w, " = ") or_return
|
||||
} else {
|
||||
io.write_string(w, ": ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert start byte and increase indentation on pretty
|
||||
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
// skip mjson starting braces
|
||||
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
||||
opt.mjson_skipped_first_braces_start = true
|
||||
return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
opt.indentation += 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert comma seperation and write indentations
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
if iteration > 0 {
|
||||
io.write_string(w, ", ") or_return
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
|
||||
case .MJSON:
|
||||
if iteration > 0 {
|
||||
// on pretty no commas necessary
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
} else {
|
||||
// comma seperation necessary for non pretty output!
|
||||
io.write_string(w, ", ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// decrease indent, write spacing and insert end byte
|
||||
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
||||
if opt.indentation == 0 {
|
||||
opt.mjson_skipped_first_braces_end = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt.indentation -= 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
return
|
||||
}
|
||||
|
||||
// writes current indentation level based on options
|
||||
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
|
||||
if !opt.pretty {
|
||||
return
|
||||
}
|
||||
|
||||
if opt.use_spaces {
|
||||
spaces := opt.spaces == 0 ? 4 : opt.spaces
|
||||
for _ in 0..<opt.indentation * spaces {
|
||||
io.write_byte(w, ' ') or_return
|
||||
}
|
||||
} else {
|
||||
for _ in 0..<opt.indentation {
|
||||
io.write_byte(w, '\t') or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -346,6 +346,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
|
||||
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)
|
||||
|
||||
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}
|
||||
unmarshal_value(p, field) or_return
|
||||
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
}
|
||||
continue struct_loop
|
||||
} else {
|
||||
// allows skipping unused struct fields
|
||||
parse_value(p) or_return
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
}
|
||||
continue struct_loop
|
||||
}
|
||||
|
||||
return Unsupported_Type_Error{v.id, p.curr_token}
|
||||
}
|
||||
|
||||
case reflect.Type_Info_Map:
|
||||
@@ -394,12 +401,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
raw_map := (^mem.Raw_Map)(v.data)
|
||||
if raw_map.entries.allocator.procedure == nil {
|
||||
raw_map.entries.allocator = p.allocator
|
||||
if raw_map.allocator.procedure == nil {
|
||||
raw_map.allocator = p.allocator
|
||||
}
|
||||
|
||||
header := runtime.__get_map_header_runtime(raw_map, t)
|
||||
|
||||
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||
defer delete(elem_backing, p.allocator)
|
||||
|
||||
@@ -415,19 +420,16 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
delete(key, p.allocator)
|
||||
return err
|
||||
}
|
||||
|
||||
hash := runtime.Map_Hash {
|
||||
hash = runtime.default_hasher_string(&key, 0),
|
||||
key_ptr = &key,
|
||||
}
|
||||
|
||||
|
||||
key_ptr := rawptr(&key)
|
||||
|
||||
key_cstr: cstring
|
||||
if reflect.is_cstring(t.key) {
|
||||
key_cstr = cstring(raw_data(key))
|
||||
hash.key_ptr = &key_cstr
|
||||
key_ptr = &key_cstr
|
||||
}
|
||||
|
||||
set_ptr := runtime.__dynamic_map_set(header, hash, map_backing_value.data)
|
||||
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||
if set_ptr == nil {
|
||||
delete(key, p.allocator)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import "core:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
|
||||
likely :: intrinsics.expect
|
||||
|
||||
@@ -408,7 +409,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
next := scan(t)
|
||||
#partial switch next.kind {
|
||||
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
|
||||
} else if len(doc.prologue) > 0 {
|
||||
/*
|
||||
@@ -614,6 +615,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
||||
}
|
||||
|
||||
case "encoding":
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
switch strings.to_lower(attr.val, context.temp_allocator) {
|
||||
case "utf-8", "utf8":
|
||||
doc.encoding = .UTF_8
|
||||
|
||||
+72
-91
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
p("Panic", message, loc)
|
||||
}
|
||||
|
||||
// formatted printing for cstrings
|
||||
caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// c string with temp allocator
|
||||
ctprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// sbprint formats using the default print settings and writes to buf
|
||||
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
|
||||
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
|
||||
was_prev_index := false
|
||||
|
||||
loop: for i := 0; i < end; /**/ {
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
|
||||
|
||||
prev_i := i
|
||||
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
||||
@@ -529,7 +547,7 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
|
||||
is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' }
|
||||
|
||||
new_offset = offset
|
||||
for new_offset <= len(s) {
|
||||
for new_offset < len(s) {
|
||||
c := s[new_offset]
|
||||
if !is_digit(c) {
|
||||
break
|
||||
@@ -660,7 +678,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
}
|
||||
} else if fi.zero && fi.width_set {
|
||||
prec = fi.width
|
||||
if neg || fi.plus || fi.space {
|
||||
if neg || fi.plus {
|
||||
// There needs to be space for the "sign"
|
||||
prec -= 1
|
||||
}
|
||||
@@ -679,7 +697,6 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.space { flags |= {.Space} }
|
||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
@@ -726,7 +743,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
}
|
||||
} else if fi.zero && fi.width_set {
|
||||
prec = fi.width
|
||||
if neg || fi.plus || fi.space {
|
||||
if neg || fi.plus {
|
||||
// There needs to be space for the "sign"
|
||||
prec -= 1
|
||||
}
|
||||
@@ -745,7 +762,6 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.space { flags |= {.Space} }
|
||||
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
@@ -849,79 +865,30 @@ _pad :: proc(fi: ^Info, s: string) {
|
||||
}
|
||||
}
|
||||
|
||||
_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
|
||||
prec := fi.prec if fi.prec_set else 3
|
||||
buf: [386]byte
|
||||
|
||||
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
|
||||
str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
if str[0] == '+' && str[1] != 'I' {
|
||||
str = str[1:]
|
||||
}
|
||||
}
|
||||
|
||||
_pad(fi, str)
|
||||
}
|
||||
|
||||
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'f', 'F', 'g', 'G', 'v':
|
||||
prec: int = 3
|
||||
if fi.prec_set {
|
||||
prec = fi.prec
|
||||
}
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'f', prec, bit_size)
|
||||
b := buf[:len(str)+1]
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:]
|
||||
} else {
|
||||
b[0] = '+'
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' '
|
||||
}
|
||||
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
io.write_string(fi.writer, string(b), &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.plus || b[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
io.write_byte(fi.writer, b[0], &fi.n)
|
||||
fmt_write_padding(fi, fi.width - len(b))
|
||||
io.write_string(fi.writer, string(b[1:]), &fi.n)
|
||||
} else {
|
||||
_pad(fi, string(b))
|
||||
}
|
||||
} else {
|
||||
_pad(fi, string(b[1:]))
|
||||
}
|
||||
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'f')
|
||||
case 'e', 'E':
|
||||
prec: int = 3
|
||||
if fi.prec_set {
|
||||
prec = fi.prec
|
||||
}
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size)
|
||||
b := buf[:len(str)+1]
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:]
|
||||
} else {
|
||||
b[0] = '+'
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' '
|
||||
}
|
||||
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
io.write_string(fi.writer, string(b), &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.plus || str[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
io.write_byte(fi.writer, b[0], &fi.n)
|
||||
fmt_write_padding(fi, fi.width - len(b))
|
||||
io.write_string(fi.writer, string(b[1:]), &fi.n)
|
||||
} else {
|
||||
_pad(fi, string(b))
|
||||
}
|
||||
} else {
|
||||
_pad(fi, string(b[1:]))
|
||||
}
|
||||
// BUG(): "%.3e" returns "3.000e+00"
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'e')
|
||||
|
||||
case 'h', 'H':
|
||||
prev_fi := fi^
|
||||
@@ -975,7 +942,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
io.write_string(fi.writer, s[:fi.width], &fi.n)
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1031,6 +998,15 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
|
||||
io.write_string(fi.writer, "#soa{data=0x", &fi.n)
|
||||
_fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
io.write_string(fi.writer, ", index=", &fi.n)
|
||||
_fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
io.write_string(fi.writer, "}", &fi.n)
|
||||
}
|
||||
|
||||
|
||||
enum_value_to_string :: proc(val: any) -> (string, bool) {
|
||||
v := val
|
||||
v.id = runtime.typeid_base(v.id)
|
||||
@@ -1807,8 +1783,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
type_info := type_info_of(v.id)
|
||||
switch info in type_info.variant {
|
||||
case runtime.Type_Info_Any: // Ignore
|
||||
case runtime.Type_Info_Tuple: // Ignore
|
||||
case runtime.Type_Info_Any: // Ignore
|
||||
case runtime.Type_Info_Parameters: // Ignore
|
||||
|
||||
case runtime.Type_Info_Named:
|
||||
fmt_named(fi, v, verb, info)
|
||||
@@ -1867,6 +1843,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fmt_pointer(fi, ptr, verb)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
ptr := (^runtime.Raw_Soa_Pointer)(v.data)^
|
||||
fmt_soa_pointer(fi, ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
ptr := (^rawptr)(v.data)^
|
||||
if ptr == nil {
|
||||
@@ -2038,26 +2018,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
if j > 0 {
|
||||
io.write_string(fi.writer, ", ", &fi.n)
|
||||
}
|
||||
j += 1
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
key := data + entry_type.offsets[2]
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
|
||||
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
|
||||
value := data + entry_type.offsets[3]
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ foreign odin_env {
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
write_vtable := &io.Stream_VTable{
|
||||
write_vtable := io.Stream_VTable{
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := u32(uintptr(s.stream_data))
|
||||
write(fd, p)
|
||||
@@ -22,14 +22,14 @@ write_vtable := &io.Stream_VTable{
|
||||
@(private="file")
|
||||
stdout := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_vtable = &write_vtable,
|
||||
stream_data = rawptr(uintptr(1)),
|
||||
},
|
||||
}
|
||||
@(private="file")
|
||||
stderr := io.Writer{
|
||||
stream = {
|
||||
stream_vtable = write_vtable,
|
||||
stream_vtable = &write_vtable,
|
||||
stream_data = rawptr(uintptr(2)),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
// fprintf formats according to the specififed format string and writes to fd
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
// println formats using the default print settings and writes to os.stdout
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
// printf formats according to the specififed format string and writes to os.stdout
|
||||
// printf formats according to the specified format string and writes to os.stdout
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
// eprint formats using the default print settings and writes to os.stderr
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintln formats using the default print settings and writes to os.stderr
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintf formats according to the specififed format string and writes to os.stderr
|
||||
// eprintf formats according to the specified format string and writes to os.stderr
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
+7
-3
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
return
|
||||
}
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
return h
|
||||
}
|
||||
|
||||
fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
|
||||
@@ -118,7 +118,7 @@ XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_
|
||||
}
|
||||
|
||||
@(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)
|
||||
}
|
||||
|
||||
|
||||
+65
-7
@@ -46,7 +46,7 @@ Image :: struct {
|
||||
height: int,
|
||||
channels: int,
|
||||
depth: int, // Channel depth in bits, typically 8 or 16
|
||||
pixels: bytes.Buffer,
|
||||
pixels: bytes.Buffer `fmt:"-"`,
|
||||
/*
|
||||
Some image loaders/writers can return/take an optional background color.
|
||||
For convenience, we return them as u16 so we don't need to switch on the type
|
||||
@@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil {
|
||||
^Netpbm_Info,
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
^TGA_Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
|
||||
|
||||
General_Image_Error :: enum {
|
||||
None = 0,
|
||||
Unsupported_Option,
|
||||
// File I/O
|
||||
Unable_To_Read_File,
|
||||
Unable_To_Write_File,
|
||||
@@ -376,10 +378,20 @@ QOI_Info :: struct {
|
||||
header: QOI_Header,
|
||||
}
|
||||
|
||||
TGA_Data_Type :: enum u8 {
|
||||
No_Image_Data = 0,
|
||||
Uncompressed_Color_Mapped = 1,
|
||||
Uncompressed_RGB = 2,
|
||||
Uncompressed_Black_White = 3,
|
||||
Compressed_Color_Mapped = 9,
|
||||
Compressed_RGB = 10,
|
||||
Compressed_Black_White = 11,
|
||||
}
|
||||
|
||||
TGA_Header :: struct #packed {
|
||||
id_length: u8,
|
||||
color_map_type: u8,
|
||||
data_type_code: u8,
|
||||
data_type_code: TGA_Data_Type,
|
||||
color_map_origin: u16le,
|
||||
color_map_length: u16le,
|
||||
color_map_depth: u8,
|
||||
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
|
||||
}
|
||||
#assert(size_of(TGA_Header) == 18)
|
||||
|
||||
New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
|
||||
|
||||
TGA_Footer :: struct #packed {
|
||||
extension_area_offset: u32le,
|
||||
developer_directory_offset: u32le,
|
||||
signature: [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
|
||||
}
|
||||
#assert(size_of(TGA_Footer) == 26)
|
||||
|
||||
TGA_Extension :: struct #packed {
|
||||
extension_size: u16le, // Size of this struct. If not 495 bytes it means it's an unsupported version.
|
||||
author_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
author_comments: [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
|
||||
datetime: struct {month, day, year, hour, minute, second: u16le},
|
||||
job_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
job_time: struct {hour, minute, second: u16le},
|
||||
software_id: [41]u8 `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
|
||||
software_version: struct #packed {
|
||||
number: u16le, // Version number * 100
|
||||
letter: u8 `fmt:"r"`, // " " if not used
|
||||
},
|
||||
key_color: [4]u8, // ARGB key color used at time of production
|
||||
aspect_ratio: [2]u16le, // Numerator / Denominator
|
||||
gamma: [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
|
||||
color_correction_offset: u32le, // 0 if no color correction information
|
||||
postage_stamp_offset: u32le, // 0 if no thumbnail
|
||||
scanline_offset: u32le, // 0 if no scanline table
|
||||
attributes: TGA_Alpha_Kind,
|
||||
}
|
||||
#assert(size_of(TGA_Extension) == 495)
|
||||
|
||||
TGA_Alpha_Kind :: enum u8 {
|
||||
None,
|
||||
Undefined_Ignore,
|
||||
Undefined_Retain,
|
||||
Useful,
|
||||
Premultiplied,
|
||||
}
|
||||
|
||||
TGA_Info :: struct {
|
||||
header: TGA_Header,
|
||||
image_id: string,
|
||||
footer: Maybe(TGA_Footer),
|
||||
extension: Maybe(TGA_Extension),
|
||||
}
|
||||
|
||||
// Function to help with image buffer calculations
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||
@@ -469,7 +527,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
|
||||
}
|
||||
|
||||
// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -489,7 +547,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -498,7 +556,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -518,7 +576,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -527,7 +585,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 1..4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import "core:os"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:unicode"
|
||||
import "core:runtime"
|
||||
|
||||
Image :: image.Image
|
||||
Format :: image.Netpbm_Format
|
||||
@@ -407,6 +408,8 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
|
||||
}
|
||||
length = header_end_index + len(HEADER_END)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
// string buffer for the tupltype
|
||||
tupltype: strings.Builder
|
||||
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package png
|
||||
|
||||
import "core:image"
|
||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
defer close(fd)
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||
)
|
||||
|
||||
if channels == 3 {
|
||||
|
||||
+17
-14
@@ -16,6 +16,7 @@ import coretime "core:time"
|
||||
import "core:strings"
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
/*
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
|
||||
assert(len(c.data) == int(c.header.length))
|
||||
#partial switch c.header.type {
|
||||
case .tEXt:
|
||||
@@ -194,18 +197,18 @@ text_destroy :: proc(text: Text) {
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if len(fields[0]) < 1 || len(fields[0]) > 79 {
|
||||
// Invalid profile name
|
||||
ok = false; return
|
||||
return
|
||||
}
|
||||
|
||||
if len(fields[1]) != 0 {
|
||||
// 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
|
||||
@@ -213,12 +216,12 @@ iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
|
||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
|
||||
if zlib_error != nil {
|
||||
bytes.buffer_destroy(&buf)
|
||||
ok = false; return
|
||||
return
|
||||
}
|
||||
|
||||
res.name = strings.clone(string(fields[0]))
|
||||
res.profile = bytes.buffer_to_bytes(&buf)
|
||||
|
||||
ok = true
|
||||
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) {
|
||||
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)
|
||||
if len(fields) != 2 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
|
||||
res.depth = fields[1][0]
|
||||
if res.depth != 8 && res.depth != 16 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
|
||||
data := fields[1][1:]
|
||||
@@ -275,21 +278,21 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
||||
|
||||
if res.depth == 8 {
|
||||
if len(data) % 6 != 0 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
count = len(data) / 6
|
||||
if count > 256 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
|
||||
res.entries = mem.slice_data_cast([][4]u8, data)
|
||||
} else { // res.depth == 16
|
||||
if len(data) % 10 != 0 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
count = len(data) / 10
|
||||
if count > 256 {
|
||||
return {}, false
|
||||
return
|
||||
}
|
||||
|
||||
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.used = u16(count)
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+14
-10
@@ -23,6 +23,7 @@ import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
|
||||
// Limit chunk sizes.
|
||||
// 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:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1195,7 +1196,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o = o[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1206,7 +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
|
||||
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
|
||||
@@ -1247,6 +1248,8 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
||||
|
||||
// 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.
|
||||
up := make([]u8, row_stride, context.temp_allocator)
|
||||
ok = true
|
||||
@@ -1299,10 +1302,9 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// @(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
|
||||
ok = true
|
||||
|
||||
row_stride_in := ((channels * width * depth) + 7) >> 3
|
||||
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
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
// Apron so we don't need to special case first rows.
|
||||
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")
|
||||
defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
||||
|
||||
defilter_16 :: proc(params: ^Filter_Params) -> bool {
|
||||
using params
|
||||
ok = true
|
||||
|
||||
stride := channels * 2
|
||||
row_stride := width * stride
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
// TODO: See about doing a Duff's #unroll where practicable
|
||||
// Apron so we don't need to special case first rows.
|
||||
up := make([]u8, row_stride, context.temp_allocator)
|
||||
@@ -1518,7 +1522,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
||||
dest = dest[row_stride:]
|
||||
}
|
||||
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
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:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Benoit Jacquier: tga loader
|
||||
*/
|
||||
|
||||
|
||||
@@ -14,11 +15,16 @@ import "core:mem"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:strings"
|
||||
|
||||
// TODO: alpha_premultiply support
|
||||
|
||||
Error :: image.Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
GA_Pixel :: image.GA_Pixel
|
||||
RGB_Pixel :: image.RGB_Pixel
|
||||
RGBA_Pixel :: image.RGBA_Pixel
|
||||
|
||||
@@ -57,7 +63,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
||||
}
|
||||
|
||||
header := image.TGA_Header{
|
||||
data_type_code = 0x02, // Color, uncompressed.
|
||||
data_type_code = .Uncompressed_RGB,
|
||||
dimensions = {u16le(img.width), u16le(img.height)},
|
||||
bits_per_pixel = u8(img.depth * img.channels),
|
||||
image_descriptor = 1 << 5, // Origin is top left.
|
||||
@@ -98,4 +104,337 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
|
||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
if .alpha_premultiply in options {
|
||||
return nil, .Unsupported_Option
|
||||
}
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
if .return_header in options && .return_metadata in options {
|
||||
options -= {.return_header}
|
||||
}
|
||||
|
||||
// First check for a footer.
|
||||
filesize := compress.input_size(ctx) or_return
|
||||
|
||||
footer: image.TGA_Footer
|
||||
have_valid_footer := false
|
||||
|
||||
extension: image.TGA_Extension
|
||||
have_valid_extension := false
|
||||
|
||||
if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
|
||||
if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
|
||||
if string(f.signature[:]) == image.New_TGA_Signature {
|
||||
have_valid_footer = true
|
||||
footer = f
|
||||
|
||||
if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
|
||||
if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
|
||||
if e.extension_size == size_of(image.TGA_Extension) {
|
||||
have_valid_extension = true
|
||||
extension = e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header := image.read_data(ctx, image.TGA_Header) or_return
|
||||
|
||||
// Header checks
|
||||
rle_encoding := false
|
||||
color_mapped := false
|
||||
black_white := false
|
||||
src_channels := 0
|
||||
dest_depth := header.bits_per_pixel
|
||||
dest_channels := 0
|
||||
|
||||
#partial switch header.data_type_code {
|
||||
// Supported formats: RGB(A), RGB(A) RLE
|
||||
case .Compressed_RGB:
|
||||
rle_encoding = true
|
||||
case .Uncompressed_RGB:
|
||||
// Intentionally blank
|
||||
case .Uncompressed_Black_White:
|
||||
black_white = true
|
||||
dest_depth = 24
|
||||
case .Uncompressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
case .Compressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
rle_encoding = true
|
||||
case .Compressed_Black_White:
|
||||
black_white = true
|
||||
rle_encoding = true
|
||||
dest_depth = 24
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if color_mapped {
|
||||
if header.color_map_type != 1 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
dest_depth = header.color_map_depth
|
||||
|
||||
// Expect LUT entry index to be 8 bits
|
||||
if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
}
|
||||
|
||||
switch dest_depth {
|
||||
case 15: // B5G5R5
|
||||
src_channels = 2
|
||||
dest_channels = 3
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 16: // B5G5R5A1
|
||||
src_channels = 2
|
||||
dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it.
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 24: // RGB8
|
||||
src_channels = 1 if (color_mapped || black_white) else 3
|
||||
dest_channels = 3
|
||||
case 32: // RGBA8
|
||||
src_channels = 4 if !color_mapped else 1
|
||||
dest_channels = 4
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS {
|
||||
return nil, .Image_Dimensions_Too_Large
|
||||
}
|
||||
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
|
||||
defer if err != nil {
|
||||
destroy(img)
|
||||
}
|
||||
|
||||
img.which = .TGA
|
||||
img.channels = 4 if .alpha_add_if_missing in options else dest_channels
|
||||
img.channels = 3 if .alpha_drop_if_present in options else img.channels
|
||||
|
||||
img.depth = 8
|
||||
img.width = int(header.dimensions[0])
|
||||
img.height = int(header.dimensions[1])
|
||||
|
||||
// Read Image ID if present
|
||||
image_id := ""
|
||||
if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
if .return_metadata in options {
|
||||
id := strings.trim_right_null(string(_id))
|
||||
image_id = strings.clone(id)
|
||||
}
|
||||
}
|
||||
|
||||
color_map := make([]RGBA_Pixel, header.color_map_length)
|
||||
defer delete(color_map)
|
||||
|
||||
if color_mapped {
|
||||
switch header.color_map_depth {
|
||||
case 16:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rg = lut
|
||||
color_map[i].ba = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 24:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rgb = lut
|
||||
color_map[i].a = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 32:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGBA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i] = lut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .return_metadata in options {
|
||||
info := new(image.TGA_Info)
|
||||
info.header = header
|
||||
info.image_id = image_id
|
||||
if have_valid_footer {
|
||||
info.footer = footer
|
||||
}
|
||||
if have_valid_extension {
|
||||
info.extension = extension
|
||||
}
|
||||
img.metadata = info
|
||||
}
|
||||
|
||||
if .do_not_decompress_image in options {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
|
||||
return img, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
origin_is_top := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK != 0
|
||||
origin_is_left := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0
|
||||
rle_repetition_count := 0
|
||||
read_pixel := true
|
||||
is_packet_rle := false
|
||||
|
||||
pixel: RGBA_Pixel
|
||||
|
||||
stride := img.width * dest_channels
|
||||
line := 0 if origin_is_top else img.height - 1
|
||||
|
||||
for _ in 0..<img.height {
|
||||
offset := line * stride + (0 if origin_is_left else (stride - dest_channels))
|
||||
for _ in 0..<img.width {
|
||||
// handle RLE decoding
|
||||
if rle_encoding {
|
||||
if rle_repetition_count == 0 {
|
||||
rle_cmd, err := compress.read_u8(ctx)
|
||||
if err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
is_packet_rle = (rle_cmd >> 7) != 0
|
||||
rle_repetition_count = 1 + int(rle_cmd & 0x7F)
|
||||
read_pixel = true
|
||||
} else if !is_packet_rle {
|
||||
read_pixel = rle_repetition_count > 0
|
||||
} else {
|
||||
read_pixel = false
|
||||
}
|
||||
}
|
||||
// Read pixel
|
||||
if read_pixel {
|
||||
src, src_err := compress.read_slice(ctx, src_channels)
|
||||
if src_err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
switch src_channels {
|
||||
case 1:
|
||||
// Color-mapped or Black & White
|
||||
if black_white {
|
||||
pixel = {src[0], src[0], src[0], 255}
|
||||
} else if header.color_map_depth == 24 {
|
||||
pixel = color_map[src[0]].bgra
|
||||
} else if header.color_map_depth == 16 {
|
||||
lut := color_map[src[0]]
|
||||
v := u16(lut.r) | u16(lut.g) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
}
|
||||
|
||||
case 2:
|
||||
v := u16(src[0]) | u16(src[1]) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
|
||||
case 3:
|
||||
pixel = {src[2], src[1], src[0], 255}
|
||||
case 4:
|
||||
pixel = {src[2], src[1], src[0], src[3]}
|
||||
case:
|
||||
return img, .Corrupt
|
||||
}
|
||||
}
|
||||
|
||||
// Write pixel
|
||||
copy(img.pixels.buf[offset:], pixel[:dest_channels])
|
||||
offset += dest_channels if origin_is_left else -dest_channels
|
||||
rle_repetition_count -= 1
|
||||
}
|
||||
line += 1 if origin_is_top else -1
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
return img, err
|
||||
}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil || img.width == 0 || img.height == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
if v, ok := img.metadata.(^image.TGA_Info); ok {
|
||||
delete(v.image_id)
|
||||
free(v)
|
||||
}
|
||||
|
||||
// Make destroy idempotent
|
||||
img.width = 0
|
||||
img.height = 0
|
||||
free(img)
|
||||
}
|
||||
|
||||
IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7)
|
||||
IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
|
||||
IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.TGA, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package intrinsics
|
||||
|
||||
// Package-Related
|
||||
@@ -188,6 +188,11 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
|
||||
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
// SIMD related
|
||||
@@ -278,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) ---
|
||||
|
||||
// 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) ---
|
||||
|
||||
|
||||
@@ -295,6 +300,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
// Internal compiler use only
|
||||
|
||||
__entry_point :: proc() ---
|
||||
__entry_point :: proc() ---
|
||||
|
||||
@@ -122,73 +122,3 @@ to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #option
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable == nil || s.impl_read_byte == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_byte == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
if s.impl_read_byte != nil {
|
||||
ok = true
|
||||
} else if s.impl_read != nil {
|
||||
ok = true
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, ok: bool = true) #optional_ok {
|
||||
b.stream = s
|
||||
if s.stream_vtable == nil || s.impl_write_byte == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, ok: bool = true) #optional_ok {
|
||||
r.stream = s
|
||||
if s.stream_vtable == nil || s.impl_read_rune == nil {
|
||||
ok = false
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, ok: bool = true) #optional_ok {
|
||||
r.stream = s
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_rune == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
if s.impl_read_rune != nil {
|
||||
ok = true
|
||||
} else if s.impl_read != nil {
|
||||
ok = true
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
} else {
|
||||
ok = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+53
-34
@@ -123,13 +123,6 @@ Writer_At :: struct {using stream: Stream}
|
||||
Reader_From :: struct {using stream: Stream}
|
||||
Writer_To :: struct {using stream: Stream}
|
||||
|
||||
Byte_Reader :: struct {using stream: Stream}
|
||||
Byte_Scanner :: struct {using stream: Stream}
|
||||
Byte_Writer :: struct {using stream: Stream}
|
||||
|
||||
Rune_Reader :: struct {using stream: Stream}
|
||||
Rune_Scanner :: struct {using stream: Stream}
|
||||
|
||||
|
||||
destroy :: proc(s: Stream) -> Error {
|
||||
close_err := close({s})
|
||||
@@ -147,24 +140,48 @@ destroy :: proc(s: Stream) -> Error {
|
||||
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
|
||||
// bytes read along with the error.
|
||||
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
n, err = s->impl_read(p)
|
||||
if n_read != nil {
|
||||
n_read^ += n
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_read != nil {
|
||||
n, err = s->impl_read(p)
|
||||
if n_read != nil {
|
||||
n_read^ += n
|
||||
}
|
||||
return
|
||||
} else if s.impl_read_byte != nil {
|
||||
bytes_read := 0
|
||||
defer if n_read != nil {
|
||||
n_read^ += bytes_read
|
||||
}
|
||||
for _, i in p {
|
||||
p[i] = s->impl_read_byte() or_return
|
||||
bytes_read += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return 0, .Empty
|
||||
}
|
||||
|
||||
// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
|
||||
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
n, err = s->impl_write(p)
|
||||
if n_written != nil {
|
||||
n_written^ += n
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_write != nil {
|
||||
n, err = s->impl_write(p)
|
||||
if n_written != nil {
|
||||
n_written^ += n
|
||||
}
|
||||
return
|
||||
} else if s.impl_write_byte != nil {
|
||||
bytes_written := 0
|
||||
defer if n_written != nil {
|
||||
n_written^ += bytes_written
|
||||
}
|
||||
for c in p {
|
||||
s->impl_write_byte(c) or_return
|
||||
bytes_written += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
return 0, .Empty
|
||||
}
|
||||
@@ -319,7 +336,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
|
||||
|
||||
|
||||
// read_byte reads and returns the next byte from r.
|
||||
read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
read_byte :: proc(r: Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
defer if err == nil && n_read != nil {
|
||||
n_read^ += 1
|
||||
}
|
||||
@@ -339,21 +356,12 @@ read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
|
||||
return buf[0], err
|
||||
}
|
||||
|
||||
write_byte :: proc{
|
||||
write_byte_to_byte_writer,
|
||||
write_byte_to_writer,
|
||||
}
|
||||
|
||||
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
return _write_byte(w, c, n_written)
|
||||
}
|
||||
|
||||
write_byte_to_writer :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
|
||||
return _write_byte(auto_cast w, c, n_written)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
|
||||
_write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
|
||||
defer if err == nil && n_written != nil {
|
||||
n_written^ += 1
|
||||
}
|
||||
@@ -373,7 +381,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
|
||||
}
|
||||
|
||||
// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
|
||||
read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
|
||||
read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
|
||||
defer if err == nil && n_read != nil {
|
||||
n_read^ += size
|
||||
}
|
||||
@@ -417,13 +425,21 @@ read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int,
|
||||
return
|
||||
}
|
||||
|
||||
unread_byte :: proc(s: Byte_Scanner) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_byte != nil {
|
||||
unread_byte :: proc(s: Stream) -> Error {
|
||||
if s.stream_vtable == nil {
|
||||
return .Empty
|
||||
}
|
||||
if s.impl_unread_byte != nil {
|
||||
return s->impl_unread_byte()
|
||||
}
|
||||
if s.impl_seek != nil {
|
||||
_, err := s->impl_seek(-1, .Current)
|
||||
return err
|
||||
}
|
||||
|
||||
return .Empty
|
||||
}
|
||||
unread_rune :: proc(s: Rune_Scanner) -> Error {
|
||||
unread_rune :: proc(s: Writer) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_rune != nil {
|
||||
return s->impl_unread_rune()
|
||||
}
|
||||
@@ -442,7 +458,10 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
|
||||
n_written^ += size
|
||||
}
|
||||
|
||||
if s.stream_vtable != nil && s.impl_write_rune != nil {
|
||||
if s.stream_vtable == nil {
|
||||
return 0, .Empty
|
||||
}
|
||||
if s.impl_write_rune != nil {
|
||||
return s->impl_write_rune(r)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
|
||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_console_logger :: proc(log: ^Logger) {
|
||||
destroy_console_logger :: proc(log: Logger) {
|
||||
free(log.data)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
fmt.sbprintf(&buf, "[%s] ", data.ident)
|
||||
}
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text)
|
||||
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
+5
-6
@@ -6,7 +6,6 @@ import "core:fmt"
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
|
||||
Level :: runtime.Logger_Level
|
||||
/*
|
||||
Logger_Level :: enum {
|
||||
Debug = 0,
|
||||
@@ -16,8 +15,8 @@ Logger_Level :: enum {
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
Level :: runtime.Logger_Level
|
||||
|
||||
Option :: runtime.Logger_Option
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
@@ -30,11 +29,12 @@ Option :: enum {
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
Option :: runtime.Logger_Option
|
||||
|
||||
Options :: runtime.Logger_Options
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
Options :: runtime.Logger_Options
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
@@ -52,12 +52,11 @@ Location_File_Opts :: Options{
|
||||
}
|
||||
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
|
||||
Logger :: runtime.Logger
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
@@ -66,6 +65,7 @@ Logger :: struct {
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
Logger :: runtime.Logger
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
@@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil, Level.Debug, nil}
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Log_Allocator :: struct {
|
||||
allocator: runtime.Allocator,
|
||||
level: Level,
|
||||
prefix: string,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
|
||||
la.allocator = allocator
|
||||
la.level = level
|
||||
la.prefix = prefix
|
||||
la.locked = false
|
||||
}
|
||||
|
||||
|
||||
log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = log_allocator_proc,
|
||||
data = la,
|
||||
}
|
||||
}
|
||||
|
||||
log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
la := (^Log_Allocator)(allocator_data)
|
||||
|
||||
padding := " " if la.prefix != "" else ""
|
||||
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Alloc_Non_Zeroed:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Free:
|
||||
if old_size != 0 {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size},
|
||||
location = location,
|
||||
)
|
||||
} else {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
|
||||
args = {la.prefix, padding, old_memory},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
case .Free_All:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Resize:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Features:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Info:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
if err != nil {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR ERROR=%v",
|
||||
args = {la.prefix, padding, error},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
@@ -25,8 +25,6 @@
|
||||
TODO: Handle +/- Infinity and NaN.
|
||||
*/
|
||||
|
||||
|
||||
//+ignore
|
||||
package math_big
|
||||
|
||||
import "core:mem"
|
||||
|
||||
@@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
|
||||
in the loop is non-zero, although very low.
|
||||
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
|
||||
|
||||
If the BPSW test and/or the addtional Frobenious test have been
|
||||
If the BPSW test and/or the additional Frobenious test have been
|
||||
performed instead of just the Miller-Rabin test with the bases 2 and 3,
|
||||
a single extra test should suffice, so such a very unlikely event will not do much harm.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
*/
|
||||
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package math_big
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -356,7 +356,7 @@ Flux_Tween :: struct($T: typeid) {
|
||||
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
return {
|
||||
values = make(map[^T]Flux_Tween(T), value_capacity),
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity)
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
|
||||
package linalg
|
||||
@@ -81,7 +81,7 @@ max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T))
|
||||
} else when N == 2 {
|
||||
out = builtin.max(a[0], a[1])
|
||||
} else when N == 3 {
|
||||
out = builtin.max(a[0], a[1], a[3])
|
||||
out = builtin.max(a[0], a[1], a[2])
|
||||
}else {
|
||||
out = builtin.max(a[0], a[1])
|
||||
for i in 2..<N {
|
||||
@@ -531,7 +531,7 @@ not_equal :: proc{not_equal_single, not_equal_array}
|
||||
|
||||
any :: proc(x: $A/[$N]bool) -> (out: bool) {
|
||||
for e in x {
|
||||
if x {
|
||||
if e {
|
||||
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}
|
||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
return v / length(v)
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
@@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize}
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
m := length(v)
|
||||
return 0 if m == 0 else v/m
|
||||
}
|
||||
@@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
|
||||
return math.sqrt(dot(v, v))
|
||||
}
|
||||
|
||||
|
||||
+379
-29
@@ -1,6 +1,7 @@
|
||||
package math
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:builtin"
|
||||
_ :: intrinsics
|
||||
|
||||
Float_Class :: enum {
|
||||
@@ -36,6 +37,11 @@ MAX_F16_PRECISION :: 4 // Maximum number of meaningful digits after the decimal
|
||||
RAD_PER_DEG :: TAU/360.0
|
||||
DEG_PER_RAD :: 360.0/TAU
|
||||
|
||||
abs :: builtin.abs
|
||||
min :: builtin.min
|
||||
max :: builtin.max
|
||||
clamp :: builtin.clamp
|
||||
|
||||
sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
|
||||
sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
|
||||
sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
|
||||
@@ -108,6 +114,92 @@ exp :: proc{
|
||||
exp_f64, exp_f64le, exp_f64be,
|
||||
}
|
||||
|
||||
pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
|
||||
pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
|
||||
pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
|
||||
pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
|
||||
pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
|
||||
pow10_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(pow10_f64(f64(x))) }
|
||||
pow10 :: proc{
|
||||
pow10_f16, pow10_f16le, pow10_f16be,
|
||||
pow10_f32, pow10_f32le, pow10_f32be,
|
||||
pow10_f64, pow10_f64le, pow10_f64be,
|
||||
}
|
||||
|
||||
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
@static pow10_pos_tab := [?]f16{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f16{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 4 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -7 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f16(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
@static pow10_pos_tab := [?]f32{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f32{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
|
||||
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
|
||||
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
|
||||
1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39,
|
||||
1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 38 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -45 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f32(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
|
||||
@static pow10_tab := [?]f64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31,
|
||||
}
|
||||
@static pow10_pos_tab32 := [?]f64{
|
||||
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
|
||||
}
|
||||
@static pow10_neg_tab32 := [?]f64{
|
||||
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 308 {
|
||||
return pow10_pos_tab32[uint(n)/32] * pow10_tab[uint(n)%32]
|
||||
}
|
||||
if -323 <= n && n <= 0 {
|
||||
return pow10_neg_tab32[uint(-n)/32] / pow10_tab[uint(-n)%32]
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
|
||||
@@ -185,16 +277,23 @@ log :: proc{
|
||||
log_f64, log_f64le, log_f64be,
|
||||
}
|
||||
|
||||
log2_f16 :: logb_f16
|
||||
log2_f16le :: logb_f16le
|
||||
log2_f16be :: logb_f16be
|
||||
log2_f32 :: logb_f32
|
||||
log2_f32le :: logb_f32le
|
||||
log2_f32be :: logb_f32be
|
||||
log2_f64 :: logb_f64
|
||||
log2_f64le :: logb_f64le
|
||||
log2_f64be :: logb_f64be
|
||||
log2 :: logb
|
||||
log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
|
||||
log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
|
||||
log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
|
||||
|
||||
log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
|
||||
log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
|
||||
log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
|
||||
|
||||
log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
|
||||
log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
|
||||
log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
|
||||
|
||||
log2 :: proc{
|
||||
log2_f16, log2_f16le, log2_f16be,
|
||||
log2_f32, log2_f32le, log2_f32be,
|
||||
log2_f64, log2_f64le, log2_f64be,
|
||||
}
|
||||
|
||||
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
|
||||
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
|
||||
@@ -607,6 +706,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
|
||||
return r
|
||||
}
|
||||
|
||||
divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
return
|
||||
}
|
||||
|
||||
floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
if (div > 0 && y < 0) || (mod < 0 && y > 0) {
|
||||
div -= 1
|
||||
mod += y
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
|
||||
shift :: F16_SHIFT
|
||||
mask :: F16_MASK
|
||||
@@ -1056,7 +1174,7 @@ is_nan :: proc{
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1073,7 +1191,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1090,7 +1208,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1312,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case x == 0:
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
case is_inf(x, 0):
|
||||
if is_inf(x, 1) {
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.25, y)
|
||||
return copy_sign(PI/4, y)
|
||||
}
|
||||
return copy_sign(0, y)
|
||||
}
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.75, y)
|
||||
return copy_sign(3*PI/4, y)
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case is_inf(y, 0):
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
}
|
||||
|
||||
q := atan(y / x)
|
||||
@@ -1347,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
||||
}
|
||||
|
||||
atan2 :: proc{
|
||||
atan2_f16, atan2_f16le, atan2_f16be,
|
||||
atan2_f32, atan2_f32le, atan2_f32be,
|
||||
atan2_f64, atan2_f64le, atan2_f64be,
|
||||
atan2_f64, atan2_f32, atan2_f16,
|
||||
atan2_f64le, atan2_f64be,
|
||||
atan2_f32le, atan2_f32be,
|
||||
atan2_f16le, atan2_f16be,
|
||||
}
|
||||
|
||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1)
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
|
||||
|
||||
asin_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
x := x
|
||||
z, r, s: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x*pio2_hi + 1e-120
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if ix < 0x3e500000 && ix >= 0x00100000 {
|
||||
return x
|
||||
}
|
||||
return x + x*R(x*x)
|
||||
}
|
||||
/* 1 > |x| >= 0.5 */
|
||||
z = (1 - abs(x))*0.5
|
||||
s = sqrt(z)
|
||||
r = R(z)
|
||||
if ix >= 0x3fef3333 { /* if |x| > 0.975 */
|
||||
x = pio2_hi-(2*(s+s*r)-pio2_lo)
|
||||
} else {
|
||||
f, c: f64
|
||||
/* f+c = sqrt(z) */
|
||||
f = s
|
||||
(^u64)(&f)^ &= 0xffffffff_00000000
|
||||
c = (z-f*f)/(s+f)
|
||||
x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
|
||||
}
|
||||
return -x if hx >> 31 != 0 else x
|
||||
}
|
||||
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(asin_f64(f64(x)))
|
||||
}
|
||||
asin :: proc{
|
||||
asin_f64, asin_f32, asin_f16,
|
||||
asin_f64le, asin_f64be,
|
||||
asin_f32le, asin_f32be,
|
||||
asin_f16le, asin_f16be,
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
|
||||
|
||||
acos_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
z, w, s, c, df: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if hx >> 31 != 0 {
|
||||
return 2*pio2_hi + 1e-120
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
if ix <= 0x3c600000 { /* |x| < 2**-57 */
|
||||
return pio2_hi + 1e-120
|
||||
}
|
||||
return pio2_hi - (x - (pio2_lo-x*R(x*x)))
|
||||
}
|
||||
/* x < -0.5 */
|
||||
if hx >> 31 != 0 {
|
||||
z = (1.0+x)*0.5
|
||||
s = sqrt(z)
|
||||
w = R(z)*s-pio2_lo
|
||||
return 2*(pio2_hi - (s+w))
|
||||
}
|
||||
/* x > 0.5 */
|
||||
z = (1.0-x)*0.5
|
||||
s = sqrt(z)
|
||||
df = s
|
||||
(^u64)(&df)^ &= 0xffffffff_00000000
|
||||
c = (z-df*df)/(s+df)
|
||||
w = R(z)*s+c
|
||||
return 2*(df+w)
|
||||
}
|
||||
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(acos_f64(f64(x)))
|
||||
}
|
||||
acos :: proc{
|
||||
acos_f64, acos_f32, acos_f16,
|
||||
acos_f64le, acos_f64be,
|
||||
acos_f32le, acos_f32be,
|
||||
acos_f16le, acos_f16be,
|
||||
}
|
||||
|
||||
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) - exp(-x))*0.5
|
||||
return copy_sign(((exp(x) - exp(-x))*0.5), x)
|
||||
}
|
||||
|
||||
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) + exp(-x))*0.5
|
||||
return ((exp(x) + exp(-x))*0.5)
|
||||
}
|
||||
|
||||
tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
t := exp(2*x)
|
||||
return (t - 1) / (t + 1)
|
||||
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
P0 :: -9.64399179425052238628e-1
|
||||
P1 :: -9.92877231001918586564e1
|
||||
P2 :: -1.61468768441708447952e3
|
||||
Q0 :: +1.12811678491632931402e2
|
||||
Q1 :: +2.23548839060100448583e3
|
||||
Q2 :: +4.84406305325125486048e3
|
||||
|
||||
MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
|
||||
|
||||
|
||||
x := f64(y)
|
||||
z := abs(x)
|
||||
switch {
|
||||
case z > 0.5*MAXLOG:
|
||||
if x < 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case z >= 0.625:
|
||||
s := exp(2 * z)
|
||||
z = 1 - 2/(s+1)
|
||||
if x < 0 {
|
||||
z = -z
|
||||
}
|
||||
case:
|
||||
if x == 0 {
|
||||
return T(x)
|
||||
}
|
||||
s := x * x
|
||||
z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
|
||||
}
|
||||
return T(z)
|
||||
}
|
||||
|
||||
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random element from the given slice
|
||||
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
}
|
||||
+31
-99
@@ -61,114 +61,50 @@ DEFAULT_PAGE_SIZE ::
|
||||
4 * 1024
|
||||
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||
return err
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
|
||||
return err
|
||||
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return runtime.mem_free_all(allocator, loc)
|
||||
}
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return nil
|
||||
} else if ptr == nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
runtime.copy(data, byte_slice(ptr, old_size))
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ptr := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
runtime.copy(data, old_data)
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return data, err
|
||||
return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc)
|
||||
allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
|
||||
return set
|
||||
}
|
||||
return nil
|
||||
@@ -177,29 +113,27 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc)
|
||||
allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc)
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc)
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free((^byte)(str), allocator, loc)
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc)
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc)
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
free(raw.entries.data, raw.entries.allocator, loc)
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -230,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
||||
@@ -245,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
@@ -260,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
array = transmute(T)s
|
||||
return
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, cap, loc)
|
||||
return m
|
||||
}
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||
|
||||
+90
-41
@@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct {
|
||||
}
|
||||
|
||||
|
||||
arena_init :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
a.peak_used = 0
|
||||
a.temp_count = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.arena_init'")
|
||||
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
@@ -51,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena := cast(^Arena)allocator_data
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
@@ -64,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(ptr, size)
|
||||
}
|
||||
return byte_slice(ptr, size), nil
|
||||
|
||||
case .Free:
|
||||
@@ -79,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -143,7 +153,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
s := (^Scratch_Allocator)(allocator_data)
|
||||
|
||||
if s.data == nil {
|
||||
DEFAULT_BACKING_SIZE :: 1<<22
|
||||
DEFAULT_BACKING_SIZE :: 4 * Megabyte
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself")
|
||||
@@ -154,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size := size
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
@@ -162,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -172,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -203,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return ptr, err
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
@@ -258,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -293,6 +310,14 @@ Stack :: struct {
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.stack_init'")
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
@@ -317,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
@@ -335,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, alignment, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -376,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, alignment)
|
||||
return raw_alloc(s, size, alignment, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -402,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment)
|
||||
data, err := raw_alloc(s, size, alignment, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -423,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
case .Query_Info:
|
||||
@@ -445,27 +472,34 @@ Small_Stack_Allocation_Header :: struct {
|
||||
|
||||
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
|
||||
Small_Stack :: struct {
|
||||
data: []byte,
|
||||
offset: int,
|
||||
data: []byte,
|
||||
offset: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'small_stack_init'")
|
||||
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = small_stack_allocator_proc,
|
||||
data = stack,
|
||||
data = stack,
|
||||
}
|
||||
}
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data
|
||||
|
||||
if s.data == nil {
|
||||
@@ -474,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
@@ -490,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, align)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, align, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -525,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, align)
|
||||
return raw_alloc(s, size, align, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -548,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return byte_slice(old_memory, size), nil
|
||||
}
|
||||
|
||||
data, err := raw_alloc(s, size, align)
|
||||
data, err := raw_alloc(s, size, align, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -557,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -600,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
pool := (^Dynamic_Pool)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return dynamic_pool_alloc_bytes(pool, size)
|
||||
case .Free:
|
||||
return nil, .Mode_Not_Implemented
|
||||
@@ -620,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -771,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called")
|
||||
}
|
||||
case .Alloc_Non_Zeroed:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called")
|
||||
@@ -808,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
mode: Allocator_Mode,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
@@ -826,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
|
||||
t.backing = backing_allocator
|
||||
t.allocation_map.allocator = internals_allocator
|
||||
t.bad_free_array.allocator = internals_allocator
|
||||
|
||||
if .Free_All in query_features(t.backing) {
|
||||
t.clear_on_free_all = true
|
||||
}
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
@@ -833,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
@@ -842,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data)
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory)
|
||||
@@ -854,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
info.pointer = nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return
|
||||
}
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
})
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
||||
}
|
||||
result_ptr := raw_data(result)
|
||||
|
||||
@@ -877,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -898,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -906,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -914,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
unreachable()
|
||||
}
|
||||
|
||||
return result, err
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+6
-47
@@ -3,11 +3,11 @@ package mem
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
|
||||
Byte :: 1
|
||||
Kilobyte :: 1024 * Byte
|
||||
Megabyte :: 1024 * Kilobyte
|
||||
Gigabyte :: 1024 * Megabyte
|
||||
Terabyte :: 1024 * Gigabyte
|
||||
Byte :: runtime.Byte
|
||||
Kilobyte :: runtime.Kilobyte
|
||||
Megabyte :: runtime.Megabyte
|
||||
Gigabyte :: runtime.Gigabyte
|
||||
Terabyte :: runtime.Terabyte
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
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 {
|
||||
switch {
|
||||
case a == b:
|
||||
return 0
|
||||
case a == nil:
|
||||
return -1
|
||||
case b == nil:
|
||||
return -1
|
||||
case n == 0:
|
||||
return 0
|
||||
}
|
||||
|
||||
x := slice_ptr(a, n)
|
||||
y := slice_ptr(b, n)
|
||||
|
||||
SU :: size_of(uintptr)
|
||||
fast := n/SU + 1
|
||||
offset := (fast-1)*SU
|
||||
curr_block := 0
|
||||
if n < SU {
|
||||
fast = 0
|
||||
}
|
||||
|
||||
la := slice_ptr((^uintptr)(a), fast)
|
||||
lb := slice_ptr((^uintptr)(b), fast)
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
if la[curr_block] ~ lb[curr_block] != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
if x[pos] ~ y[pos] != 0 {
|
||||
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
if x[offset] ~ y[offset] != 0 {
|
||||
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
return runtime.memory_compare(a, b, n)
|
||||
}
|
||||
|
||||
check_zero :: proc(data: []byte) -> bool {
|
||||
|
||||
+3
-19
@@ -1,5 +1,6 @@
|
||||
package mem
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
|
||||
Raw_Any :: runtime.Raw_Any
|
||||
@@ -8,6 +9,7 @@ Raw_Cstring :: runtime.Raw_Cstring
|
||||
Raw_Slice :: runtime.Raw_Slice
|
||||
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
|
||||
Raw_Map :: runtime.Raw_Map
|
||||
Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
|
||||
|
||||
Raw_Complex64 :: struct {real, imag: f32}
|
||||
Raw_Complex128 :: struct {real, imag: f64}
|
||||
@@ -20,22 +22,4 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: runtime.raw_array_data
|
||||
raw_simd_data :: runtime.raw_simd_data
|
||||
raw_string_data :: runtime.raw_string_data
|
||||
raw_slice_data :: runtime.raw_slice_data
|
||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
||||
raw_data :: runtime.raw_data
|
||||
|
||||
|
||||
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),
|
||||
}
|
||||
raw_data :: builtin.raw_data
|
||||
|
||||
@@ -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
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
DEFAULT_PAGE_SIZE := uint(4096)
|
||||
|
||||
Allocator_Error :: mem.Allocator_Error
|
||||
|
||||
@(require_results)
|
||||
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
return _reserve(size)
|
||||
}
|
||||
@@ -14,6 +16,7 @@ commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
return _commit(data, size)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
data = reserve(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]
|
||||
|
||||
|
||||
@(require_results)
|
||||
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 {
|
||||
result := size + align-1
|
||||
@@ -63,6 +67,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
}
|
||||
|
||||
page_size := DEFAULT_PAGE_SIZE
|
||||
assert(mem.is_power_of_two(uintptr(page_size)))
|
||||
committed := committed
|
||||
committed = clamp(committed, 0, reserved)
|
||||
|
||||
@@ -82,8 +87,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock := platform_memory_alloc(0, total_size) or_return
|
||||
|
||||
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||
commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
|
||||
assert(commit_err == nil)
|
||||
platform_memory_commit(pmblock, uint(base_offset) + committed) or_return
|
||||
|
||||
// Should be zeroed
|
||||
assert(pmblock.block.used == 0)
|
||||
@@ -95,18 +99,12 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
|
||||
sentinel := &global_platform_memory_block_sentinel
|
||||
platform_mutex_lock()
|
||||
pmblock.next = sentinel
|
||||
pmblock.prev = sentinel.prev
|
||||
pmblock.prev.next = pmblock
|
||||
pmblock.next.prev = pmblock
|
||||
platform_mutex_unlock()
|
||||
|
||||
return &pmblock.block, nil
|
||||
}
|
||||
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
@(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 {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(block.base[block.used:])
|
||||
@@ -134,11 +132,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
return nil
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||
size := uint(min_size) + alignment_offset
|
||||
size, size_ok := safe_add(min_size, alignment_offset)
|
||||
if !size_ok {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
|
||||
if block.used + size > block.reserved {
|
||||
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
@@ -153,12 +158,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
||||
platform_mutex_lock()
|
||||
block.prev.next = block.next
|
||||
block.next.prev = block.prev
|
||||
platform_mutex_unlock()
|
||||
|
||||
platform_memory_free(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private, 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
|
||||
}
|
||||
@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
}
|
||||
|
||||
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
committed: uint,
|
||||
reserved: uint,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
|
||||
}
|
||||
}
|
||||
|
||||
platform_mutex_lock :: proc() {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
platform_mutex_unlock :: proc() {
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(init)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
|
||||
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||
if to_commit < block.committed {
|
||||
return nil
|
||||
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
|
||||
commit(block, to_commit) or_return
|
||||
block.committed = to_commit
|
||||
return nil
|
||||
|
||||
@@ -51,6 +51,7 @@ PAGE_TARGETS_INVALID :: 0x40000000
|
||||
PAGE_TARGETS_NO_UPDATE :: 0x40000000
|
||||
|
||||
ERROR_INVALID_ADDRESS :: 487
|
||||
ERROR_COMMITMENT_LIMIT :: 1455
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Kernel32 {
|
||||
@@ -76,12 +77,13 @@ _commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
||||
if result == nil {
|
||||
switch err := GetLastError(); err {
|
||||
case ERROR_INVALID_ADDRESS:
|
||||
case 0:
|
||||
return .Invalid_Argument
|
||||
case ERROR_INVALID_ADDRESS, ERROR_COMMITMENT_LIMIT:
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
// TODO(bill): Handle errors correctly
|
||||
return .Invalid_Argument
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Optional_Ok,
|
||||
Optional_Second,
|
||||
Optional_Allocator_Error,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32]
|
||||
|
||||
@@ -700,6 +700,7 @@ Proc_Type :: struct {
|
||||
|
||||
Pointer_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
pointer: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
r.results = auto_cast clone(r.results)
|
||||
case ^Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
r.tag = clone(r.tag)
|
||||
case ^Multi_Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
case ^Array_Type:
|
||||
|
||||
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
|
||||
Relative_Slice = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
Soa_Pointer = 24,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4
|
||||
@@ -245,6 +246,7 @@ Type :: struct {
|
||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||
// .Multi_Pointer - 1 type: 0=element
|
||||
// .Matrix - 1 type: 0=element
|
||||
// .Soa_Pointer - 1 type: 0=element
|
||||
types: Array(Type_Index),
|
||||
|
||||
// Used by:
|
||||
|
||||
@@ -245,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||
return
|
||||
}
|
||||
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||
if .Optional_Semicolons not_in p.flags {
|
||||
return false
|
||||
}
|
||||
|
||||
prev := p.curr_tok
|
||||
if tokenizer.is_newline(prev) {
|
||||
if tokenizer.is_newline(p.curr_tok) {
|
||||
advance_token(p)
|
||||
return true
|
||||
}
|
||||
@@ -2067,7 +2062,7 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
||||
case "bounds_check": tags += {.Bounds_Check}
|
||||
case "no_bounds_check": tags += {.No_Bounds_Check}
|
||||
case "optional_ok": tags += {.Optional_Ok}
|
||||
case "optional_second": tags += {.Optional_Second}
|
||||
case "optional_allocator_error": tags += {.Optional_Allocator_Error}
|
||||
case:
|
||||
}
|
||||
}
|
||||
@@ -2235,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
return parse_call_expr(p, bd)
|
||||
|
||||
|
||||
case "soa", "simd":
|
||||
case "soa":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
@@ -2244,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case ^ast.Dynamic_Array_Type: t.tag = bd
|
||||
case ^ast.Pointer_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
|
||||
}
|
||||
return original_type
|
||||
|
||||
case "simd":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
original_type := parse_type(p)
|
||||
type := ast.unparen_expr(original_type)
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
|
||||
dirpath: string
|
||||
dirpath, err = absolute_path_from_handle(fd)
|
||||
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
|
||||
defer delete(dirpath)
|
||||
|
||||
n := n
|
||||
size := n
|
||||
if n <= 0 {
|
||||
|
||||
@@ -50,7 +50,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
continue
|
||||
}
|
||||
|
||||
fullpath := make([]byte, len(dirpath)+1+len(filename))
|
||||
fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
|
||||
copy(fullpath, dirpath)
|
||||
copy(fullpath[len(dirpath):], "/")
|
||||
copy(fullpath[len(dirpath)+1:], filename)
|
||||
|
||||
@@ -2,6 +2,7 @@ package os
|
||||
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
dirp: Dir
|
||||
@@ -51,6 +52,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
continue
|
||||
}
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
||||
defer delete(fullpath, context.temp_allocator)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
@@ -41,9 +41,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
return
|
||||
@@ -68,13 +66,16 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
n = -1
|
||||
size = 100
|
||||
}
|
||||
dfi := make([dynamic]File_Info, 0, size)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
wpath: []u16
|
||||
wpath, err = cleanpath_from_handle_u16(fd)
|
||||
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
|
||||
if len(wpath) == 0 || err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size)
|
||||
|
||||
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
|
||||
copy(wpath_search, wpath)
|
||||
wpath_search[len(wpath)+0] = '\\'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
|
||||
// lookup_env gets the value of the environment variable named by the key
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
@@ -18,6 +19,8 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
b := make([dynamic]u16, n, context.temp_allocator)
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
@@ -87,6 +90,7 @@ environ :: proc(allocator := context.allocator) -> []string {
|
||||
|
||||
// clear_env deletes all environment variables
|
||||
clear_env :: proc() {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
envs := environ(context.temp_allocator)
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
@@ -162,7 +163,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
total_read: int
|
||||
length := len(data)
|
||||
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
e: win32.BOOL
|
||||
if is_console {
|
||||
@@ -172,7 +174,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
@@ -325,6 +328,7 @@ get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
@@ -332,6 +336,7 @@ exists :: proc(path: string) -> bool {
|
||||
}
|
||||
|
||||
is_file :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
@@ -342,6 +347,7 @@ is_file :: proc(path: string) -> bool {
|
||||
}
|
||||
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
@@ -357,6 +363,8 @@ is_dir :: proc(path: string) -> bool {
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
|
||||
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
|
||||
|
||||
@@ -385,6 +393,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
|
||||
|
||||
change_directory :: proc(path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wpath) {
|
||||
@@ -394,6 +403,7 @@ change_directory :: proc(path: string) -> (err: Errno) {
|
||||
}
|
||||
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
@@ -405,6 +415,7 @@ make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
@@ -477,12 +488,14 @@ fix_long_path :: proc(path: string) -> string {
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
||||
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
||||
return Errno(win32.CreateHardLinkW(n, o, nil))
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
@@ -494,6 +507,7 @@ unlink :: proc(path: string) -> (err: Errno) {
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
|
||||
|
||||
+15
-11
@@ -93,7 +93,7 @@ file_size_from_path :: proc(path: string) -> i64 {
|
||||
return length
|
||||
}
|
||||
|
||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0)
|
||||
@@ -102,10 +102,10 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
return read_entire_file_from_handle(fd, allocator)
|
||||
return read_entire_file_from_handle(fd, allocator, loc)
|
||||
}
|
||||
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
length: i64
|
||||
@@ -118,9 +118,9 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
|
||||
return nil, true
|
||||
}
|
||||
|
||||
data = make([]byte, int(length), allocator)
|
||||
data = make([]byte, int(length), allocator, loc)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read_full(fd, data)
|
||||
@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -216,7 +216,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
new_memory = aligned_alloc(new_size, new_alignment, p) or_return
|
||||
|
||||
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if new_size > old_size {
|
||||
new_region := mem.raw_data(new_memory[old_size:])
|
||||
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
processor_core_count :: proc() -> int {
|
||||
return _processor_core_count()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> string {
|
||||
get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
|
||||
value, _ := lookup_env(key, allocator)
|
||||
return value
|
||||
}
|
||||
@@ -13,7 +15,7 @@ get_env :: proc(key: string, allocator := context.allocator) -> string {
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
return _lookup_env(key, allocator)
|
||||
}
|
||||
|
||||
@@ -36,7 +38,7 @@ clear_env :: proc() {
|
||||
|
||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
return _environ(allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
import "core:runtime"
|
||||
|
||||
_get_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
@@ -20,7 +22,7 @@ _clear_env :: proc() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
//TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -65,7 +65,19 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs)
|
||||
|
||||
r := make([dynamic]string, 0, 50, allocator)
|
||||
n := 0
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := ([^]u16)(p)[i]
|
||||
if c == 0 {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
r := make([dynamic]string, 0, n, allocator)
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := ([^]u16)(p)[i]
|
||||
if c == 0 {
|
||||
|
||||
@@ -39,10 +39,8 @@ _file_allocator :: proc() -> runtime.Allocator {
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
name_cstr := _name_to_cstring(name)
|
||||
|
||||
flags_i: int
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
@@ -254,7 +252,7 @@ _symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
|
||||
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
||||
bufsz : uint = 256
|
||||
buf := make([]byte, bufsz, allocator)
|
||||
for {
|
||||
@@ -272,7 +270,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (
|
||||
}
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
@@ -411,12 +409,7 @@ _is_dir_fd :: proc(fd: int) -> bool {
|
||||
// defined as 512, however, it is well known that paths can exceed that limit.
|
||||
// So, in theory you could have a path larger than the entire temp_allocator's
|
||||
// buffer. Therefor, any large paths will use context.allocator.
|
||||
_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
|
||||
if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
|
||||
cname = strings.clone_to_cstring(name)
|
||||
allocated = true
|
||||
return
|
||||
}
|
||||
cname = strings.clone_to_cstring(name, context.temp_allocator)
|
||||
return
|
||||
@(private="file")
|
||||
_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) {
|
||||
return strings.clone_to_cstring(name, context.temp_allocator)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:io"
|
||||
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
s.stream_data = f
|
||||
s.stream_vtable = _file_stream_vtable
|
||||
s.stream_vtable = &_file_stream_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
|
||||
|
||||
@(private)
|
||||
_file_stream_vtable := &io.Stream_VTable{
|
||||
_file_stream_vtable := io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
@@ -74,7 +75,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
read_entire_file :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
|
||||
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.fd = rawptr(handle)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment)
|
||||
|
||||
case .Free:
|
||||
|
||||
@@ -4,17 +4,17 @@ package os2
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int, zero_memory: bool) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size)
|
||||
return heap_alloc(new_size, zero_memory)
|
||||
}
|
||||
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
@@ -36,16 +36,16 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr), zero_memory)
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -72,12 +72,12 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p)
|
||||
return aligned_alloc(new_size, new_alignment, true, p)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment)
|
||||
return aligned_alloc(size, alignment, true)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user