mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
770 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74458ab096 | |||
| c39b1a31db | |||
| 635c7fa153 | |||
| b7ac0a9e8d | |||
| 3f3ae4b2b6 | |||
| c2423dc07f | |||
| 1296630160 | |||
| 63eec25044 | |||
| 7a9b0731cf | |||
| d45661c405 | |||
| 002bec256a | |||
| 000861cba8 | |||
| 36473b2774 | |||
| 4188f50105 | |||
| 3e3b9ae2df | |||
| e89f0de232 | |||
| 4858e16a11 | |||
| 902a6db0e1 | |||
| 19ae6122c7 | |||
| b82b91ea08 | |||
| 636f0d7063 | |||
| ed73441a4c | |||
| 4c2be6cd49 | |||
| f3f51bd643 | |||
| 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 | |||
| f5431a046d | |||
| 5a9422b6bc | |||
| d30198c99a | |||
| 0c8d59dd20 | |||
| a460d140fe | |||
| 881d18ee88 | |||
| d73a4aa34b | |||
| 5c298c1501 | |||
| a83ca2120e | |||
| 81799f7f78 | |||
| 7973f7e750 | |||
| 3dc62a67e0 | |||
| 081e36c909 | |||
| 3a1d364f59 | |||
| e50648279d | |||
| 929af320da | |||
| 8e7c7eeeba | |||
| b739044e69 | |||
| 9e0107c9fc | |||
| 107e016508 | |||
| 22d16c20f8 | |||
| 697c839c84 | |||
| de8bd88d2a | |||
| 595efba747 | |||
| c041d15569 | |||
| 57b20e634b | |||
| e285796fc1 | |||
| a19494d3a7 | |||
| d2a362fd52 | |||
| 0f3562ef02 | |||
| 03f683f9e7 | |||
| cecadce86d | |||
| a7c3906003 | |||
| 70dc0c15fd | |||
| 9eeed9d5bd | |||
| a054c2934e | |||
| 38102f14c1 | |||
| 0997df4fcf | |||
| a5a56e061c | |||
| 8b007ad55a | |||
| 57dd5ec4db | |||
| 5b621d5be1 | |||
| 7aee762f3a | |||
| 4ee50c5a35 | |||
| 28f440dd9e | |||
| 43b78e51a4 | |||
| 84f9fb706b | |||
| 812823cad8 | |||
| 0655260378 | |||
| cfc3723879 | |||
| 4c3281b3f2 | |||
| ff94c605e0 | |||
| cb0a59bb2c | |||
| 076700bd22 | |||
| bcccc8338f | |||
| 425dec8bb8 | |||
| 838554460b | |||
| 659c3c528d | |||
| 60aeab3c38 | |||
| 5e3cf45df3 | |||
| 4633591918 | |||
| 0e6a8b7c72 | |||
| 147848ca20 | |||
| cde002c579 | |||
| f23d93ba89 | |||
| c97a8418dc | |||
| 4aca9372a6 | |||
| 4912ecc3ea | |||
| c1c8ceafc2 | |||
| 9c0a3b6c60 | |||
| 7b539e3025 | |||
| b2b0043875 | |||
| 53e0d182af | |||
| a6fa41e290 | |||
| edba99d636 | |||
| 35674959f2 | |||
| dc8b7a0eb8 | |||
| 8d1f46d837 | |||
| a2117d23b2 | |||
| a58e4d0359 | |||
| 576914aee1 | |||
| 8171f8209a | |||
| 64ff05303c | |||
| 6caab6225d | |||
| 326411498a | |||
| d50fcf0020 | |||
| d354d36a3b | |||
| 483a72ac61 | |||
| dbec4b0d0e | |||
| 4cb489b9e4 | |||
| 28ec50d567 | |||
| 73beed0477 | |||
| e0ecdd4b24 | |||
| 0cb1a578d0 | |||
| 5168cf03a9 | |||
| b886ae6515 | |||
| 277a973b98 | |||
| 0ec4d97bfd | |||
| e6236e5c3e | |||
| e201280844 | |||
| 8e50a6c61b | |||
| 7e6f5f89d0 | |||
| 97acc57649 | |||
| 83c8c48ed7 | |||
| 0815b4d59f | |||
| a0135080b3 | |||
| f45e8e5d47 | |||
| 98ba4beede | |||
| a9304f2fef | |||
| a0697ab057 | |||
| 2e895c72d3 | |||
| 674ebe395f | |||
| 96eecaab54 | |||
| b1ae5bc9fe | |||
| 7258588ed5 | |||
| d913155972 | |||
| 9746e25784 | |||
| d26cfd2141 | |||
| 21f2c06f4b | |||
| 727a25f41f | |||
| 3f27cb2309 | |||
| f213622982 | |||
| 4aad835a66 | |||
| 4af8a64580 | |||
| c9c3611b1d | |||
| 220dfd7440 | |||
| bce8819ed5 | |||
| 5f2b220a85 | |||
| f174c805a9 | |||
| 25869b7504 | |||
| ecd81e8a53 | |||
| d7f9f7f170 | |||
| 08f5259d77 | |||
| a9f744cb64 | |||
| b02e42c6dc | |||
| cb0273b5d7 | |||
| 8dbf45a65a | |||
| 9f64de9568 | |||
| 0ebe9ba487 | |||
| efe00e1aa6 | |||
| 2bdbce55f9 | |||
| 9614ca92f0 | |||
| d30e59f539 | |||
| a3afe617c2 | |||
| 69daac583e | |||
| b28d4b753b | |||
| e6ab4f4856 | |||
| c8ab1b7ee1 | |||
| 9f10487678 | |||
| 2542983d70 | |||
| d492fb3501 | |||
| 1829aa1638 | |||
| 4cb4173ced | |||
| 00e704b216 | |||
| 227ee0f705 | |||
| 17f47a7ab0 | |||
| 4e8bc0786d | |||
| 3d3ccf061f | |||
| 3a8adc6721 | |||
| e1748a5dd1 | |||
| 59b4c889d3 | |||
| b6408d1b3f | |||
| 3db3047f47 | |||
| 7420fbd95b | |||
| 7c990b3833 | |||
| 9c059f1a12 | |||
| fb167d1d0a | |||
| 0992239d86 | |||
| 9eb3da0474 | |||
| e91f8feedf | |||
| 22a0c3fce1 | |||
| 6c7e5748a8 | |||
| 0b0c6da8b0 | |||
| 78826071c0 | |||
| e61b73d7ad | |||
| f886632bf1 | |||
| eafa5098aa | |||
| 0571b80d37 | |||
| 80c10644dd | |||
| 041625381c | |||
| 48f56d728b | |||
| 872d391cfb | |||
| 3e6ec65dd9 | |||
| 157c87b2a2 | |||
| d3081bd889 | |||
| 2ae5bf4395 | |||
| f28547cae1 | |||
| 5332705e31 | |||
| bfb082cda4 | |||
| 37d04198ab | |||
| ae9d540c1c | |||
| c90b7c38f1 | |||
| 9e376fbda7 | |||
| 00739bf06d | |||
| e8148055ad | |||
| dd0a20ab45 | |||
| babbc304b8 | |||
| a8b44f33bd | |||
| bd48561688 | |||
| 23842a8950 | |||
| 1676c643df | |||
| e2bfb024de | |||
| 4d06a54c0c | |||
| 0a8e6169d7 | |||
| 04ae87eaef | |||
| b313d09c2c | |||
| c0d2359a91 | |||
| 51a2f09032 | |||
| f60e8031f2 | |||
| ea42613fec | |||
| b89bb87759 | |||
| 2cbb3d5a24 | |||
| 9e288b7ce8 | |||
| 776b48c10d | |||
| 199dae6cd5 | |||
| e58f45bef7 | |||
| abe122ecb7 | |||
| f8744d87b0 | |||
| 3e7f6b8751 | |||
| 6ffe814ca7 | |||
| eec9be71f6 | |||
| 888913c739 | |||
| 33d96fd28a | |||
| f1e8738af2 | |||
| 9c52a11b1b | |||
| d89c4606bd | |||
| c6903fbcd5 | |||
| 13c8149046 | |||
| 665db0f778 | |||
| 173286de65 | |||
| f2ecda8fec | |||
| 6f1222e9bf | |||
| 84a424f21e | |||
| 9b7710488b | |||
| 9f413862e9 | |||
| b8802d7df7 | |||
| b13dad02a4 | |||
| f045f8d805 | |||
| 30b7c8ad66 | |||
| 89222a0ab2 | |||
| 21e637d2b3 | |||
| 6c196931d2 | |||
| d7195b0798 | |||
| b40998de9e | |||
| 8c0c327df9 | |||
| 50cbb8a1fc | |||
| ff9d058392 | |||
| 1acc8f438b | |||
| c53426fcb4 | |||
| 5187db525f | |||
| a8bd340267 | |||
| ca1f419dc2 | |||
| fae60a6b88 | |||
| 108558ddfc | |||
| 65b8cfae82 | |||
| 4055c31cf0 | |||
| e88af4e458 | |||
| 79eb7b52d9 | |||
| 83b2bf44c4 | |||
| 670f18ad1b | |||
| 9a81716936 | |||
| f013499eea | |||
| d04f732e68 | |||
| 35fd8e7f68 | |||
| 38ff2a3ed9 | |||
| bd502d16bc | |||
| a11e17fbc3 | |||
| 8b3b659433 | |||
| f33228fd6e | |||
| df5b693de8 | |||
| ab98108441 | |||
| 6f80174f84 | |||
| 0ebe81fce2 | |||
| c75dd14308 | |||
| c166b6a21d | |||
| 6ed5cbee12 | |||
| 5b200ccdf8 | |||
| cf0f0c4b31 | |||
| 1399ecb41c | |||
| 339d6cfd41 | |||
| 7bded4f189 | |||
| c7269b9ef0 | |||
| 85688015aa | |||
| 3c6cc575c6 | |||
| 0564cb6483 | |||
| 4be92c7eb8 | |||
| 88e9eb7d0c | |||
| d19fc54c3d | |||
| 5d4291d9fa | |||
| b70cd03e9e | |||
| e91e5e1fe9 | |||
| ae57a49915 | |||
| bfcb527b42 | |||
| a343fb171d | |||
| fa2296a124 | |||
| a996cfc536 | |||
| c1d55b9296 | |||
| 5c18cca1ca | |||
| 78e6cd0c60 | |||
| 176954a6d8 | |||
| ad6b3bd95f | |||
| 762895bc45 | |||
| 592e9afa5f | |||
| a2e0373934 | |||
| fb49841b1d | |||
| 01ea0d6f1e | |||
| bb7f291f5f | |||
| 174fa9b490 | |||
| dda2ed290a | |||
| ee9908b09e | |||
| 66de1856e3 | |||
| ba5f7c4e2a | |||
| 487bd3d942 | |||
| 4fac7a8f27 | |||
| 25dae06b6a | |||
| a1f15c2c69 | |||
| 516f6647b4 | |||
| a7840d50e2 | |||
| cb10af08cb | |||
| 4e49d24df9 | |||
| 68222cb8ab | |||
| 912d29af83 | |||
| f3868ac932 | |||
| 5b42dd7707 | |||
| 51707032d1 | |||
| a0babefe55 | |||
| f3aefbc443 | |||
| a6c779b50e | |||
| 9fa41a97b9 | |||
| cef022539e | |||
| f6dfa33697 | |||
| bc3bf939e0 | |||
| f5e5eac3b9 | |||
| d50786bd30 | |||
| 0ccbea17aa | |||
| 136d50a745 | |||
| babfba5e8f | |||
| 846f8377b2 | |||
| 77d4409549 | |||
| 7f3540b7f5 | |||
| 3ad2cde833 | |||
| 910799cc5f | |||
| 6c0192083e | |||
| c60d7842cd | |||
| d7eaf0f87b | |||
| bb4329711c | |||
| 618d3bf62f | |||
| cf8a4b9812 | |||
| 4db533ff71 | |||
| f28e3276e7 | |||
| 026540040d | |||
| 8518d3b232 | |||
| 1c1f5e2231 | |||
| bdedfb1071 | |||
| 92ed9e0b94 | |||
| 5c10b35df7 | |||
| 0668811397 | |||
| 6bb6344208 | |||
| 2f7bd154a2 | |||
| 20c5033b38 | |||
| 20fe6d102a | |||
| 4e30a64d9f | |||
| c48ef7d70b | |||
| e079a7009d | |||
| f383bf3136 | |||
| 609ddf28b7 | |||
| 34f1bda57c | |||
| f137b927b6 | |||
| 2185dada56 | |||
| 0b08080119 | |||
| 432b2b19e9 | |||
| 952f294bce | |||
| c23274adb0 | |||
| 833f9dd037 | |||
| 1ff8b97dae | |||
| 70451f9335 | |||
| 1f438d4e6c | |||
| 421d45a7a7 | |||
| 20e7b5c88a | |||
| 7092273a8f | |||
| d0e8a735ba | |||
| 208226dba2 | |||
| f308f37ba1 | |||
| c2610cb75e | |||
| 59e9df2609 | |||
| 66b5a35ec3 | |||
| f3f6c12a7c | |||
| e331b0647e | |||
| 35502816c7 | |||
| 7ec0236fbf | |||
| 0fd43c1a0b | |||
| 06337129d8 | |||
| 337780497d | |||
| 10deb2e88b | |||
| b95ca80f85 | |||
| 83d880a94a | |||
| cde6a2f7a5 | |||
| c2f5cbdeb4 | |||
| 8e57511ffa | |||
| 12d19d21c4 | |||
| 7002c94a63 | |||
| 57e69ea392 | |||
| 09f936b04d | |||
| 140c00aa0c | |||
| 808ea30b48 | |||
| 63d6c08d90 | |||
| 10e4de3c01 | |||
| 8ac12886ed | |||
| 63cc8a80a0 | |||
| 1549d01bf7 | |||
| b168bf9460 | |||
| 0203bb657e | |||
| 53f0c6ef1a | |||
| 4c4480104d | |||
| 5c72974167 | |||
| f21e9ee712 | |||
| 81dd727f75 | |||
| 3b54015e80 | |||
| b032d5af87 | |||
| 209a155608 | |||
| d8e77cd738 | |||
| 95d4ce4aa3 | |||
| 39393cca92 | |||
| acadbe050c | |||
| 8fcf2f5dca | |||
| 831a86599e | |||
| 233b32fd3e | |||
| 3c5124ce68 | |||
| a8d78660ee | |||
| cc1df9591f | |||
| 54a326f046 | |||
| 3d9d85121d | |||
| a31d23a32a | |||
| 084f431aa5 | |||
| 7002f0a7d7 | |||
| 3ec70c5517 | |||
| d9f293b281 | |||
| 8c1499dbc2 | |||
| 7d2eedee73 | |||
| eba35a8f7d | |||
| e967f2ca2c | |||
| 438713af20 | |||
| 568869077e | |||
| f25a3f2a7d | |||
| 5609221831 | |||
| f3432e6bb5 | |||
| 43b350c590 | |||
| c2c66aad60 | |||
| d7681d5b06 | |||
| c902615192 | |||
| 2895830ce6 | |||
| 1eef9552b4 | |||
| 577fa2d29b | |||
| 72fcf16a39 | |||
| b9d523e0b2 | |||
| f3d225ca4f | |||
| d84d2f85e8 | |||
| 10f1d8c604 | |||
| 184d1c57b1 | |||
| dfbe68bcfe | |||
| 3049e07f72 | |||
| da54d0ec8c | |||
| b57edb89eb | |||
| e43eccbb91 | |||
| e48f41165c | |||
| 9eb4cbcbd2 | |||
| 2612f241c9 | |||
| 0f1153fae2 | |||
| b84561f2b8 | |||
| f7e78e2671 | |||
| d10a2bc5d5 | |||
| 94fda3d48d | |||
| 5cf4f565d6 | |||
| c20b5cbd10 | |||
| 115612620f | |||
| a5bf3b0bc5 | |||
| 5c647e2f61 | |||
| 06884da42b | |||
| 6e7179d8f3 | |||
| e85f1dd9fb | |||
| 9ac94e621b | |||
| db8d119cad | |||
| 836c325021 | |||
| 3bb31093fa | |||
| 214b43974d | |||
| 55556aea77 | |||
| 223897d224 | |||
| 542e45de26 | |||
| 1fa9488a4d | |||
| b1196bd659 | |||
| 57167be2a6 | |||
| 846930a07f | |||
| 0cc67ff5e3 | |||
| a86574da84 | |||
| 5a6836ab99 | |||
| 43432f92ec | |||
| d1499f3f78 | |||
| fff23e2bbb | |||
| 33895b6d92 | |||
| e10105a780 | |||
| 5451c9672d | |||
| 4eba2bb8d9 | |||
| 2a58bceb56 | |||
| fdcf08410c | |||
| 5142955f00 | |||
| 500150b12a | |||
| 50ddd8dd26 | |||
| 6c6de2a07d | |||
| 01912b6ba5 | |||
| be2c7b5c9b | |||
| ed60ed3bae | |||
| 23cb96de02 | |||
| a2c771876e | |||
| b5b329378f | |||
| f7b18cd86e | |||
| d74e4b427d | |||
| 22dc020647 | |||
| e8485ee7e7 | |||
| c516fb947f | |||
| 3aa0a733f3 | |||
| 4e080057fb | |||
| 9c1f270bd5 | |||
| e46d87b221 | |||
| 5bc866e420 | |||
| 5af7004f44 | |||
| 01e8e682c0 | |||
| 2ef6544ca2 | |||
| 9921ac01cc | |||
| 7057f5fc11 | |||
| f17a9dd5e7 | |||
| ec3394b8da | |||
| 0cca42a1f4 | |||
| 85edcf9cc2 | |||
| 3b842ffe29 | |||
| 6c0e2e2a53 | |||
| 42371f7aea | |||
| 286f782e5e | |||
| 58fc305b11 | |||
| 7e0c359f99 | |||
| f50399e394 | |||
| 7bc21c6691 | |||
| dd56c85e55 | |||
| 9e2a847ebc | |||
| daef39a206 | |||
| 5d496cdcda | |||
| f27f595549 | |||
| 536e0a8c29 | |||
| bc18310107 | |||
| 3fdb3dd767 | |||
| d224679619 | |||
| 2dd181e663 | |||
| f002857edc | |||
| 97739da85a | |||
| 6c14586fff | |||
| 0c45a46aab | |||
| d1fc9d3073 | |||
| 2fb351bf04 | |||
| dc832ad49f | |||
| eef44b11f3 | |||
| bb4f108487 | |||
| ccb38c3dc6 | |||
| cc81057d21 | |||
| 8b4b81fdeb | |||
| 4cb46f5631 | |||
| f4ad4c7aa6 | |||
| 37dda30c49 | |||
| 8fb718245a | |||
| a4cb6f96ea | |||
| 56e3b7cb7d | |||
| ae1f5d2181 | |||
| b4df272eb5 | |||
| dca2fbccff | |||
| d48d3bfa87 | |||
| 8559790bd8 | |||
| 37c6279031 | |||
| 0d4e710b96 | |||
| 205aa10b88 | |||
| 6f1cc3946b | |||
| 253ecd55a0 | |||
| c9e31dc90d | |||
| 3d06322d4a | |||
| 85e6efdf16 | |||
| 6b89ff43ea | |||
| 4cdc55af91 | |||
| 4b289f904c | |||
| 53c70da0b8 | |||
| 6f20b5bb59 | |||
| 96ab17ecfc | |||
| 18bde22b26 | |||
| e61aad925b | |||
| 5d190b15d7 | |||
| fe442d7c0c | |||
| 97d1a67871 | |||
| bac96cf2ad | |||
| 7e0cc0af25 | |||
| 1d29d9be25 | |||
| 0e91e63043 | |||
| 0cf37bde8b | |||
| 173799527a | |||
| 5931e2383b | |||
| e4743b15b1 | |||
| 9f95d6fa65 | |||
| 982a1aebb3 | |||
| cec049b7d3 | |||
| dc323cfa1d | |||
| 0afa226a93 | |||
| 1146604344 | |||
| 89c2e1a5fa | |||
| 971d498e79 | |||
| 6e7a50c02f | |||
| 6aaab4988e | |||
| d9b0c05acf | |||
| 47f637d23b | |||
| 59f55a2119 | |||
| 8bac82320f | |||
| 14bf20320a | |||
| b99940f33a | |||
| 81495068b9 | |||
| 6985181961 | |||
| 97717d65ef | |||
| 8023c8abc7 | |||
| 2d3f59d9a7 | |||
| be8de4a1ff | |||
| 18ad6c33ef | |||
| 0e27b27b81 | |||
| 10a311092b | |||
| 18463d68d4 | |||
| 74d3bcec05 | |||
| b2fdb881eb | |||
| be9b935953 | |||
| 9e69452327 | |||
| d24bebdb9e | |||
| 4911df9f99 | |||
| a223340c44 | |||
| 5bc8164274 | |||
| 1a2c36e482 | |||
| 56737c1431 | |||
| 9ae566adcc | |||
| aadb4db211 | |||
| 88de3a1c06 | |||
| 6456618891 | |||
| e252d3bedf | |||
| 36c22393a4 | |||
| 6d6e840bc2 | |||
| 4b1822ade8 | |||
| b21e7e4518 | |||
| 1f4e5e919f | |||
| c293e88f2e | |||
| e008b5a160 | |||
| 0b61215f7b | |||
| b91c0ec715 | |||
| bad295cf69 | |||
| 832003dd4b | |||
| 1f19610fd6 | |||
| 658a605c75 | |||
| e51bb4ef12 | |||
| 6630d703f8 | |||
| 9c3cdc4620 | |||
| 345032f804 | |||
| a315e7c962 | |||
| 42364f2fce | |||
| 8f600798ef | |||
| f28c268d97 | |||
| f1cff20249 | |||
| 865d88dd56 | |||
| 069c05669f | |||
| 516065d7c2 |
@@ -1,5 +1,5 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: "Close Stale Issues & PRs"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 21 * * *"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v4.1.0
|
||||
with:
|
||||
# stale-issue-message: |
|
||||
# Hello!
|
||||
#
|
||||
# I am marking this issue as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||
# - open a PR referencing and resolving the issue;
|
||||
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||
# - open a new issue with updated details and a plan on resolving the issue.
|
||||
#
|
||||
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||
#
|
||||
# stale-pr-message: |
|
||||
# Hello!
|
||||
#
|
||||
# I am marking this PR as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||
#
|
||||
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||
|
||||
days-before-stale: 120
|
||||
days-before-close: 30
|
||||
exempt-draft-pr: true
|
||||
ascending: true
|
||||
operations-per-run: 1000
|
||||
exempt-issue-labels: "ignore"
|
||||
@@ -269,6 +269,8 @@ bin/
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
*.bin
|
||||
demo.bin
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
@@ -58,7 +58,7 @@ set libs= ^
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set linker_flags=%linker_flags% -debug
|
||||
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||
) else ( rem Release
|
||||
set linker_flags=%linker_flags% -debug
|
||||
)
|
||||
|
||||
+35
-23
@@ -1,12 +1,20 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD)
|
||||
: ${CXX=clang++}
|
||||
: ${CPPFLAGS=}
|
||||
: ${CXXFLAGS=}
|
||||
: ${LDFLAGS=}
|
||||
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
|
||||
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD || :)
|
||||
if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
|
||||
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
LDFLAGS="-pthread -lm -lstdc++"
|
||||
CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
|
||||
CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||
CC=clang
|
||||
OS=$(uname)
|
||||
|
||||
panic() {
|
||||
@@ -18,13 +26,13 @@ version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
|
||||
|
||||
config_darwin() {
|
||||
ARCH=$(uname -m)
|
||||
LLVM_CONFIG=llvm-config
|
||||
: ${LLVM_CONFIG=llvm-config}
|
||||
|
||||
# allow for arm only llvm's with version 13
|
||||
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
|
||||
|
||||
@@ -37,34 +45,38 @@ config_darwin() {
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
|
||||
config_freebsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config11
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
|
||||
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_openbsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config}
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_linux() {
|
||||
if which llvm-config > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config
|
||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
: ${LLVM_CONFIG=}
|
||||
|
||||
if [ ! "$LLVM_CONFIG" ]; then
|
||||
if which llvm-config > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config
|
||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
fi
|
||||
fi
|
||||
|
||||
MIN_LLVM_VERSION=("11.0.0")
|
||||
@@ -74,7 +86,7 @@ config_linux() {
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
@@ -97,7 +109,7 @@ build_odin() {
|
||||
esac
|
||||
|
||||
set -x
|
||||
$CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||
$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||
set +x
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
+66
-66
@@ -1,90 +1,90 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!=0;
|
||||
true :: 0==0;
|
||||
nil :: nil
|
||||
false :: 0!=0
|
||||
true :: 0==0
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
ODIN_OS :: ODIN_OS
|
||||
ODIN_ARCH :: ODIN_ARCH
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||
ODIN_VENDOR :: ODIN_VENDOR
|
||||
ODIN_VERSION :: ODIN_VERSION
|
||||
ODIN_ROOT :: ODIN_ROOT
|
||||
ODIN_DEBUG :: ODIN_DEBUG
|
||||
|
||||
byte :: u8; // alias
|
||||
byte :: u8 // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
bool :: bool
|
||||
b8 :: b8
|
||||
b16 :: b16
|
||||
b32 :: b32
|
||||
b64 :: b64
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
i8 :: i8
|
||||
u8 :: u8
|
||||
i16 :: i16
|
||||
u16 :: u16
|
||||
i32 :: i32
|
||||
u32 :: u32
|
||||
i64 :: i64
|
||||
u64 :: u64
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
i128 :: i128
|
||||
u128 :: u128
|
||||
|
||||
rune :: rune;
|
||||
rune :: rune
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
f16 :: f16
|
||||
f32 :: f32
|
||||
f64 :: f64
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
complex32 :: complex32
|
||||
complex64 :: complex64
|
||||
complex128 :: complex128
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
quaternion64 :: quaternion64
|
||||
quaternion128 :: quaternion128
|
||||
quaternion256 :: quaternion256
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
int :: int
|
||||
uint :: uint
|
||||
uintptr :: uintptr
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
rawptr :: rawptr
|
||||
string :: string
|
||||
cstring :: cstring
|
||||
any :: any
|
||||
|
||||
typeid :: typeid;
|
||||
typeid :: typeid
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
i16le :: i16le
|
||||
u16le :: u16le
|
||||
i32le :: i32le
|
||||
u32le :: u32le
|
||||
i64le :: i64le
|
||||
u64le :: u64le
|
||||
i128le :: i128le
|
||||
u128le :: u128le
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
i16be :: i16be
|
||||
u16be :: u16be
|
||||
i32be :: i32be
|
||||
u32be :: u32be
|
||||
i64be :: i64be
|
||||
u64be :: u64be
|
||||
i128be :: i128be
|
||||
u128be :: u128be
|
||||
|
||||
|
||||
f16le :: f16le;
|
||||
f32le :: f32le;
|
||||
f64le :: f64le;
|
||||
f16le :: f16le
|
||||
f32le :: f32le
|
||||
f64le :: f64le
|
||||
|
||||
f16be :: f16be;
|
||||
f32be :: f32be;
|
||||
f64be :: f64be;
|
||||
f16be :: f16be
|
||||
f32be :: f32be
|
||||
f64be :: f64be
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
return copy(b.buf[m:], p), nil
|
||||
}
|
||||
|
||||
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||
return buffer_write(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(s))
|
||||
@@ -229,6 +233,10 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
return
|
||||
}
|
||||
|
||||
buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
|
||||
|
||||
+45
-1
@@ -10,7 +10,14 @@ clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location
|
||||
return c[:len(s)]
|
||||
}
|
||||
|
||||
ptr_from_slice :: proc(str: []byte) -> ^byte {
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
copy(c, s)
|
||||
return c[:len(s)], nil
|
||||
}
|
||||
|
||||
ptr_from_slice :: ptr_from_bytes
|
||||
ptr_from_bytes :: proc(str: []byte) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
@@ -134,6 +141,25 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := len(sep) * (len(a) - 1)
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
@@ -151,6 +177,24 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := 0
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
||||
@private
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||
s, n := s, n
|
||||
|
||||
@@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
|
||||
|
||||
|
||||
quote_string :: proc(s: string) -> []byte {
|
||||
b := strings.make_builder(0, len(s)+2)
|
||||
b := strings.builder_make(0, len(s)+2)
|
||||
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
||||
return b.buf[:]
|
||||
}
|
||||
@@ -1276,7 +1276,7 @@ preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
|
||||
if start.file != nil {
|
||||
dir = filepath.dir(start.file.name)
|
||||
}
|
||||
path := filepath.join(dir, filename)
|
||||
path := filepath.join({dir, filename})
|
||||
if os.exists(path) {
|
||||
tok = include_file(cpp, tok, path, start.next.next)
|
||||
continue
|
||||
|
||||
+11
-11
@@ -211,19 +211,19 @@ _signbitf :: #force_inline proc(x: float) -> int {
|
||||
return int(transmute(uint32_t)x >> 31)
|
||||
}
|
||||
|
||||
isfinite :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isfinite :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_INFINITE
|
||||
}
|
||||
|
||||
isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isinf :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) > FP_INFINITE
|
||||
}
|
||||
|
||||
isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnan :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NAN
|
||||
}
|
||||
|
||||
isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnormal :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NORMAL
|
||||
}
|
||||
|
||||
@@ -231,27 +231,27 @@ isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
// implemented as the relational comparisons, as that would produce an invalid
|
||||
// "sticky" state that propagates and affects maths results. These need
|
||||
// to be implemented natively in Odin assuming isunordered to prevent that.
|
||||
isgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x > y
|
||||
}
|
||||
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x >= y
|
||||
}
|
||||
|
||||
isless :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isless :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x < y
|
||||
}
|
||||
|
||||
islessequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
islessgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
isunordered :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isunordered :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
if isnan(x) {
|
||||
// Force evaluation of y to propagate exceptions for ordering semantics.
|
||||
// To ensure correct semantics of IEEE 754 this cannot be compiled away.
|
||||
@@ -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}
|
||||
|
||||
@@ -196,7 +196,7 @@ foreign libc {
|
||||
getc :: proc(stream: ^FILE) -> int ---
|
||||
getchar :: proc() -> int ---
|
||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
putchar :: proc() -> int ---
|
||||
putchar :: proc(c: int) -> int ---
|
||||
puts :: proc(s: cstring) -> int ---
|
||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||
|
||||
@@ -8,7 +8,7 @@ tss_dtor_t :: proc "c" (rawptr)
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:msvcprt.lib"
|
||||
"system:msvcprt.lib",
|
||||
}
|
||||
|
||||
thrd_success :: 0 // _Thrd_success
|
||||
@@ -77,7 +77,7 @@ when ODIN_OS == .Windows {
|
||||
when ODIN_OS == .Linux {
|
||||
foreign import libc {
|
||||
"system:c",
|
||||
"system:pthread"
|
||||
"system:pthread",
|
||||
}
|
||||
|
||||
thrd_success :: 0
|
||||
|
||||
@@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size + offset {
|
||||
buf := z.input_data[offset:][:size]
|
||||
return (^T)(&buf[0])^, .None
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF
|
||||
} else {
|
||||
return T{}, .Short_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
@@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
// Get current position to return to.
|
||||
cur_pos, e1 := z.input->impl_seek(0, .Current)
|
||||
if e1 != .None {
|
||||
return T{}, e1
|
||||
}
|
||||
|
||||
// Seek to offset.
|
||||
pos, e2 := z.input->impl_seek(offset, .Start)
|
||||
if e2 != .None {
|
||||
return T{}, e2
|
||||
}
|
||||
|
||||
r, e3 := io.to_reader_at(z.input)
|
||||
if !e3 {
|
||||
return T{}, .Empty
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
}
|
||||
_, e4 := io.read_at(r, b[:], pos)
|
||||
if e4 != .None {
|
||||
return T{}, .Empty
|
||||
}
|
||||
|
||||
// Return read head to original position.
|
||||
z.input->impl_seek(cur_pos, .Start)
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ main :: proc() {
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
|
||||
@@ -102,7 +102,7 @@ E_Deflate :: compress.Deflate_Error
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
|
||||
|
||||
load :: proc{load_from_slice, load_from_file, load_from_context}
|
||||
load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
@@ -112,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_slice(data, buf, len(data), expected_output_size)
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
output = buf,
|
||||
}
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
||||
|
||||
@@ -185,6 +185,33 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||
- index: The bit index. Can be an enum member.
|
||||
|
||||
Out:
|
||||
- ok: Whether or not we managed to unset requested bit.
|
||||
|
||||
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||
*/
|
||||
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
idx := int(index) - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false }
|
||||
context.allocator = allocator
|
||||
|
||||
leg_index := idx >> INDEX_SHIFT
|
||||
bit_index := idx & INDEX_MASK
|
||||
|
||||
resize_if_needed(ba, leg_index) or_return
|
||||
|
||||
ba.max_index = max(idx, ba.max_index)
|
||||
ba.bits[leg_index] &= ~(1 << uint(bit_index))
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
*/
|
||||
|
||||
@@ -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]) {
|
||||
|
||||
@@ -69,6 +69,23 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
@@ -82,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -419,8 +419,10 @@ update :: proc(ctx: ^$T, data: []byte) {
|
||||
sha2_transf(ctx, shifted_message, block_nb)
|
||||
|
||||
rem_len = new_len % CURR_BLOCK_SIZE
|
||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
||||
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||
if rem_len > 0 {
|
||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
||||
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||
}
|
||||
|
||||
ctx.length = rem_len
|
||||
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
package dynlib
|
||||
|
||||
Library :: distinct rawptr
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
return _load_library(path, global_symbols)
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
return _unload_library(library)
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol)
|
||||
}
|
||||
|
||||
+15
-14
@@ -1,23 +1,24 @@
|
||||
// +build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+private
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
return Library(lib), lib != nil
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
return Library(lib), lib != nil
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) {
|
||||
os.dlclose(rawptr(library))
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
return os.dlclose(rawptr(library))
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// +build windows
|
||||
//+build windows
|
||||
//+private
|
||||
package dynlib
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
@@ -12,12 +13,12 @@ load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
return handle, handle != nil
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library)
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
found = ptr != nil
|
||||
|
||||
@@ -34,6 +34,10 @@ Reader :: struct {
|
||||
// If lazy_quotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field
|
||||
lazy_quotes: bool,
|
||||
|
||||
// multiline_fields, when set to true, will treat a field starting with a " as a multiline string
|
||||
// therefore, instead of reading until the next \n, it'll read until the next "
|
||||
multiline_fields: bool,
|
||||
|
||||
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
|
||||
// for performance
|
||||
// By default, each call to 'read' returns a newly allocated slice
|
||||
@@ -194,32 +198,72 @@ is_valid_delim :: proc(r: rune) -> bool {
|
||||
@private
|
||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer)
|
||||
append(&r.raw_buffer, ..line)
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n')
|
||||
if !r.multiline_fields {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer)
|
||||
append(&r.raw_buffer, ..line)
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n')
|
||||
append(&r.raw_buffer, ..line)
|
||||
}
|
||||
line = r.raw_buffer[:]
|
||||
}
|
||||
line = r.raw_buffer[:]
|
||||
}
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1]
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
r.line_count += 1
|
||||
r.line_count += 1
|
||||
|
||||
// normalize \r\n to \n
|
||||
n := len(line)
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n'
|
||||
line = line[:n-1]
|
||||
}
|
||||
// normalize \r\n to \n
|
||||
n := len(line)
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n'
|
||||
line = line[:n-1]
|
||||
}
|
||||
return line, err
|
||||
|
||||
return line, err
|
||||
} else {
|
||||
// Reading a "line" that can possibly contain multiline fields.
|
||||
// Unfortunately, this means we need to read a character at a time.
|
||||
|
||||
err: io.Error
|
||||
cur: rune
|
||||
is_quoted: bool
|
||||
|
||||
field_length := 0
|
||||
|
||||
clear(&r.raw_buffer)
|
||||
|
||||
read_loop: for err == .None {
|
||||
cur, _, err = bufio.reader_read_rune(&r.r)
|
||||
|
||||
if err != .None { break read_loop }
|
||||
|
||||
switch cur {
|
||||
case '"':
|
||||
is_quoted = field_length == 0
|
||||
field_length += 1
|
||||
|
||||
case '\n', '\r':
|
||||
if !is_quoted { break read_loop }
|
||||
|
||||
case r.comma:
|
||||
field_length = 0
|
||||
|
||||
case:
|
||||
field_length += 1
|
||||
}
|
||||
|
||||
rune_buf, rune_len := utf8.encode_rune(cur)
|
||||
append(&r.raw_buffer, ..rune_buf[:rune_len])
|
||||
}
|
||||
|
||||
return r.raw_buffer[:], err
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
length_newline :: proc(b: []byte) -> int {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Package endian implements sa simple translation between bytes and numbers with
|
||||
specific endian encodings.
|
||||
|
||||
buf: [100]u8
|
||||
put_u16(buf[:], .Little, 16) or_return
|
||||
|
||||
You may ask yourself, why isn't `byte_order` platform Endianness by default, so we can write:
|
||||
put_u16(buf[:], 16) or_return
|
||||
|
||||
The answer is that very few file formats are written in native/platform endianness. Most of them specify the endianness of
|
||||
each of their fields, or use a header field which specifies it for the entire file.
|
||||
|
||||
e.g. a file which specifies it at the top for all fields could do this:
|
||||
file_order := .Little if buf[0] == 0 else .Big
|
||||
field := get_u16(buf[1:], file_order) or_return
|
||||
|
||||
If on the other hand a field is *always* Big-Endian, you're wise to explicitly state it for the benefit of the reader,
|
||||
be that your future self or someone else.
|
||||
|
||||
field := get_u16(buf[:], .Big) or_return
|
||||
*/
|
||||
package encoding_endian
|
||||
@@ -0,0 +1,153 @@
|
||||
package encoding_endian
|
||||
|
||||
Byte_Order :: enum u8 {
|
||||
Little,
|
||||
Big,
|
||||
}
|
||||
|
||||
PLATFORM_BYTE_ORDER :: Byte_Order.Little when ODIN_ENDIAN == .Little else Byte_Order.Big
|
||||
|
||||
get_u16 :: proc(b: []byte, order: Byte_Order) -> (v: u16, ok: bool) {
|
||||
if len(b) < 2 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u16(b[0]) | u16(b[1])<<8
|
||||
} else {
|
||||
v = u16(b[1]) | u16(b[0])<<8
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
get_u32 :: proc(b: []byte, order: Byte_Order) -> (v: u32, ok: bool) {
|
||||
if len(b) < 4 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u32(b[0]) | u32(b[1])<<8 | u32(b[2])<<16 | u32(b[3])<<24
|
||||
} else {
|
||||
v = u32(b[3]) | u32(b[2])<<8 | u32(b[1])<<16 | u32(b[0])<<24
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
get_u64 :: proc(b: []byte, order: Byte_Order) -> (v: u64, ok: bool) {
|
||||
if len(b) < 8 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u64(b[0]) | u64(b[1])<<8 | u64(b[2])<<16 | u64(b[3])<<24 |
|
||||
u64(b[4])<<32 | u64(b[5])<<40 | u64(b[6])<<48 | u64(b[7])<<56
|
||||
} else {
|
||||
v = u64(b[7]) | u64(b[6])<<8 | u64(b[5])<<16 | u64(b[4])<<24 |
|
||||
u64(b[3])<<32 | u64(b[2])<<40 | u64(b[1])<<48 | u64(b[0])<<56
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
get_i16 :: proc(b: []byte, order: Byte_Order) -> (i16, bool) {
|
||||
v, ok := get_u16(b, order)
|
||||
return i16(v), ok
|
||||
}
|
||||
get_i32 :: proc(b: []byte, order: Byte_Order) -> (i32, bool) {
|
||||
v, ok := get_u32(b, order)
|
||||
return i32(v), ok
|
||||
}
|
||||
get_i64 :: proc(b: []byte, order: Byte_Order) -> (i64, bool) {
|
||||
v, ok := get_u64(b, order)
|
||||
return i64(v), ok
|
||||
}
|
||||
|
||||
get_f16 :: proc(b: []byte, order: Byte_Order) -> (f16, bool) {
|
||||
v, ok := get_u16(b, order)
|
||||
return transmute(f16)v, ok
|
||||
}
|
||||
get_f32 :: proc(b: []byte, order: Byte_Order) -> (f32, bool) {
|
||||
v, ok := get_u32(b, order)
|
||||
return transmute(f32)v, ok
|
||||
}
|
||||
get_f64 :: proc(b: []byte, order: Byte_Order) -> (f64, bool) {
|
||||
v, ok := get_u64(b, order)
|
||||
return transmute(f64)v, ok
|
||||
}
|
||||
|
||||
|
||||
put_u16 :: proc(b: []byte, order: Byte_Order, v: u16) -> bool {
|
||||
if len(b) < 2 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
} else {
|
||||
b[0] = byte(v >> 8)
|
||||
b[1] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
put_u32 :: proc(b: []byte, order: Byte_Order, v: u32) -> bool {
|
||||
if len(b) < 4 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
} else {
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
put_u64 :: proc(b: []byte, order: Byte_Order, v: u64) -> bool {
|
||||
if len(b) < 8 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v >> 0)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
} else {
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
put_i16 :: proc(b: []byte, order: Byte_Order, v: i16) -> bool {
|
||||
return put_u16(b, order, u16(v))
|
||||
}
|
||||
|
||||
put_i32 :: proc(b: []byte, order: Byte_Order, v: i32) -> bool {
|
||||
return put_u32(b, order, u32(v))
|
||||
}
|
||||
|
||||
put_i64 :: proc(b: []byte, order: Byte_Order, v: i64) -> bool {
|
||||
return put_u64(b, order, u64(v))
|
||||
}
|
||||
|
||||
|
||||
put_f16 :: proc(b: []byte, order: Byte_Order, v: f16) -> bool {
|
||||
return put_u16(b, order, transmute(u16)v)
|
||||
}
|
||||
|
||||
put_f32 :: proc(b: []byte, order: Byte_Order, v: f32) -> bool {
|
||||
return put_u32(b, order, transmute(u32)v)
|
||||
}
|
||||
|
||||
put_f64 :: proc(b: []byte, order: Byte_Order, v: f64) -> bool {
|
||||
return put_u64(b, order, transmute(u64)v)
|
||||
}
|
||||
@@ -25,8 +25,8 @@ import "core:strings"
|
||||
|
||||
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
|
||||
|
||||
write_rune :: strings.write_rune_builder
|
||||
write_string :: strings.write_string_builder
|
||||
write_rune :: strings.write_rune
|
||||
write_string :: strings.write_string
|
||||
|
||||
Error :: enum u8 {
|
||||
None = 0,
|
||||
@@ -94,8 +94,8 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
l := len(input)
|
||||
if l == 0 { return "", .None }
|
||||
|
||||
builder := strings.make_builder()
|
||||
defer strings.destroy_builder(&builder)
|
||||
builder := strings.builder_make()
|
||||
defer strings.builder_destroy(&builder)
|
||||
|
||||
t := Tokenizer{src=input}
|
||||
in_data := false
|
||||
@@ -231,16 +231,16 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
for len(entity) > 0 {
|
||||
r := entity[0]
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
val *= base
|
||||
val += int(r - '0')
|
||||
|
||||
case 'a'..'f':
|
||||
case 'a'..='f':
|
||||
if base == 10 { return -1, false }
|
||||
val *= base
|
||||
val += int(r - 'a' + 10)
|
||||
|
||||
case 'A'..'F':
|
||||
case 'A'..='F':
|
||||
if base == 10 { return -1, false }
|
||||
val *= base
|
||||
val += int(r - 'A' + 10)
|
||||
|
||||
@@ -8,7 +8,7 @@ import "core:time"
|
||||
|
||||
doc_print :: proc(doc: ^xml.Document) {
|
||||
buf: strings.Builder
|
||||
defer strings.destroy_builder(&buf)
|
||||
defer strings.builder_destroy(&buf)
|
||||
w := strings.to_writer(&buf)
|
||||
|
||||
xml.print(w, doc)
|
||||
|
||||
@@ -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!
|
||||
*/
|
||||
|
||||
|
||||
+218
-43
@@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.make_builder(allocator)
|
||||
// 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.destroy_builder(&b)
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
marshal_to_builder(&b, v) or_return
|
||||
opt := opt
|
||||
marshal_to_builder(&b, v, &opt) or_return
|
||||
|
||||
if len(b.buf) != 0 {
|
||||
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
|
||||
@@ -49,13 +78,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
unreachable()
|
||||
|
||||
case runtime.Type_Info_Integer:
|
||||
buf: [21]byte
|
||||
buf: [40]byte
|
||||
u: u128
|
||||
switch i in a {
|
||||
case i8: u = u128(i)
|
||||
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 +112,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case u128be: u = u128(i)
|
||||
}
|
||||
|
||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s: string
|
||||
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
|
||||
|
||||
@@ -147,6 +191,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Procedure:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -166,52 +213,48 @@ 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 {
|
||||
return .Unsupported_Type
|
||||
@@ -223,31 +266,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
entry_size := ed.elem_size
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := rawptr(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
|
||||
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
|
||||
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 +332,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 +392,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
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
||||
return parse_object(&p)
|
||||
case .JSON5:
|
||||
return parse_value(&p)
|
||||
case .MJSON:
|
||||
case .SJSON:
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident, .String:
|
||||
return parse_object_body(&p, .EOF)
|
||||
|
||||
@@ -33,8 +33,9 @@ package json
|
||||
Specification :: enum {
|
||||
JSON,
|
||||
JSON5, // https://json5.org/
|
||||
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||
Bitsquid = MJSON,
|
||||
SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||
Bitsquid = SJSON,
|
||||
MJSON = SJSON,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
variant := u.variants[0]
|
||||
v.id = variant.id
|
||||
ti = reflect.type_info_base(variant)
|
||||
if !(u.maybe && reflect.is_pointer(variant)) {
|
||||
if !reflect.is_pointer_internally(variant) {
|
||||
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||
assign_int(tag, 1)
|
||||
}
|
||||
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
||||
|
||||
if end_token == .Close_Brace {
|
||||
assert(expect_token(p, .Open_Brace) == nil)
|
||||
unmarshal_expect_token(p, .Open_Brace)
|
||||
}
|
||||
|
||||
v := v
|
||||
@@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
|
||||
if end_token == .Close_Brace {
|
||||
assert(expect_token(p, .Close_Brace) == nil)
|
||||
unmarshal_expect_token(p, .Close_Brace)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ package varint
|
||||
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
|
||||
// Instead we'll set limits on the values we'll encode/decode
|
||||
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
|
||||
LEB128_MAX_BYTES :: 19
|
||||
LEB128_MAX_BYTES :: 19
|
||||
|
||||
Error :: enum {
|
||||
None = 0,
|
||||
@@ -132,14 +132,12 @@ encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
SIGN_MASK :: (i128(1) << 121) // sign extend mask
|
||||
|
||||
// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
|
||||
// `buf` must be appropriately sized.
|
||||
encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
|
||||
val := val
|
||||
more := true
|
||||
SIGN_MASK :: i128(1) << 121 // sign extend mask
|
||||
|
||||
val, more := val, true
|
||||
|
||||
for more {
|
||||
size += 1
|
||||
|
||||
@@ -23,7 +23,7 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
||||
|
||||
written += wprintf(writer, "[XML Prolog]\n")
|
||||
|
||||
for attr in doc.prolog {
|
||||
for attr in doc.prologue {
|
||||
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ example :: proc() {
|
||||
times[round] = time.tick_diff(start, end)
|
||||
}
|
||||
|
||||
fastest := time.Duration(max(i64))
|
||||
fastest := max(time.Duration)
|
||||
slowest := time.Duration(0)
|
||||
total := time.Duration(0)
|
||||
|
||||
@@ -84,7 +84,7 @@ example :: proc() {
|
||||
|
||||
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
|
||||
buf: strings.Builder
|
||||
defer strings.destroy_builder(&buf)
|
||||
defer strings.builder_destroy(&buf)
|
||||
w := strings.to_writer(&buf)
|
||||
|
||||
xml.print(w, doc)
|
||||
|
||||
@@ -198,7 +198,7 @@ is_valid_identifier_rune :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '_', '-', ':': return true
|
||||
case 'A'..='Z', 'a'..='z': return true
|
||||
case '0'..'9': return true
|
||||
case '0'..='9': return true
|
||||
case -1: return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ import "core:strings"
|
||||
|
||||
likely :: intrinsics.expect
|
||||
|
||||
DEFAULT_Options :: Options{
|
||||
flags = {
|
||||
.Ignore_Unsupported,
|
||||
},
|
||||
DEFAULT_OPTIONS :: Options{
|
||||
flags = {.Ignore_Unsupported},
|
||||
expected_doctype = "",
|
||||
}
|
||||
|
||||
@@ -51,7 +49,7 @@ Option_Flag :: enum {
|
||||
Input_May_Be_Modified,
|
||||
|
||||
/*
|
||||
Document MUST start with `<?xml` prolog.
|
||||
Document MUST start with `<?xml` prologue.
|
||||
*/
|
||||
Must_Have_Prolog,
|
||||
|
||||
@@ -94,7 +92,7 @@ Document :: struct {
|
||||
elements: [dynamic]Element,
|
||||
element_count: Element_ID,
|
||||
|
||||
prolog: Attributes,
|
||||
prologue: Attributes,
|
||||
encoding: Encoding,
|
||||
|
||||
doctype: struct {
|
||||
@@ -138,12 +136,12 @@ Element :: struct {
|
||||
children: [dynamic]Element_ID,
|
||||
}
|
||||
|
||||
Attr :: struct {
|
||||
Attribute :: struct {
|
||||
key: string,
|
||||
val: string,
|
||||
}
|
||||
|
||||
Attributes :: [dynamic]Attr
|
||||
Attributes :: [dynamic]Attribute
|
||||
|
||||
Options :: struct {
|
||||
flags: Option_Flags,
|
||||
@@ -221,7 +219,7 @@ Error :: enum {
|
||||
/*
|
||||
Implementation starts here.
|
||||
*/
|
||||
parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
data := data
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -411,10 +409,10 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
#partial switch next.kind {
|
||||
case .Ident:
|
||||
if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
|
||||
parse_prolog(doc) or_return
|
||||
} else if len(doc.prolog) > 0 {
|
||||
parse_prologue(doc) or_return
|
||||
} else if len(doc.prologue) > 0 {
|
||||
/*
|
||||
We've already seen a prolog.
|
||||
We've already seen a prologue.
|
||||
*/
|
||||
return doc, .Too_Many_Prologs
|
||||
} else {
|
||||
@@ -481,7 +479,7 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
}
|
||||
}
|
||||
|
||||
if .Must_Have_Prolog in opts.flags && len(doc.prolog) == 0 {
|
||||
if .Must_Have_Prolog in opts.flags && len(doc.prologue) == 0 {
|
||||
return doc, .No_Prolog
|
||||
}
|
||||
|
||||
@@ -493,16 +491,16 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
return doc, .None
|
||||
}
|
||||
|
||||
parse_from_string :: proc(data: string, options := DEFAULT_Options, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
parse_string :: proc(data: string, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
_data := transmute([]u8)data
|
||||
|
||||
return parse_from_slice(_data, options, path, error_handler, allocator)
|
||||
return parse_bytes(_data, options, path, error_handler, allocator)
|
||||
}
|
||||
|
||||
parse :: proc { parse_from_string, parse_from_slice }
|
||||
parse :: proc { parse_string, parse_bytes }
|
||||
|
||||
// Load an XML file
|
||||
load_from_file :: proc(filename: string, options := DEFAULT_Options, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
@@ -511,7 +509,7 @@ load_from_file :: proc(filename: string, options := DEFAULT_Options, error_handl
|
||||
|
||||
options.flags += { .Input_May_Be_Modified }
|
||||
|
||||
return parse_from_slice(data, options, filename, error_handler, allocator)
|
||||
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||
}
|
||||
|
||||
destroy :: proc(doc: ^Document) {
|
||||
@@ -523,7 +521,7 @@ destroy :: proc(doc: ^Document) {
|
||||
}
|
||||
delete(doc.elements)
|
||||
|
||||
delete(doc.prolog)
|
||||
delete(doc.prologue)
|
||||
delete(doc.comments)
|
||||
delete(doc.input)
|
||||
|
||||
@@ -556,7 +554,7 @@ expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
|
||||
return tok, .Unexpected_Token
|
||||
}
|
||||
|
||||
parse_attribute :: proc(doc: ^Document) -> (attr: Attr, offset: int, err: Error) {
|
||||
parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: Error) {
|
||||
assert(doc != nil)
|
||||
context.allocator = doc.allocator
|
||||
t := doc.tokenizer
|
||||
@@ -574,7 +572,7 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attr, offset: int, err: Error)
|
||||
return
|
||||
}
|
||||
|
||||
check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attr, offset: int) -> (err: Error) {
|
||||
check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attribute, offset: int) -> (err: Error) {
|
||||
for a in attribs {
|
||||
if attr.key == a.key {
|
||||
error(t, offset, "Duplicate attribute: %v\n", attr.key)
|
||||
@@ -598,21 +596,21 @@ parse_attributes :: proc(doc: ^Document, attribs: ^Attributes) -> (err: Error) {
|
||||
return .None
|
||||
}
|
||||
|
||||
parse_prolog :: proc(doc: ^Document) -> (err: Error) {
|
||||
parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
||||
assert(doc != nil)
|
||||
context.allocator = doc.allocator
|
||||
t := doc.tokenizer
|
||||
|
||||
offset := t.offset
|
||||
parse_attributes(doc, &doc.prolog) or_return
|
||||
parse_attributes(doc, &doc.prologue) or_return
|
||||
|
||||
for attr in doc.prolog {
|
||||
for attr in doc.prologue {
|
||||
switch attr.key {
|
||||
case "version":
|
||||
switch attr.val {
|
||||
case "1.0", "1.1":
|
||||
case:
|
||||
error(t, offset, "[parse_prolog] Warning: Unhandled XML version: %v\n", attr.val)
|
||||
error(t, offset, "[parse_prologue] Warning: Unhandled XML version: %v\n", attr.val)
|
||||
}
|
||||
|
||||
case "encoding":
|
||||
@@ -627,7 +625,7 @@ parse_prolog :: proc(doc: ^Document) -> (err: Error) {
|
||||
/*
|
||||
Unrecognized encoding, assume UTF-8.
|
||||
*/
|
||||
error(t, offset, "[parse_prolog] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||
}
|
||||
|
||||
case:
|
||||
|
||||
+652
-493
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,8 +1,8 @@
|
||||
package hash
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
||||
result := u64(seed)
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
result = seed
|
||||
#no_bounds_check for b in data {
|
||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
length := len(data)
|
||||
|
||||
for length != 0 && uintptr(buffer) & 7 != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||
buffer = buffer[1:]
|
||||
length -= 1
|
||||
}
|
||||
|
||||
@@ -28,14 +28,14 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc32_table[1][buf[6]] ~
|
||||
crc32_table[0][buf[7]]
|
||||
|
||||
buffer = intrinsics.ptr_offset(buffer, 8)
|
||||
buffer = buffer[8:]
|
||||
length -= 8
|
||||
}
|
||||
|
||||
|
||||
for length != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||
buffer = buffer[1:]
|
||||
length -= 1
|
||||
}
|
||||
|
||||
|
||||
+104
-98
@@ -15,7 +15,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
||||
a = (a + u64(buf[0]))
|
||||
b = (b + a)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
buffer = buffer[1:]
|
||||
buf = buf[1:]
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
h1: u32 = seed
|
||||
nblocks := len(data)/4
|
||||
p := raw_data(data)
|
||||
p1 := mem.ptr_offset(p, 4*nblocks)
|
||||
p1 := p[4*nblocks:]
|
||||
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
for ; p < p1; p = p[4:] {
|
||||
k1 := (cast(^u32)p)^
|
||||
|
||||
k1 *= c1_32
|
||||
@@ -172,108 +172,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
return h1
|
||||
}
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||
@(optimization_mode="speed")
|
||||
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
|
||||
h: u64 = seed ~ (u64(len(data)) * m)
|
||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
|
||||
h: u64 = seed ~ (u64(len(data)) * m)
|
||||
data64 := mem.slice_data_cast([]u64, data)
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i]
|
||||
for _, i in data64 {
|
||||
k := data64[i]
|
||||
|
||||
k *= m
|
||||
k ~= k>>r
|
||||
k *= m
|
||||
k *= m
|
||||
k ~= k>>r
|
||||
k *= m
|
||||
|
||||
h ~= k
|
||||
h *= m
|
||||
}
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough
|
||||
case 1:
|
||||
h ~= u64(data[0])
|
||||
h *= m
|
||||
}
|
||||
|
||||
h ~= h>>r
|
||||
h ~= k
|
||||
h *= m
|
||||
h ~= h>>r
|
||||
|
||||
return h
|
||||
} else {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
h1 := u32(seed) ~ u32(len(data))
|
||||
h2 := u32(seed) >> 32
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||
len := len(data)
|
||||
i := 0
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
|
||||
k2 = data32[i]; i += 1
|
||||
k2 *= m
|
||||
k2 ~= k2>>r
|
||||
k2 *= m
|
||||
h2 *= m
|
||||
h2 ~= k2
|
||||
len -= 4
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
h2 ~= u32(data8[0])
|
||||
h2 *= m
|
||||
}
|
||||
|
||||
h1 ~= h2>>18
|
||||
h1 *= m
|
||||
h2 ~= h1>>22
|
||||
h2 *= m
|
||||
h1 ~= h2>>17
|
||||
h1 *= m
|
||||
h2 ~= h1>>19
|
||||
h2 *= m
|
||||
|
||||
return u64(h1)<<32 | u64(h2)
|
||||
}
|
||||
|
||||
offset := len(data64) * size_of(u64)
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
|
||||
case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
|
||||
case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
|
||||
case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
|
||||
case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
|
||||
case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
|
||||
case 1:
|
||||
h ~= u64(data[offset + 0])
|
||||
h *= m
|
||||
}
|
||||
|
||||
h ~= h>>r
|
||||
h *= m
|
||||
h ~= h>>r
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||
@(optimization_mode="speed")
|
||||
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
h1 := u32(seed) ~ u32(len(data))
|
||||
h2 := u32(seed) >> 32
|
||||
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||
len := len(data)
|
||||
i := 0
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
|
||||
k2 = data32[i]; i += 1
|
||||
k2 *= m
|
||||
k2 ~= k2>>r
|
||||
k2 *= m
|
||||
h2 *= m
|
||||
h2 ~= k2
|
||||
len -= 4
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
h2 ~= u32(data8[0])
|
||||
h2 *= m
|
||||
}
|
||||
|
||||
h1 ~= h2>>18
|
||||
h1 *= m
|
||||
h2 ~= h1>>22
|
||||
h2 *= m
|
||||
h1 ~= h2>>17
|
||||
h1 *= m
|
||||
h2 ~= h1>>19
|
||||
h2 *= m
|
||||
|
||||
return u64(h1)<<32 | u64(h2)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
|
||||
@@ -211,7 +211,9 @@ XXH3_update :: #force_inline proc(
|
||||
length := len(input)
|
||||
secret := state.custom_secret[:] if len(state.external_secret) == 0 else state.external_secret[:]
|
||||
|
||||
assert(len(input) > 0)
|
||||
if len(input) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
state.total_length += u64(length)
|
||||
assert(state.buffered_size <= XXH3_INTERNAL_BUFFER_SIZE)
|
||||
|
||||
@@ -197,6 +197,7 @@ XXH32 :: proc(input: []u8, seed := XXH32_DEFAULT_SEED) -> (digest: XXH32_hash) {
|
||||
*/
|
||||
XXH32_create_state :: proc(allocator := context.allocator) -> (res: ^XXH32_state, err: Error) {
|
||||
state := new(XXH32_state, allocator)
|
||||
XXH32_reset_state(state)
|
||||
return state, .None if state != nil else .Error
|
||||
}
|
||||
|
||||
@@ -258,7 +259,7 @@ XXH32_update :: proc(state: ^XXH32_state, input: []u8) -> (err: Error) {
|
||||
v3 := state.v3
|
||||
v4 := state.v4
|
||||
|
||||
for len(buf) >= 15 {
|
||||
for len(buf) >= 16 {
|
||||
#no_bounds_check v1 = XXH32_round(v1, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
|
||||
#no_bounds_check v2 = XXH32_round(v2, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
|
||||
#no_bounds_check v3 = XXH32_round(v3, XXH32_read32(buf, .Unaligned)); buf = buf[4:]
|
||||
|
||||
@@ -163,6 +163,7 @@ XXH64 :: proc(input: []u8, seed := XXH64_DEFAULT_SEED) -> (digest: XXH64_hash) {
|
||||
*/
|
||||
XXH64_create_state :: proc(allocator := context.allocator) -> (res: ^XXH64_state, err: Error) {
|
||||
state := new(XXH64_state, allocator)
|
||||
XXH64_reset_state(state)
|
||||
return state, .None if state != nil else .Error
|
||||
}
|
||||
|
||||
|
||||
+68
-8
@@ -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
|
||||
@@ -54,12 +54,14 @@ Image :: struct {
|
||||
*/
|
||||
background: Maybe(RGB_Pixel_16),
|
||||
metadata: Image_Metadata,
|
||||
which: Which_File_Type,
|
||||
}
|
||||
|
||||
Image_Metadata :: union {
|
||||
Image_Metadata :: union #shared_nil {
|
||||
^Netpbm_Info,
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
^TGA_Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -167,11 +169,13 @@ Error :: union #shared_nil {
|
||||
|
||||
General_Image_Error :: enum {
|
||||
None = 0,
|
||||
Unsupported_Option,
|
||||
// File I/O
|
||||
Unable_To_Read_File,
|
||||
Unable_To_Write_File,
|
||||
|
||||
// Invalid
|
||||
Unsupported_Format,
|
||||
Invalid_Signature,
|
||||
Invalid_Input_Image,
|
||||
Image_Dimensions_Too_Large,
|
||||
@@ -374,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,
|
||||
@@ -388,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
|
||||
@@ -467,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 {
|
||||
@@ -487,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
|
||||
}
|
||||
@@ -496,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 {
|
||||
@@ -516,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
|
||||
}
|
||||
@@ -525,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 {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package image
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:bytes"
|
||||
|
||||
Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
|
||||
Destroy_Proc :: #type proc(img: ^Image)
|
||||
|
||||
@(private)
|
||||
_internal_loaders: [Which_File_Type]Loader_Proc
|
||||
_internal_destroyers: [Which_File_Type]Destroy_Proc
|
||||
|
||||
register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
|
||||
assert(loader != nil)
|
||||
assert(destroyer != nil)
|
||||
assert(_internal_loaders[kind] == nil)
|
||||
_internal_loaders[kind] = loader
|
||||
|
||||
assert(_internal_destroyers[kind] == nil)
|
||||
_internal_destroyers[kind] = destroyer
|
||||
}
|
||||
|
||||
load :: proc{
|
||||
load_from_bytes,
|
||||
load_from_file,
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
loader := _internal_loaders[which(data)]
|
||||
if loader == nil {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
return loader(data, options, allocator)
|
||||
}
|
||||
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
data, ok := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
if ok {
|
||||
return load_from_bytes(data, options, allocator)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
destroy :: proc(img: ^Image, allocator := context.allocator) {
|
||||
if img == nil {
|
||||
return
|
||||
}
|
||||
context.allocator = allocator
|
||||
destroyer := _internal_destroyers[img.which]
|
||||
if destroyer != nil {
|
||||
destroyer(img)
|
||||
} else {
|
||||
assert(img.metadata == nil)
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
free(img)
|
||||
}
|
||||
}
|
||||
@@ -6,24 +6,25 @@ Formats:
|
||||
PAM (P7 ): Portable Arbitrary Map, stores arbitrary channel images (1 or 2 bytes per value)
|
||||
PFM (Pf, PF): Portable Float Map, stores floating-point images (Pf: 1 channel, PF: 3 channel)
|
||||
|
||||
Reading
|
||||
Reading:
|
||||
All formats fill out header fields `format`, `width`, `height`, `channels`, `depth`
|
||||
Specific formats use more fields
|
||||
PGM, PPM, and PAM set `maxval`
|
||||
PAM also sets `tupltype`, and is able to set `channels` to an arbitrary value
|
||||
PFM sets `scale` and `little_endian`
|
||||
PGM, PPM, and PAM set `maxval` (maximum of 65535)
|
||||
PAM sets `tupltype` if there is one, and can set `channels` to any value (not just 1 or 3)
|
||||
PFM sets `scale` (float equivalent of `maxval`) and `little_endian` (endianness of stored floats)
|
||||
Currently doesn't support reading multiple images from one binary-format file
|
||||
|
||||
Writing
|
||||
Writing:
|
||||
You can use your own `Netpbm_Info` struct to control how images are written
|
||||
All formats require the header field `format` to be specified
|
||||
Additional header fields are required for specific formats
|
||||
PGM, PPM, and PAM require `maxval`
|
||||
PGM, PPM, and PAM require `maxval` (maximum of 65535)
|
||||
PAM also uses `tupltype`, though it may be left as default (empty or nil string)
|
||||
PFM requires `scale` and `little_endian`, though the latter may be left untouched (default is false)
|
||||
PFM requires `scale`, and optionally `little_endian`
|
||||
|
||||
Some syntax differences from the specifications:
|
||||
`channels` stores what the PAM specification calls `depth`
|
||||
`depth` instead stores how many bytes will fit `maxval` (should only be 1, 2, or 4)
|
||||
`channels` stores the number of values per pixel, what the PAM specification calls `depth`
|
||||
`depth` instead is the number of bits for a single value (32 for PFM, 16 or 8 otherwise)
|
||||
`scale` and `little_endian` are separated, so the `header` will always store a positive `scale`
|
||||
`little_endian` will only be true for a negative `scale` PFM, every other format will be false
|
||||
`little_endian` only describes the netpbm data being read/written, the image buffer will be native
|
||||
|
||||
@@ -28,7 +28,7 @@ BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM
|
||||
|
||||
load :: proc {
|
||||
load_from_file,
|
||||
load_from_buffer,
|
||||
load_from_bytes,
|
||||
}
|
||||
|
||||
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
@@ -40,13 +40,14 @@ load_from_file :: proc(filename: string, allocator := context.allocator) -> (img
|
||||
return
|
||||
}
|
||||
|
||||
return load_from_buffer(data)
|
||||
return load_from_bytes(data)
|
||||
}
|
||||
|
||||
load_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
img = new(Image)
|
||||
img.which = .NetPBM
|
||||
|
||||
header: Header; defer header_destroy(&header)
|
||||
header_size: int
|
||||
@@ -109,7 +110,7 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
||||
// using info so we can just talk about the header
|
||||
using info
|
||||
|
||||
//? validation
|
||||
// validation
|
||||
if header.format in (PBM + PGM + Formats{.Pf}) && img.channels != 1 \
|
||||
|| header.format in (PPM + Formats{.PF}) && img.channels != 3 {
|
||||
err = .Invalid_Number_Of_Channels
|
||||
@@ -129,7 +130,7 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
||||
|
||||
// we will write to a string builder
|
||||
data: strings.Builder
|
||||
strings.init_builder(&data)
|
||||
strings.builder_init(&data)
|
||||
|
||||
// all PNM headers start with the format
|
||||
fmt.sbprintf(&data, "%s\n", header.format)
|
||||
@@ -408,7 +409,7 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
|
||||
|
||||
// string buffer for the tupltype
|
||||
tupltype: strings.Builder
|
||||
strings.init_builder(&tupltype, context.temp_allocator); defer strings.destroy_builder(&tupltype)
|
||||
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
|
||||
fmt.sbprint(&tupltype, "")
|
||||
|
||||
// PAM uses actual lines, so we can iterate easily
|
||||
@@ -748,4 +749,15 @@ autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, for
|
||||
|
||||
// We couldn't find a suitable format
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) {
|
||||
return load_from_bytes(data, allocator)
|
||||
}
|
||||
destroyer :: proc(img: ^Image) {
|
||||
_ = destroy(img)
|
||||
}
|
||||
image.register(.NetPBM, loader, destroyer)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
+16
-12
@@ -18,7 +18,6 @@ import "core:compress/zlib"
|
||||
import "core:image"
|
||||
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:hash"
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
@@ -318,13 +317,12 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
|
||||
}
|
||||
|
||||
chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string {
|
||||
t := transmute(^u8)type
|
||||
return strings.string_from_ptr(t, 4)
|
||||
return string(([^]u8)(type)[:4])
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -344,10 +342,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_slice(data, options)
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
img = new(Image)
|
||||
return img, .Unable_To_Read_File
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +372,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
img.which = .PNG
|
||||
|
||||
info := new(image.PNG_Info)
|
||||
img.metadata = info
|
||||
@@ -1004,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o16 = o16[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1197,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o = o[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1208,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw
|
||||
returns will likely bypass this processing pipeline.
|
||||
*/
|
||||
unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
panic("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
}
|
||||
|
||||
return img, nil
|
||||
@@ -1639,4 +1637,10 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
|
||||
return nil
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_slice, load_from_context}
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.PNG, load_from_bytes, destroy)
|
||||
}
|
||||
+11
-6
@@ -180,9 +180,9 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
@@ -196,10 +196,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_slice(data, options)
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
img = new(Image)
|
||||
return img, .Unable_To_Read_File
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +224,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
img.which = .QOI
|
||||
|
||||
if .return_metadata in options {
|
||||
info := new(image.QOI_Info)
|
||||
@@ -359,7 +359,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
return
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_slice, load_from_context}
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
/*
|
||||
Cleanup of image-specific data.
|
||||
@@ -403,4 +403,9 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
|
||||
i4 := u16(pixel.a) * 11
|
||||
|
||||
return u8((i1 + i2 + i3 + i4) & 63)
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.QOI, load_from_bytes, destroy)
|
||||
}
|
||||
+341
-3
@@ -4,6 +4,7 @@
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Benoit Jacquier: tga loader
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,14 +13,18 @@ package tga
|
||||
|
||||
import "core:mem"
|
||||
import "core:image"
|
||||
import "core:compress"
|
||||
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
|
||||
|
||||
@@ -58,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.
|
||||
@@ -99,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)
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package image
|
||||
|
||||
import "core:os"
|
||||
|
||||
Which_File_Type :: enum {
|
||||
Unknown,
|
||||
|
||||
BMP,
|
||||
DjVu, // AT&T DjVu file format
|
||||
EXR,
|
||||
FLIF,
|
||||
GIF,
|
||||
HDR, // Radiance RGBE HDR
|
||||
ICNS, // Apple Icon Image
|
||||
JPEG,
|
||||
JPEG_2000,
|
||||
JPEG_XL,
|
||||
NetPBM, // NetPBM family
|
||||
PIC, // Softimage PIC
|
||||
PNG, // Portable Network Graphics
|
||||
PSD, // Photoshop PSD
|
||||
QOI, // Quite Okay Image
|
||||
SGI_RGB, // Silicon Graphics Image RGB file format
|
||||
Sun_Rast, // Sun Raster Graphic
|
||||
TGA, // Targa Truevision
|
||||
TIFF, // Tagged Image File Format
|
||||
WebP,
|
||||
XBM, // X BitMap
|
||||
}
|
||||
|
||||
which :: proc{
|
||||
which_bytes,
|
||||
which_file,
|
||||
}
|
||||
|
||||
which_bytes :: proc(data: []byte) -> Which_File_Type {
|
||||
test_tga :: proc(s: string) -> bool {
|
||||
get8 :: #force_inline proc(s: ^string) -> u8 {
|
||||
v := s[0]
|
||||
s^ = s[1:]
|
||||
return v
|
||||
}
|
||||
get16le :: #force_inline proc(s: ^string) -> u16 {
|
||||
v := u16(s[0]) | u16(s[1])<<16
|
||||
s^ = s[2:]
|
||||
return v
|
||||
}
|
||||
s := s
|
||||
s = s[1:] // skip offset
|
||||
|
||||
color_type := get8(&s)
|
||||
if color_type > 1 {
|
||||
return false
|
||||
}
|
||||
image_type := get8(&s) // image type
|
||||
if color_type == 1 { // Colormap (Paletted) Image
|
||||
if image_type != 1 && image_type != 9 { // color type requires 1 or 9
|
||||
return false
|
||||
}
|
||||
s = s[4:] // skip index of first colormap
|
||||
bpcme := get8(&s) // check bits per colormap entry
|
||||
if bpcme != 8 && bpcme != 15 && bpcme != 16 && bpcme != 24 && bpcme != 32 {
|
||||
return false
|
||||
}
|
||||
s = s[4:] // skip image origin (x, y)
|
||||
} else { // Normal image without colormap
|
||||
if image_type != 2 && image_type != 3 && image_type != 10 && image_type != 11 {
|
||||
return false
|
||||
}
|
||||
s = s[9:] // skip colormap specification
|
||||
}
|
||||
if get16le(&s) < 1 || get16le(&s) < 1 { // test width and height
|
||||
return false
|
||||
}
|
||||
bpp := get8(&s) // bits per pixel
|
||||
if color_type == 1 && bpp != 8 && bpp != 16 {
|
||||
return false
|
||||
}
|
||||
if bpp != 8 && bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
header: [128]byte
|
||||
copy(header[:], data)
|
||||
s := string(header[:])
|
||||
|
||||
switch {
|
||||
case s[:2] == "BM":
|
||||
return .BMP
|
||||
case s[:8] == "AT&TFORM":
|
||||
switch s[12:16] {
|
||||
case "DJVU", "DJVM":
|
||||
return .DjVu
|
||||
}
|
||||
case s[:4] == "\x76\x2f\x31\x01":
|
||||
return .EXR
|
||||
case s[:6] == "GIF87a", s[:6] == "GIF89a":
|
||||
return .GIF
|
||||
case s[6:10] == "JFIF", s[6:10] == "Exif":
|
||||
return .JPEG
|
||||
case s[:3] == "\xff\xd8\xff":
|
||||
switch s[4] {
|
||||
case 0xdb, 0xee, 0xe1, 0xe0:
|
||||
return .JPEG
|
||||
}
|
||||
switch {
|
||||
case s[:12] == "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01":
|
||||
return .JPEG
|
||||
}
|
||||
case s[:4] == "\xff\x4f\xff\x51", s[:12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a":
|
||||
return .JPEG_2000
|
||||
case s[:12] == "\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a":
|
||||
return .JPEG_XL
|
||||
case s[0] == 'P':
|
||||
switch s[2] {
|
||||
case '\t', '\n', '\r':
|
||||
switch s[1] {
|
||||
case '1', '4': // PBM
|
||||
return .NetPBM
|
||||
case '2', '5': // PGM
|
||||
return .NetPBM
|
||||
case '3', '6': // PPM
|
||||
return .NetPBM
|
||||
case '7': // PAM
|
||||
return .NetPBM
|
||||
case 'F', 'f': // PFM
|
||||
return .NetPBM
|
||||
}
|
||||
}
|
||||
case s[:8] == "\x89PNG\r\n\x1a\n":
|
||||
return .PNG
|
||||
case s[:4] == "qoif":
|
||||
return .QOI
|
||||
case s[:2] == "\x01\xda":
|
||||
return .SGI_RGB
|
||||
case s[:4] == "\x59\xA6\x6A\x95":
|
||||
return .Sun_Rast
|
||||
case s[:4] == "MM\x2a\x00", s[:4] == "II\x00\x2A":
|
||||
return .TIFF
|
||||
case s[:4] == "RIFF" && s[8:12] == "WEBP":
|
||||
return .WebP
|
||||
case s[:8] == "#define ":
|
||||
return .XBM
|
||||
|
||||
case s[:11] == "#?RADIANCE\n", s[:7] == "#?RGBE\n":
|
||||
return .HDR
|
||||
case s[:4] == "\x38\x42\x50\x53":
|
||||
return .PSD
|
||||
case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
return .PIC
|
||||
case s[:4] == "\x69\x63\x6e\x73":
|
||||
return .ICNS
|
||||
case s[:4] == "\x46\x4c\x49\x46":
|
||||
return .FLIF
|
||||
case:
|
||||
// More complex formats
|
||||
if test_tga(s) {
|
||||
return .TGA
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return .Unknown
|
||||
}
|
||||
|
||||
|
||||
which_file :: proc(path: string) -> Which_File_Type {
|
||||
f, err := os.open(path)
|
||||
if err != 0 {
|
||||
return .Unknown
|
||||
}
|
||||
header: [128]byte
|
||||
os.read(f, header[:])
|
||||
file_type := which_bytes(header[:])
|
||||
os.close(f)
|
||||
return file_type
|
||||
}
|
||||
+116
-22
@@ -6,12 +6,14 @@ package intrinsics
|
||||
is_package_imported :: proc(package_name: string) -> bool ---
|
||||
|
||||
// Types
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
// Volatile
|
||||
volatile_load :: proc(dst: ^$T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
non_temporal_load :: proc(dst: ^$T) -> T ---
|
||||
non_temporal_store :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
// Trapping
|
||||
debug_trap :: proc() ---
|
||||
@@ -23,18 +25,20 @@ alloca :: proc(size, align: int) -> [^]u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
@@ -87,20 +91,20 @@ atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
// fetch then operator
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_add :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_add_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_sub :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_sub_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_and :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_and_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_nand :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_nand_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_or :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_or_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_xor :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_xor_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_exchange :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_exchange_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
|
||||
@@ -186,10 +190,97 @@ type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawpt
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
// SIMD related
|
||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||
|
||||
// Keeps Odin's Behaviour
|
||||
// (x << y) if y <= mask else 0
|
||||
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
|
||||
// Similar to C's Behaviour
|
||||
// x << (y & mask)
|
||||
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
|
||||
simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_and_not :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_neg :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_abs :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
// Return an unsigned integer of the same size as the input type
|
||||
// NOT A BOOLEAN
|
||||
// element-wise:
|
||||
// false => 0x00...00
|
||||
// true => 0xff...ff
|
||||
simd_lanes_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
|
||||
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
||||
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
||||
|
||||
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_min :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_max :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_and :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_or :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_xor :: proc(a: #simd[N]T) -> T ---
|
||||
|
||||
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
||||
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
// Lane-wise operations
|
||||
simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
// rounding to the nearest integral value; if two values are equally near, rounds to the even one
|
||||
simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
|
||||
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
||||
|
||||
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
||||
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
|
||||
|
||||
// WASM targets only
|
||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||
wasm_memory_size :: proc(index: uintptr) -> int ---
|
||||
|
||||
// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for
|
||||
// A negative value will be blocked forever
|
||||
// Return value:
|
||||
// 0 - indicates that the thread blocked and then was woken up
|
||||
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
||||
// 2 - the thread blocked, but the timeout
|
||||
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
||||
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_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
|
||||
|
||||
// Darwin targets only
|
||||
objc_object :: struct{}
|
||||
@@ -204,6 +295,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() ---
|
||||
+2
-7
@@ -4,7 +4,6 @@
|
||||
package io
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Seek whence values
|
||||
@@ -254,11 +253,7 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
|
||||
return 0, .Empty
|
||||
}
|
||||
|
||||
curr_offset: i64
|
||||
curr_offset, err = r->impl_seek(offset, .Current)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
curr_offset := r->impl_seek(offset, .Current) or_return
|
||||
|
||||
n, err = r->impl_read(p)
|
||||
_, err1 := r->impl_seek(curr_offset, .Start)
|
||||
@@ -552,7 +547,7 @@ _copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, er
|
||||
}
|
||||
}
|
||||
// NOTE(bill): alloca is fine here
|
||||
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size}
|
||||
buf = intrinsics.alloca(size, 2*align_of(rawptr))[:size]
|
||||
}
|
||||
for {
|
||||
nr, er := read(src, buf)
|
||||
|
||||
@@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
|
||||
return
|
||||
}
|
||||
|
||||
// writer append a quoted rune into the byte buffer, return the written size
|
||||
write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
|
||||
_write_byte :: #force_inline proc(w: Writer, c: byte) -> int {
|
||||
err := write_byte(w, c)
|
||||
return 1 if err == nil else 0
|
||||
}
|
||||
|
||||
quote := byte('\'')
|
||||
n += _write_byte(w, quote)
|
||||
buf, width := utf8.encode_rune(r)
|
||||
if width == 1 && r == utf8.RUNE_ERROR {
|
||||
n += _write_byte(w, '\\')
|
||||
n += _write_byte(w, 'x')
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
|
||||
} else {
|
||||
i, _ := write_escaped_rune(w, r, quote)
|
||||
n += i
|
||||
}
|
||||
n += _write_byte(w, quote)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Tee_Reader :: struct {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
h = data.file_handle
|
||||
}
|
||||
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_slice(backing[:])
|
||||
buf := strings.builder_from_bytes(backing[:])
|
||||
|
||||
do_level_header(options, level, &buf)
|
||||
|
||||
@@ -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,107 @@
|
||||
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 .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
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
|
||||
+23
-11
@@ -325,9 +325,9 @@ ease :: proc "contextless" (type: Ease, p: $T) -> T
|
||||
// in case type was invalid
|
||||
return 0
|
||||
}
|
||||
|
||||
Flux_Map :: struct($T: typeid) {
|
||||
values: map[^T]Flux_Tween(T),
|
||||
keys_to_be_deleted: [dynamic]^T,
|
||||
}
|
||||
|
||||
Flux_Tween :: struct($T: typeid) {
|
||||
@@ -353,15 +353,17 @@ Flux_Tween :: struct($T: typeid) {
|
||||
}
|
||||
|
||||
// init flux map to a float type and a wanted cap
|
||||
flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
return {
|
||||
make(map[^T]Flux_Tween(T), cap),
|
||||
values = make(map[^T]Flux_Tween(T), value_capacity),
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// delete map content
|
||||
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
|
||||
delete(flux.values)
|
||||
delete(flux.keys_to_be_deleted)
|
||||
}
|
||||
|
||||
// clear map content, stops all animations
|
||||
@@ -374,8 +376,8 @@ flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
|
||||
// return value can be used to set callbacks
|
||||
flux_to :: proc(
|
||||
flux: ^Flux_Map($T),
|
||||
value: ^f32,
|
||||
goal: f32,
|
||||
value: ^T,
|
||||
goal: T,
|
||||
type: Ease = .Quadratic_Out,
|
||||
duration: time.Duration = time.Second,
|
||||
delay: f64 = 0,
|
||||
@@ -413,6 +415,8 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i
|
||||
// calls callbacks in all stages, when they're filled
|
||||
// deletes tween from the map after completion
|
||||
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
|
||||
clear(&flux.keys_to_be_deleted)
|
||||
|
||||
for key, tween in &flux.values {
|
||||
delay_remainder := f64(0)
|
||||
|
||||
@@ -451,7 +455,8 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
||||
}
|
||||
|
||||
if tween.progress >= 1 {
|
||||
delete_key(&flux.values, key)
|
||||
// append keys to array that will be deleted after the loop
|
||||
append(&flux.keys_to_be_deleted, key)
|
||||
|
||||
if tween.on_complete != nil {
|
||||
tween.on_complete(flux, tween.data)
|
||||
@@ -459,17 +464,24 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop through keys that should be deleted from the map
|
||||
if len(flux.keys_to_be_deleted) != 0 {
|
||||
for key in flux.keys_to_be_deleted {
|
||||
delete_key(&flux.values, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop a specific key inside the map
|
||||
// returns true when it successfully removed the key
|
||||
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
|
||||
if key in flux.values {
|
||||
delete_key(&flux.values, key)
|
||||
return true
|
||||
}
|
||||
if key in flux.values {
|
||||
delete_key(&flux.values, key)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
// returns the amount of time left for the tween animation, if the key exists in the map
|
||||
|
||||
@@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
}
|
||||
|
||||
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
b := n * (2 * dot(n, i))
|
||||
return i - b
|
||||
b := N * (2 * dot(N, I))
|
||||
return I - b
|
||||
}
|
||||
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
dv := dot(n, i)
|
||||
dv := dot(N, I)
|
||||
k := 1 - eta*eta - (1 - dv*dv)
|
||||
a := i * eta
|
||||
b := n * eta*dv*math.sqrt(k)
|
||||
a := I * eta
|
||||
b := N * eta*dv*math.sqrt(k)
|
||||
return (a - b) * E(int(k >= 0))
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -476,21 +476,21 @@ quaternion_angle_axis :: proc{
|
||||
|
||||
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
|
||||
+37
-11
@@ -185,16 +185,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 +614,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
|
||||
@@ -1357,7 +1383,7 @@ atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1 + sqrt(1 - x*x))
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
package rand
|
||||
|
||||
import "core:math"
|
||||
|
||||
float64_uniform :: float64_range
|
||||
float32_uniform :: float32_range
|
||||
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float64(r)
|
||||
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
c = 1-c
|
||||
lo, hi = hi, lo
|
||||
}
|
||||
return lo + (hi - lo) * math.sqrt(u * c)
|
||||
|
||||
}
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float32(r)
|
||||
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
c = 1-c
|
||||
lo, hi = hi, lo
|
||||
}
|
||||
return lo + (hi - lo) * math.sqrt(u * c)
|
||||
}
|
||||
|
||||
|
||||
// Normal/Gaussian Distribution
|
||||
float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return norm_float64(r) * stddev + mean
|
||||
}
|
||||
// Normal/Gaussian Distribution
|
||||
float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev), r))
|
||||
}
|
||||
|
||||
|
||||
// Log Normal Distribution
|
||||
float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev, r))
|
||||
}
|
||||
// Log Normal Distribution
|
||||
float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_normal(f64(mean), f64(stddev), r))
|
||||
}
|
||||
|
||||
|
||||
// Exponential Distribution
|
||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||
// Return values range from
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
|
||||
return - math.ln(1 - float64(r)) / lambda
|
||||
}
|
||||
// Exponential Distribution
|
||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||
// Return values range from
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_exponential(f64(lambda), r))
|
||||
}
|
||||
|
||||
|
||||
// Gamma Distribution (NOT THE GAMMA FUNCTION)
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// math.pow(x, alpha-1) * math.exp(-x / beta)
|
||||
// pdf(x) = --------------------------------------------
|
||||
// math.gamma(alpha) * math.pow(beta, alpha)
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
|
||||
LOG4 :: 1.3862943611198906188344642429163531361510002687205105082413600189
|
||||
SG_MAGIC_CONST :: 2.5040773967762740733732583523868748412194809812852436493487
|
||||
|
||||
switch {
|
||||
case alpha > 1:
|
||||
// R.C.H. Cheng, "The generation of Gamma variables with non-integral shape parameters", Applied Statistics, (1977), 26, No. 1, p71-74
|
||||
|
||||
ainv := math.sqrt(2 * alpha - 1)
|
||||
bbb := alpha - LOG4
|
||||
ccc := alpha + ainv
|
||||
for {
|
||||
u1 := float64(r)
|
||||
if !(1e-7 < u1 && u1 < 0.9999999) {
|
||||
continue
|
||||
}
|
||||
u2 := 1 - float64(r)
|
||||
v := math.ln(u1 / (1 - u1)) / ainv
|
||||
x := alpha * math.exp(v)
|
||||
z := u1 * u1 * u2
|
||||
t := bbb + ccc*v - x
|
||||
if t + SG_MAGIC_CONST - 4.5 * z >= 0 || t >= math.ln(z) {
|
||||
return x * beta
|
||||
}
|
||||
}
|
||||
case alpha == 1:
|
||||
// float64_exponential(1/beta)
|
||||
return -math.ln(1 - float64(r)) * beta
|
||||
case:
|
||||
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
||||
x: f64
|
||||
for {
|
||||
u := float64(r)
|
||||
b := (math.e + alpha) / math.e
|
||||
p := b * u
|
||||
if p <= 1 {
|
||||
x = math.pow(p, 1/alpha)
|
||||
} else {
|
||||
x = -math.ln((b - p) / alpha)
|
||||
}
|
||||
u1 := float64(r)
|
||||
if p > 1 {
|
||||
if u1 <= math.pow(x, alpha-1) {
|
||||
break
|
||||
}
|
||||
} else if u1 <= math.exp(-x) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return x * beta
|
||||
}
|
||||
}
|
||||
// Gamma Distribution (NOT THE GAMMA FUNCTION)
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// math.pow(x, alpha-1) * math.exp(-x / beta)
|
||||
// pdf(x) = --------------------------------------------
|
||||
// math.gamma(alpha) * math.pow(beta, alpha)
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gamma(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Beta Distribution
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
|
||||
y := float64_gamma(alpha, 1.0, r)
|
||||
if y != 0 {
|
||||
return y / (y + float64_gamma(beta, 1.0, r))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// Beta Distribution
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.pow(1 - float64(r), -1.0 / alpha)
|
||||
}
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha), r))
|
||||
}
|
||||
|
||||
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
u := 1 - float64(r)
|
||||
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
||||
}
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_weibull(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Circular Data (von Mises) Distribution
|
||||
// `mean_angle` is the in mean angle between 0 and 2pi radians
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
|
||||
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
|
||||
|
||||
mu := mean_angle
|
||||
if kappa <= 1e-6 {
|
||||
return math.TAU * float64(r)
|
||||
}
|
||||
|
||||
s := 0.5 / kappa
|
||||
t := s + math.sqrt(1 + s*s)
|
||||
z: f64
|
||||
for {
|
||||
u1 := float64(r)
|
||||
z = math.cos(math.TAU * 0.5 * u1)
|
||||
|
||||
d := z / (t + z)
|
||||
u2 := float64(r)
|
||||
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
q := 1.0 / t
|
||||
f := (q + z) / (1 + q*z)
|
||||
u3 := float64(r)
|
||||
if u3 > 0.5 {
|
||||
return math.mod(mu + math.acos(f), math.TAU)
|
||||
} else {
|
||||
return math.mod(mu - math.acos(f), math.TAU)
|
||||
}
|
||||
}
|
||||
// Circular Data (von Mises) Distribution
|
||||
// `mean_angle` is the in mean angle between 0 and 2pi radians
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
|
||||
}
|
||||
|
||||
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(gamma > 0)
|
||||
|
||||
// Calculated from the inverse CDF
|
||||
|
||||
return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
|
||||
}
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
}
|
||||
|
||||
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(gamma > 0)
|
||||
return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
|
||||
}
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
}
|
||||
|
||||
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(b > 0)
|
||||
p := float64(r)-0.5
|
||||
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
||||
}
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_laplace(f64(mean), f64(b), r))
|
||||
}
|
||||
|
||||
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
|
||||
if eta <= 0 || b <= 0 {
|
||||
panic(#procedure + ": eta and b must be > 0.0")
|
||||
}
|
||||
|
||||
p := float64(r)
|
||||
return math.ln(1 - math.ln(1 - p)/eta)/b
|
||||
}
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b), r))
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import "core:intrinsics"
|
||||
Rand :: struct {
|
||||
state: u64,
|
||||
inc: u64,
|
||||
is_system: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) {
|
||||
_random(r)
|
||||
}
|
||||
|
||||
init_as_system :: proc(r: ^Rand) {
|
||||
if !#defined(_system_random) {
|
||||
panic(#procedure + " is not supported on this platform yet")
|
||||
}
|
||||
r.state = 0
|
||||
r.inc = 0
|
||||
r.is_system = true
|
||||
}
|
||||
|
||||
@(private)
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
r := r
|
||||
if r == nil {
|
||||
@@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 {
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand
|
||||
}
|
||||
when #defined(_system_random) {
|
||||
if r.is_system {
|
||||
return _system_random()
|
||||
}
|
||||
}
|
||||
|
||||
old_state := r.state
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
|
||||
@@ -119,13 +136,14 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
// Uniform random distribution [0, 1)
|
||||
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) }
|
||||
// Uniform random distribution [0, 1)
|
||||
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
|
||||
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo }
|
||||
|
||||
|
||||
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
|
||||
pos := i8(0)
|
||||
val := i64(0)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/darwin"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
for {
|
||||
value: u32
|
||||
ret := darwin.syscall_getentropy(([^]u8)(&value), 4)
|
||||
if ret < 0 {
|
||||
switch ret {
|
||||
case -4: // EINTR
|
||||
continue
|
||||
case -78: // ENOSYS
|
||||
panic("getentropy not available in kernel")
|
||||
case:
|
||||
panic("getentropy failed")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
for {
|
||||
value: u32
|
||||
ret := unix.sys_getrandom(([^]u8)(&value), 4, 0)
|
||||
if ret < 0 {
|
||||
switch ret {
|
||||
case -4: // EINTR
|
||||
// Call interupted by a signal handler, just retry the request.
|
||||
continue
|
||||
case -38: // ENOSYS
|
||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
||||
// and does not support getrandom.
|
||||
panic("getrandom not available in kernel")
|
||||
case:
|
||||
// All other failures are things that should NEVER happen
|
||||
// unless the kernel interface changes (ie: the Linux
|
||||
// developers break userland).
|
||||
panic("getrandom failed")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package rand
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
value: u32
|
||||
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
|
||||
if status < 0 {
|
||||
panic("BCryptGenRandom failed")
|
||||
}
|
||||
return value
|
||||
}
|
||||
+14
-85
@@ -55,115 +55,44 @@ Allocator :: struct {
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
||||
|
||||
DEFAULT_PAGE_SIZE ::
|
||||
64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 else
|
||||
16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else
|
||||
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)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
|
||||
return err
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -172,7 +101,7 @@ 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
|
||||
}
|
||||
|
||||
+42
-16
@@ -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
|
||||
@@ -52,15 +60,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
total_size := size + alignment
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
|
||||
total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
@@ -292,6 +301,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
|
||||
@@ -444,27 +461,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 {
|
||||
@@ -662,6 +686,7 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_free_all(pool)
|
||||
delete(unused_blocks)
|
||||
delete(used_blocks)
|
||||
delete(out_band_allocations)
|
||||
|
||||
zero(pool, size_of(pool^))
|
||||
}
|
||||
@@ -746,6 +771,8 @@ dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
|
||||
free(a, block_allocator)
|
||||
}
|
||||
clear(&out_band_allocations)
|
||||
|
||||
bytes_left = 0 // Make new allocations call `cycle_new_block` again.
|
||||
}
|
||||
|
||||
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
|
||||
@@ -855,7 +882,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory not_in data.allocation_map {
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
@@ -883,6 +910,10 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map)
|
||||
}
|
||||
case .Resize:
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
@@ -895,11 +926,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
location = loc,
|
||||
}
|
||||
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map)
|
||||
}
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
|
||||
+12
-45
@@ -25,11 +25,13 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
|
||||
return data
|
||||
}
|
||||
zero_item :: proc "contextless" (item: $P/^$T) {
|
||||
zero_item :: proc "contextless" (item: $P/^$T) -> P {
|
||||
intrinsics.mem_zero(item, size_of(T))
|
||||
return item
|
||||
}
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) {
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) -> T {
|
||||
zero(raw_data(data), size_of(E)*len(data))
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -52,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 {
|
||||
@@ -107,6 +68,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
|
||||
case ptr == nil:
|
||||
return true
|
||||
}
|
||||
switch len {
|
||||
case 1: return (^u8)(ptr)^ == 0
|
||||
case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0
|
||||
case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0
|
||||
case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0
|
||||
}
|
||||
|
||||
start := uintptr(ptr)
|
||||
start_aligned := align_forward_uintptr(start, align_of(uintptr))
|
||||
@@ -150,7 +117,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
return ([^]T)(ptr)[:len]
|
||||
}
|
||||
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
|
||||
return ([^]u8)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
|
||||
+7
-14
@@ -8,6 +8,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,20 +21,12 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a)
|
||||
}
|
||||
raw_string_data :: proc "contextless" (s: $T/string) -> ^byte {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
raw_slice_data :: proc "contextless" (a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Slice)a).data
|
||||
}
|
||||
raw_dynamic_array_data :: proc "contextless" (a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data
|
||||
}
|
||||
|
||||
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data}
|
||||
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) {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_init :: proc{
|
||||
static_arena_init,
|
||||
growing_arena_init,
|
||||
}
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
|
||||
@@ -13,6 +13,13 @@ Growing_Arena :: struct {
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
|
||||
|
||||
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -37,7 +44,7 @@ growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(block_size, block_size, {}) or_return
|
||||
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
|
||||
@@ -95,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
}
|
||||
|
||||
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) {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
//+build darwin
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
foreign import libc "System.framework"
|
||||
import "core:c"
|
||||
|
||||
PROT_NONE :: 0x0 /* [MC2] no permissions */
|
||||
PROT_READ :: 0x1 /* [MC2] pages can be read */
|
||||
PROT_WRITE :: 0x2 /* [MC2] pages can be written */
|
||||
PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
|
||||
|
||||
// Sharing options
|
||||
MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
|
||||
MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
|
||||
|
||||
// Other flags
|
||||
MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
|
||||
MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
|
||||
MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
|
||||
MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
|
||||
MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
|
||||
MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
|
||||
MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
|
||||
MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
|
||||
|
||||
// Mapping type
|
||||
MAP_FILE :: 0x0000 /* map from file (default) */
|
||||
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
|
||||
|
||||
|
||||
/*
|
||||
* The MAP_RESILIENT_* flags can be used when the caller wants to map some
|
||||
* possibly unreliable memory and be able to access it safely, possibly
|
||||
* getting the wrong contents rather than raising any exception.
|
||||
* For safety reasons, such mappings have to be read-only (PROT_READ access
|
||||
* only).
|
||||
*
|
||||
* MAP_RESILIENT_CODESIGN:
|
||||
* accessing this mapping will not generate code-signing violations,
|
||||
* even if the contents are tainted.
|
||||
* MAP_RESILIENT_MEDIA:
|
||||
* accessing this mapping will not generate an exception if the contents
|
||||
* are not available (unreachable removable or remote media, access beyond
|
||||
* end-of-file, ...). Missing contents will be replaced with zeroes.
|
||||
*/
|
||||
MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
|
||||
MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
|
||||
|
||||
MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
|
||||
|
||||
// Flags used to support translated processes.
|
||||
MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
|
||||
MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
|
||||
|
||||
// Process memory locking
|
||||
MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
|
||||
MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
|
||||
|
||||
MADV_NORMAL :: 0 /* [MC1] no further special treatment */
|
||||
MADV_RANDOM :: 1 /* [MC1] expect random page refs */
|
||||
MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
|
||||
MADV_WILLNEED :: 3 /* [MC1] will need these pages */
|
||||
MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
|
||||
MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
|
||||
MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
|
||||
MADV_CAN_REUSE :: 9
|
||||
MADV_PAGEOUT :: 10 /* page out now (internal only) */
|
||||
|
||||
// msync() flags
|
||||
MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
|
||||
MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
|
||||
MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
|
||||
MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
|
||||
MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
|
||||
|
||||
// Return bits from mincore
|
||||
MINCORE_INCORE :: 0x1 /* Page is incore */
|
||||
MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
|
||||
MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
|
||||
MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
|
||||
MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
|
||||
MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
|
||||
MINCORE_COPIED :: 0x40 /* Page has been copied */
|
||||
MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
|
||||
|
||||
// Allocation failure result
|
||||
MAP_FAILED : rawptr = rawptr(~uintptr(0))
|
||||
|
||||
foreign libc {
|
||||
@(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
|
||||
@(link_name="munlockall") _munlockall :: proc() -> c.int ---
|
||||
@(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
|
||||
@(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
|
||||
@(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
|
||||
@(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
|
||||
@(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||
@(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||
@(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
|
||||
@(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
|
||||
}
|
||||
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
if result == MAP_FAILED {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
return ([^]byte)(uintptr(result))[:size], nil
|
||||
}
|
||||
|
||||
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := _mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||
if result != 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||
_mprotect(data, size, PROT_NONE)
|
||||
_madvise(data, size, MADV_FREE)
|
||||
}
|
||||
_release :: proc "contextless" (data: rawptr, size: uint) {
|
||||
_munmap(data, size)
|
||||
}
|
||||
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
pflags: c.int
|
||||
pflags = PROT_NONE
|
||||
if .Read in flags { pflags |= PROT_READ }
|
||||
if .Write in flags { pflags |= PROT_WRITE }
|
||||
if .Execute in flags { pflags |= PROT_EXEC }
|
||||
err := _mprotect(data, size, pflags)
|
||||
return err != 0
|
||||
}
|
||||
|
||||
|
||||
_platform_memory_init :: proc() {
|
||||
DEFAULT_PAGE_SIZE = 4096
|
||||
|
||||
// is power of two
|
||||
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||
}
|
||||
@@ -37,9 +37,9 @@ MADV_WIPEONFORK :: 18
|
||||
MADV_KEEPONFORK :: 19
|
||||
MADV_HWPOISON :: 100
|
||||
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> int {
|
||||
res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
|
||||
return rawptr(res)
|
||||
return int(res)
|
||||
}
|
||||
|
||||
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -59,12 +59,11 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
|
||||
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
MAP_FAILED := rawptr(~uintptr(0))
|
||||
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
if result == MAP_FAILED {
|
||||
if result < 0 && result > -4096 {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
return ([^]byte)(result)[:size], nil
|
||||
return ([^]byte)(uintptr(result))[:size], nil
|
||||
}
|
||||
|
||||
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
|
||||
+37
-1
@@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
return
|
||||
}
|
||||
|
||||
Field_Flags :: distinct bit_set[Field_Flag]
|
||||
|
||||
Field_Flag :: enum {
|
||||
Invalid,
|
||||
Unknown,
|
||||
|
||||
Ellipsis,
|
||||
Using,
|
||||
No_Alias,
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
Any_Int,
|
||||
Subtype,
|
||||
By_Ptr,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
@@ -566,11 +573,38 @@ Field_Flag :: enum {
|
||||
Typeid_Token,
|
||||
}
|
||||
|
||||
Field_Flags :: distinct bit_set[Field_Flag]
|
||||
field_flag_strings := [Field_Flag]string{
|
||||
.Invalid = "",
|
||||
.Unknown = "",
|
||||
|
||||
.Ellipsis = "..",
|
||||
.Using = "using",
|
||||
.No_Alias = "#no_alias",
|
||||
.C_Vararg = "#c_vararg",
|
||||
.Auto_Cast = "auto_cast",
|
||||
.Any_Int = "#any_int",
|
||||
.Subtype = "#subtype",
|
||||
.By_Ptr = "#by_ptr",
|
||||
|
||||
.Results = "results",
|
||||
.Tags = "field tag",
|
||||
.Default_Parameters = "default parameters",
|
||||
.Typeid_Token = "typeid",
|
||||
}
|
||||
|
||||
field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
||||
{"no_alias", .No_Alias},
|
||||
{"c_vararg", .C_Vararg},
|
||||
{"any_int", .Any_Int},
|
||||
{"subtype", .Subtype},
|
||||
{"by_ptr", .By_Ptr},
|
||||
}
|
||||
|
||||
|
||||
Field_Flags_Struct :: Field_Flags{
|
||||
.Using,
|
||||
.Tags,
|
||||
.Subtype,
|
||||
}
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
.Typeid_Token,
|
||||
@@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Any_Int,
|
||||
.By_Ptr,
|
||||
.Default_Parameters,
|
||||
}
|
||||
|
||||
@@ -665,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:
|
||||
|
||||
@@ -183,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
||||
pd.name = pkg_name.text
|
||||
pd.comment = p.line_comment
|
||||
p.file.pkg_decl = pd
|
||||
p.file.docs = docs
|
||||
|
||||
expect_semicolon(p, pd)
|
||||
|
||||
@@ -244,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
|
||||
}
|
||||
@@ -1610,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E
|
||||
return field
|
||||
}
|
||||
|
||||
|
||||
Field_Prefix :: enum {
|
||||
Invalid,
|
||||
Unknown,
|
||||
|
||||
Using,
|
||||
No_Alias,
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
Any_Int,
|
||||
}
|
||||
|
||||
Field_Prefixes :: distinct bit_set[Field_Prefix]
|
||||
|
||||
Expr_And_Flags :: struct {
|
||||
expr: ^ast.Expr,
|
||||
flags: ast.Field_Flags,
|
||||
@@ -1665,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
|
||||
return idents[:]
|
||||
}
|
||||
|
||||
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag {
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .EOF:
|
||||
return .Invalid
|
||||
@@ -1676,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
advance_token(p)
|
||||
return .Auto_Cast
|
||||
case .Hash:
|
||||
tok: tokenizer.Token
|
||||
advance_token(p)
|
||||
defer advance_token(p)
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .Ident:
|
||||
switch p.curr_tok.text {
|
||||
case "no_alias":
|
||||
return .No_Alias
|
||||
case "c_vararg":
|
||||
return .C_Vararg
|
||||
case "any_int":
|
||||
return .Any_Int
|
||||
tok = p.curr_tok
|
||||
advance_token(p)
|
||||
if tok.kind == .Ident {
|
||||
for kf in ast.field_hash_flag_strings {
|
||||
if kf.key == tok.text {
|
||||
return kf.flag
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Unknown
|
||||
@@ -1694,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
return .Invalid
|
||||
}
|
||||
|
||||
parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
||||
counts: [len(Field_Prefix)]int
|
||||
parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) {
|
||||
counts: [len(ast.Field_Flag)]int
|
||||
|
||||
for {
|
||||
kind := is_token_field_prefix(p)
|
||||
@@ -1711,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
||||
counts[kind] += 1
|
||||
}
|
||||
|
||||
flags: ast.Field_Flags
|
||||
|
||||
for kind in Field_Prefix {
|
||||
for kind in ast.Field_Flag {
|
||||
count := counts[kind]
|
||||
switch kind {
|
||||
case .Invalid, .Unknown: // Ignore
|
||||
case .Using:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") }
|
||||
if count > 0 { flags += {.Using} }
|
||||
case .No_Alias:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") }
|
||||
if count > 0 { flags += {.No_Alias} }
|
||||
case .C_Vararg:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") }
|
||||
if count > 0 { flags += {.C_Vararg} }
|
||||
case .Auto_Cast:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") }
|
||||
if count > 0 { flags += {.Auto_Cast} }
|
||||
case .Any_Int:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") }
|
||||
if count > 0 { flags += {.Any_Int} }
|
||||
if kind == .Invalid || kind == .Unknown {
|
||||
// Ignore
|
||||
} else {
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) }
|
||||
if count > 0 { flags += {kind} }
|
||||
}
|
||||
}
|
||||
|
||||
return flags
|
||||
return
|
||||
}
|
||||
|
||||
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
|
||||
@@ -1747,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
|
||||
|
||||
for flag in ast.Field_Flag {
|
||||
if flag not_in allowed_flags && flag in flags {
|
||||
switch flag {
|
||||
case .Using:
|
||||
error(p, p.curr_tok.pos, "'using' is not allowed within this field list")
|
||||
case .No_Alias:
|
||||
error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list")
|
||||
case .C_Vararg:
|
||||
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list")
|
||||
case .Auto_Cast:
|
||||
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list")
|
||||
case .Any_Int:
|
||||
error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list")
|
||||
#partial switch flag {
|
||||
case .Unknown, .Invalid:
|
||||
// ignore
|
||||
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
|
||||
panic("Impossible prefixes")
|
||||
case:
|
||||
error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag])
|
||||
}
|
||||
flags -= {flag}
|
||||
}
|
||||
@@ -2270,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
|
||||
@@ -2279,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)
|
||||
}
|
||||
@@ -2630,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
tok := expect_token(p, .Union)
|
||||
poly_params: ^ast.Field_List
|
||||
align: ^ast.Expr
|
||||
is_maybe: bool
|
||||
is_no_nil: bool
|
||||
is_shared_nil: bool
|
||||
|
||||
@@ -2655,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
align = parse_expr(p, true)
|
||||
case "maybe":
|
||||
if is_maybe {
|
||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
||||
}
|
||||
is_maybe = true
|
||||
error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text)
|
||||
case "no_nil":
|
||||
if is_no_nil {
|
||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
||||
@@ -2675,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
p.expr_level = prev_level
|
||||
|
||||
if is_no_nil && is_maybe {
|
||||
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
|
||||
}
|
||||
if is_no_nil && is_shared_nil {
|
||||
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
|
||||
}
|
||||
if is_shared_nil && is_maybe {
|
||||
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
|
||||
}
|
||||
|
||||
union_kind := ast.Union_Type_Kind.Normal
|
||||
switch {
|
||||
case is_maybe: union_kind = .maybe
|
||||
case is_no_nil: union_kind = .no_nil
|
||||
case is_shared_nil: union_kind = .shared_nil
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
|
||||
|
||||
fix_lines(p)
|
||||
|
||||
builder := strings.make_builder(0, 5 * mem.Megabyte, p.allocator)
|
||||
builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator)
|
||||
|
||||
last_line := 0
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
||||
|
||||
return 0
|
||||
} else {
|
||||
builder := strings.make_builder(context.temp_allocator)
|
||||
builder := strings.builder_make(context.temp_allocator)
|
||||
|
||||
c_len := len(comment.text)
|
||||
trim_space := true
|
||||
@@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
||||
continue
|
||||
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
|
||||
append(&multilines, strings.to_string(builder))
|
||||
builder = strings.make_builder(context.temp_allocator)
|
||||
builder = strings.builder_make(context.temp_allocator)
|
||||
trim_space = true
|
||||
i += 1
|
||||
case c == '\n':
|
||||
append(&multilines, strings.to_string(builder))
|
||||
builder = strings.make_builder(context.temp_allocator)
|
||||
builder = strings.builder_make(context.temp_allocator)
|
||||
trim_space = true
|
||||
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
|
||||
strings.write_string(&builder, "/*")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package os
|
||||
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
@@ -51,10 +50,13 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
continue
|
||||
}
|
||||
|
||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
||||
fullpath := make([]byte, len(dirpath)+1+len(filename))
|
||||
copy(fullpath, dirpath)
|
||||
copy(fullpath[len(dirpath):], "/")
|
||||
copy(fullpath[len(dirpath)+1:], filename)
|
||||
defer delete(fullpath, context.temp_allocator)
|
||||
|
||||
fi_, err = stat(fullpath, allocator)
|
||||
fi_, err = stat(string(fullpath), allocator)
|
||||
if err != ERROR_NONE {
|
||||
for fi__ in dfi {
|
||||
file_info_delete(fi__, allocator)
|
||||
|
||||
@@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
+17
-17
@@ -11,24 +11,24 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
b := make([dynamic]u16, 100, context.temp_allocator)
|
||||
for {
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
resize(&b, len(b)*2)
|
||||
}
|
||||
b := make([dynamic]u16, n, context.temp_allocator)
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator))
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
+33
-12
@@ -365,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator)
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
@@ -384,20 +384,33 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
|
||||
|
||||
|
||||
change_directory :: proc(path: string) -> Errno {
|
||||
change_directory :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.SetCurrentDirectoryW(wpath))
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
make_directory :: proc(path: string, mode: u32) -> Errno {
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.CreateDirectoryW(wpath, nil))
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> Errno {
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.RemoveDirectoryW(wpath))
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -463,23 +476,31 @@ fix_long_path :: proc(path: string) -> string {
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Errno {
|
||||
link :: proc(old_name, new_name: string) -> (err: Errno) {
|
||||
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) -> Errno {
|
||||
unlink :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.DeleteFileW(wpath))
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
rename :: proc(old_path, new_path: string) -> (err: Errno) {
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING))
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ OS :: ODIN_OS
|
||||
ARCH :: ODIN_ARCH
|
||||
ENDIAN :: ODIN_ENDIAN
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, transmute([]byte)str)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
//TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,37 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
b := make([dynamic]u16, 100, context.temp_allocator)
|
||||
for {
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
|
||||
resize(&b, len(b)*2)
|
||||
return "", true
|
||||
}
|
||||
b := make([]u16, n+1, _temp_allocator())
|
||||
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
value = win32.utf16_to_utf8(b[:n], allocator) or_else ""
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
@@ -42,7 +47,7 @@ _unset_env :: proc(key: string) -> bool {
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
envs := environ(context.temp_allocator)
|
||||
envs := environ(_temp_allocator())
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
@@ -53,7 +58,7 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW()
|
||||
if envs == nil {
|
||||
return nil
|
||||
@@ -62,14 +67,13 @@ _environ :: proc(allocator := context.allocator) -> []string {
|
||||
|
||||
r := make([dynamic]string, 0, 50, allocator)
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := (^u16)(uintptr(p) + uintptr(i*2))^
|
||||
c := ([^]u16)(p)[i]
|
||||
if c == 0 {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from)
|
||||
|
||||
append(&r, win32.utf16_to_utf8(w, allocator))
|
||||
w := ([^]u16)(p)[from:i]
|
||||
append(&r, win32.utf16_to_utf8(w, allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
+56
-60
@@ -1,9 +1,10 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:runtime"
|
||||
|
||||
General_Error :: enum u32 {
|
||||
Invalid_Argument,
|
||||
None,
|
||||
|
||||
Permission_Denied,
|
||||
Exist,
|
||||
@@ -11,83 +12,78 @@ General_Error :: enum u32 {
|
||||
Closed,
|
||||
|
||||
Timeout,
|
||||
|
||||
Invalid_File,
|
||||
Invalid_Dir,
|
||||
Invalid_Path,
|
||||
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
Platform_Error :: struct {
|
||||
err: i32,
|
||||
}
|
||||
Platform_Error :: enum i32 {None=0}
|
||||
|
||||
Error :: union {
|
||||
Error :: union #shared_nil {
|
||||
General_Error,
|
||||
io.Error,
|
||||
runtime.Allocator_Error,
|
||||
Platform_Error,
|
||||
}
|
||||
#assert(size_of(Error) == size_of(u64))
|
||||
|
||||
Path_Error :: struct {
|
||||
op: string,
|
||||
path: string,
|
||||
err: Error,
|
||||
}
|
||||
|
||||
Link_Error :: struct {
|
||||
op: string,
|
||||
old: string,
|
||||
new: string,
|
||||
err: Error,
|
||||
}
|
||||
|
||||
path_error_delete :: proc(perr: Maybe(Path_Error)) {
|
||||
if err, ok := perr.?; ok {
|
||||
context.allocator = error_allocator()
|
||||
delete(err.op)
|
||||
delete(err.path)
|
||||
}
|
||||
}
|
||||
|
||||
link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
if err, ok := lerr.?; ok {
|
||||
context.allocator = error_allocator()
|
||||
delete(err.op)
|
||||
delete(err.old)
|
||||
delete(err.new)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
v := ferr.(Platform_Error) or_else {}
|
||||
return v.err, v.err != 0
|
||||
return i32(v), i32(v) != 0
|
||||
}
|
||||
|
||||
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
switch ferr {
|
||||
case nil: return ""
|
||||
case .Invalid_Argument: return "invalid argument"
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
if ferr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if errno, ok := is_platform_error(ferr); ok {
|
||||
return _error_string(errno)
|
||||
switch e in ferr {
|
||||
case General_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .Invalid_File: return "invalid file"
|
||||
case .Invalid_Dir: return "invalid directory"
|
||||
case .Invalid_Path: return "invalid path"
|
||||
case .Unsupported: return "unsupported"
|
||||
}
|
||||
case io.Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
case .Unknown, .Empty: //
|
||||
}
|
||||
case runtime.Allocator_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Out_Of_Memory: return "out of memory"
|
||||
case .Invalid_Pointer: return "invalid allocator pointer"
|
||||
case .Invalid_Argument: return "invalid allocator argument"
|
||||
case .Mode_Not_Implemented: return "allocator mode not implemented"
|
||||
}
|
||||
case Platform_Error:
|
||||
return _error_string(i32(e))
|
||||
}
|
||||
|
||||
return "unknown error"
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
EPERM :: 1
|
||||
ENOENT :: 2
|
||||
ESRCH :: 3
|
||||
EINTR :: 4
|
||||
EIO :: 5
|
||||
ENXIO :: 6
|
||||
EBADF :: 9
|
||||
EAGAIN :: 11
|
||||
ENOMEM :: 12
|
||||
EACCES :: 13
|
||||
EFAULT :: 14
|
||||
EEXIST :: 17
|
||||
ENODEV :: 19
|
||||
ENOTDIR :: 20
|
||||
EISDIR :: 21
|
||||
EINVAL :: 22
|
||||
ENFILE :: 23
|
||||
EMFILE :: 24
|
||||
ETXTBSY :: 26
|
||||
EFBIG :: 27
|
||||
ENOSPC :: 28
|
||||
ESPIPE :: 29
|
||||
EROFS :: 30
|
||||
EPIPE :: 32
|
||||
ERANGE :: 34 /* Result too large */
|
||||
EDEADLK :: 35 /* Resource deadlock would occur */
|
||||
ENAMETOOLONG :: 36 /* File name too long */
|
||||
ENOLCK :: 37 /* No record locks available */
|
||||
ENOSYS :: 38 /* Invalid system call number */
|
||||
ENOTEMPTY :: 39 /* Directory not empty */
|
||||
ELOOP :: 40 /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
||||
ENOMSG :: 42 /* No message of desired type */
|
||||
EIDRM :: 43 /* Identifier removed */
|
||||
ECHRNG :: 44 /* Channel number out of range */
|
||||
EL2NSYNC :: 45 /* Level 2 not synchronized */
|
||||
EL3HLT :: 46 /* Level 3 halted */
|
||||
EL3RST :: 47 /* Level 3 reset */
|
||||
ELNRNG :: 48 /* Link number out of range */
|
||||
EUNATCH :: 49 /* Protocol driver not attached */
|
||||
ENOCSI :: 50 /* No CSI structure available */
|
||||
EL2HLT :: 51 /* Level 2 halted */
|
||||
EBADE :: 52 /* Invalid exchange */
|
||||
EBADR :: 53 /* Invalid request descriptor */
|
||||
EXFULL :: 54 /* Exchange full */
|
||||
ENOANO :: 55 /* No anode */
|
||||
EBADRQC :: 56 /* Invalid request code */
|
||||
EBADSLT :: 57 /* Invalid slot */
|
||||
EDEADLOCK :: EDEADLK
|
||||
EBFONT :: 59 /* Bad font file format */
|
||||
ENOSTR :: 60 /* Device not a stream */
|
||||
ENODATA :: 61 /* No data available */
|
||||
ETIME :: 62 /* Timer expired */
|
||||
ENOSR :: 63 /* Out of streams resources */
|
||||
ENONET :: 64 /* Machine is not on the network */
|
||||
ENOPKG :: 65 /* Package not installed */
|
||||
EREMOTE :: 66 /* Object is remote */
|
||||
ENOLINK :: 67 /* Link has been severed */
|
||||
EADV :: 68 /* Advertise error */
|
||||
ESRMNT :: 69 /* Srmount error */
|
||||
ECOMM :: 70 /* Communication error on send */
|
||||
EPROTO :: 71 /* Protocol error */
|
||||
EMULTIHOP :: 72 /* Multihop attempted */
|
||||
EDOTDOT :: 73 /* RFS specific error */
|
||||
EBADMSG :: 74 /* Not a data message */
|
||||
EOVERFLOW :: 75 /* Value too large for defined data type */
|
||||
ENOTUNIQ :: 76 /* Name not unique on network */
|
||||
EBADFD :: 77 /* File descriptor in bad state */
|
||||
EREMCHG :: 78 /* Remote address changed */
|
||||
ELIBACC :: 79 /* Can not access a needed shared library */
|
||||
ELIBBAD :: 80 /* Accessing a corrupted shared library */
|
||||
ELIBSCN :: 81 /* .lib section in a.out corrupted */
|
||||
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
|
||||
EILSEQ :: 84 /* Illegal byte sequence */
|
||||
ERESTART :: 85 /* Interrupted system call should be restarted */
|
||||
ESTRPIPE :: 86 /* Streams pipe error */
|
||||
EUSERS :: 87 /* Too many users */
|
||||
ENOTSOCK :: 88 /* Socket operation on non-socket */
|
||||
EDESTADDRREQ :: 89 /* Destination address required */
|
||||
EMSGSIZE :: 90 /* Message too long */
|
||||
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT :: 92 /* Protocol not available */
|
||||
EPROTONOSUPPORT:: 93 /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
|
||||
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT :: 96 /* Protocol family not supported */
|
||||
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
|
||||
EADDRINUSE :: 98 /* Address already in use */
|
||||
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
|
||||
ENETDOWN :: 100 /* Network is down */
|
||||
ENETUNREACH :: 101 /* Network is unreachable */
|
||||
ENETRESET :: 102 /* Network dropped connection because of reset */
|
||||
ECONNABORTED :: 103 /* Software caused connection abort */
|
||||
ECONNRESET :: 104 /* Connection reset by peer */
|
||||
ENOBUFS :: 105 /* No buffer space available */
|
||||
EISCONN :: 106 /* Transport endpoint is already connected */
|
||||
ENOTCONN :: 107 /* Transport endpoint is not connected */
|
||||
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
|
||||
ETIMEDOUT :: 110 /* Connection timed out */
|
||||
ECONNREFUSED :: 111 /* Connection refused */
|
||||
EHOSTDOWN :: 112 /* Host is down */
|
||||
EHOSTUNREACH :: 113 /* No route to host */
|
||||
EALREADY :: 114 /* Operation already in progress */
|
||||
EINPROGRESS :: 115 /* Operation now in progress */
|
||||
ESTALE :: 116 /* Stale file handle */
|
||||
EUCLEAN :: 117 /* Structure needs cleaning */
|
||||
ENOTNAM :: 118 /* Not a XENIX named type file */
|
||||
ENAVAIL :: 119 /* No XENIX semaphores available */
|
||||
EISNAM :: 120 /* Is a named type file */
|
||||
EREMOTEIO :: 121 /* Remote I/O error */
|
||||
EDQUOT :: 122 /* Quota exceeded */
|
||||
ENOMEDIUM :: 123 /* No medium found */
|
||||
EMEDIUMTYPE :: 124 /* Wrong medium type */
|
||||
ECANCELED :: 125 /* Operation Canceled */
|
||||
ENOKEY :: 126 /* Required key not available */
|
||||
EKEYEXPIRED :: 127 /* Key has expired */
|
||||
EKEYREVOKED :: 128 /* Key has been revoked */
|
||||
EKEYREJECTED :: 129 /* Key was rejected by service */
|
||||
EOWNERDEAD :: 130 /* Owner died */
|
||||
ENOTRECOVERABLE:: 131 /* State not recoverable */
|
||||
ERFKILL :: 132 /* Operation not possible due to RF-kill */
|
||||
EHWPOISON :: 133 /* Memory page has hardware error */
|
||||
|
||||
_get_platform_error :: proc(res: int) -> Error {
|
||||
errno := unix.get_errno(res)
|
||||
return Platform_Error(i32(errno))
|
||||
}
|
||||
|
||||
_ok_or_error :: proc(res: int) -> Error {
|
||||
return res >= 0 ? nil : _get_platform_error(res)
|
||||
}
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
if errno == 0 {
|
||||
return ""
|
||||
}
|
||||
return "Error"
|
||||
}
|
||||
@@ -12,3 +12,49 @@ _error_string :: proc(errno: i32) -> string {
|
||||
// FormatMessageW
|
||||
return ""
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := win32.GetLastError()
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
switch err {
|
||||
case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION:
|
||||
return .Permission_Denied
|
||||
|
||||
case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS:
|
||||
return .Exist
|
||||
|
||||
case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND:
|
||||
return .Not_Exist
|
||||
|
||||
case win32.ERROR_NO_DATA:
|
||||
return .Closed
|
||||
|
||||
case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT:
|
||||
return .Timeout
|
||||
|
||||
case win32.ERROR_NOT_SUPPORTED:
|
||||
return .Unsupported
|
||||
|
||||
case
|
||||
win32.ERROR_BAD_ARGUMENTS,
|
||||
win32.ERROR_INVALID_PARAMETER,
|
||||
win32.ERROR_NOT_ENOUGH_MEMORY,
|
||||
win32.ERROR_INVALID_HANDLE,
|
||||
win32.ERROR_NO_MORE_FILES,
|
||||
win32.ERROR_LOCK_VIOLATION,
|
||||
win32.ERROR_HANDLE_EOF,
|
||||
win32.ERROR_BROKEN_PIPE,
|
||||
win32.ERROR_CALL_NOT_IMPLEMENTED,
|
||||
win32.ERROR_INSUFFICIENT_BUFFER,
|
||||
win32.ERROR_INVALID_NAME,
|
||||
win32.ERROR_LOCK_FAILED,
|
||||
win32.ERROR_ENVVAR_NOT_FOUND,
|
||||
win32.ERROR_OPERATION_ABORTED,
|
||||
win32.ERROR_IO_PENDING,
|
||||
win32.ERROR_NO_UNICODE_TRANSLATION:
|
||||
// fallthrough
|
||||
}
|
||||
return Platform_Error(err)
|
||||
}
|
||||
+136
-77
@@ -2,8 +2,11 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
Handle :: distinct uintptr
|
||||
File :: struct {
|
||||
impl: _File,
|
||||
}
|
||||
|
||||
Seek_From :: enum {
|
||||
Start = 0, // seek relative to the origin of the file
|
||||
@@ -18,131 +21,170 @@ File_Mode_Device :: File_Mode(1<<18)
|
||||
File_Mode_Char_Device :: File_Mode(1<<19)
|
||||
File_Mode_Sym_Link :: File_Mode(1<<20)
|
||||
|
||||
File_Mode_Perm :: File_Mode(0o777) // Unix permision bits
|
||||
|
||||
O_RDONLY :: int( 0)
|
||||
O_WRONLY :: int( 1)
|
||||
O_RDWR :: int( 2)
|
||||
O_APPEND :: int( 4)
|
||||
O_CREATE :: int( 8)
|
||||
O_EXCL :: int(16)
|
||||
O_SYNC :: int(32)
|
||||
O_TRUNC :: int(64)
|
||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||
File_Flag :: enum {
|
||||
Read,
|
||||
Write,
|
||||
Append,
|
||||
Create,
|
||||
Excl,
|
||||
Sync,
|
||||
Trunc,
|
||||
Sparse,
|
||||
Close_On_Exec,
|
||||
|
||||
|
||||
|
||||
stdin: Handle = 0 // OS-Specific
|
||||
stdout: Handle = 1 // OS-Specific
|
||||
stderr: Handle = 2 // OS-Specific
|
||||
|
||||
|
||||
create :: proc(name: string) -> (Handle, Error) {
|
||||
return _create(name)
|
||||
Unbuffered_IO,
|
||||
}
|
||||
|
||||
open :: proc(name: string) -> (Handle, Error) {
|
||||
return _open(name)
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||
|
||||
|
||||
|
||||
stdin: ^File = nil // OS-Specific
|
||||
stdout: ^File = nil // OS-Specific
|
||||
stderr: ^File = nil // OS-Specific
|
||||
|
||||
|
||||
create :: proc(name: string) -> (^File, Error) {
|
||||
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
||||
}
|
||||
|
||||
open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
|
||||
return _open_file(name, flag, perm)
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) {
|
||||
return _open(name, flags, perm)
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
return _close(fd)
|
||||
new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return _new_file(handle, name)
|
||||
}
|
||||
|
||||
name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
return _name(fd)
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return _seek(fd, offset, whence)
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _read(fd, p)
|
||||
}
|
||||
|
||||
read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _read_at(fd, p, offset)
|
||||
}
|
||||
|
||||
read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return _read_from(fd, r)
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _write(fd, p)
|
||||
}
|
||||
|
||||
write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _write_at(fd, p, offset)
|
||||
}
|
||||
|
||||
write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return _write_to(fd, w)
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
return _file_size(fd)
|
||||
fd :: proc(f: ^File) -> uintptr {
|
||||
return _fd(f)
|
||||
}
|
||||
|
||||
|
||||
sync :: proc(fd: Handle) -> Error {
|
||||
return _sync(fd)
|
||||
close :: proc(f: ^File) -> Error {
|
||||
return _close(f)
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> Error {
|
||||
return _flush(fd)
|
||||
name :: proc(f: ^File) -> string {
|
||||
return _name(f)
|
||||
}
|
||||
|
||||
truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
|
||||
return _truncate(fd, size)
|
||||
seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return _seek(f, offset, whence)
|
||||
}
|
||||
|
||||
remove :: proc(name: string) -> Maybe(Path_Error) {
|
||||
read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return _read(f, p)
|
||||
}
|
||||
|
||||
read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _read_at(f, p, offset)
|
||||
}
|
||||
|
||||
read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return _read_from(f, r)
|
||||
}
|
||||
|
||||
write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return _write(f, p)
|
||||
}
|
||||
|
||||
write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _write_at(f, p, offset)
|
||||
}
|
||||
|
||||
write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return _write_to(f, w)
|
||||
}
|
||||
|
||||
file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
return _file_size(f)
|
||||
}
|
||||
|
||||
|
||||
sync :: proc(f: ^File) -> Error {
|
||||
return _sync(f)
|
||||
}
|
||||
|
||||
flush :: proc(f: ^File) -> Error {
|
||||
return _flush(f)
|
||||
}
|
||||
|
||||
truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _truncate(f, size)
|
||||
}
|
||||
|
||||
remove :: proc(name: string) -> Error {
|
||||
return _remove(name)
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
return _rename(old_path, new_path)
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
link :: proc(old_name, new_name: string) -> Error {
|
||||
return _link(old_name, new_name)
|
||||
}
|
||||
|
||||
symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _symlink(old_name, new_name)
|
||||
}
|
||||
|
||||
read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
return _read_link(name)
|
||||
read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _read_link(name,allocator)
|
||||
}
|
||||
|
||||
|
||||
chdir :: proc(fd: Handle) -> Error {
|
||||
return _chdir(fd)
|
||||
chdir :: proc(name: string) -> Error {
|
||||
return _chdir(name)
|
||||
}
|
||||
|
||||
chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return _chmod(fd, mode)
|
||||
chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
return _chmod(name, mode)
|
||||
}
|
||||
|
||||
chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return _chown(fd, uid, gid)
|
||||
chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _chown(name, uid, gid)
|
||||
}
|
||||
|
||||
fchdir :: proc(f: ^File) -> Error {
|
||||
return _fchdir(f)
|
||||
}
|
||||
|
||||
fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _fchown(f, uid, gid)
|
||||
}
|
||||
|
||||
|
||||
|
||||
lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _lchown(name, uid, gid)
|
||||
}
|
||||
|
||||
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _chtimes(name, atime, mtime)
|
||||
}
|
||||
fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
return _exists(path)
|
||||
@@ -156,3 +198,20 @@ is_dir :: proc(path: string) -> bool {
|
||||
return _is_dir(path)
|
||||
}
|
||||
|
||||
|
||||
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
src := open(src_path) or_return
|
||||
defer close(src)
|
||||
|
||||
info := fstat(src, _file_allocator()) or_return
|
||||
defer file_info_delete(info, _file_allocator())
|
||||
if info.is_dir {
|
||||
return .Invalid_File
|
||||
}
|
||||
|
||||
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return
|
||||
defer close(dst)
|
||||
|
||||
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
_O_RDONLY :: 0o0
|
||||
_O_WRONLY :: 0o1
|
||||
_O_RDWR :: 0o2
|
||||
_O_CREAT :: 0o100
|
||||
_O_EXCL :: 0o200
|
||||
_O_TRUNC :: 0o1000
|
||||
_O_APPEND :: 0o2000
|
||||
_O_NONBLOCK :: 0o4000
|
||||
_O_LARGEFILE :: 0o100000
|
||||
_O_DIRECTORY :: 0o200000
|
||||
_O_NOFOLLOW :: 0o400000
|
||||
_O_SYNC :: 0o4010000
|
||||
_O_CLOEXEC :: 0o2000000
|
||||
_O_PATH :: 0o10000000
|
||||
|
||||
_AT_FDCWD :: -100
|
||||
|
||||
_CSTRING_NAME_HEAP_THRESHOLD :: 512
|
||||
|
||||
_File :: struct {
|
||||
name: string,
|
||||
fd: int,
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_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)
|
||||
}
|
||||
|
||||
flags_i: int
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
case O_RDONLY: flags_i = _O_RDONLY
|
||||
case O_WRONLY: flags_i = _O_WRONLY
|
||||
case O_RDWR: flags_i = _O_RDWR
|
||||
}
|
||||
|
||||
flags_i |= (_O_APPEND * int(.Append in flags))
|
||||
flags_i |= (_O_CREAT * int(.Create in flags))
|
||||
flags_i |= (_O_EXCL * int(.Excl in flags))
|
||||
flags_i |= (_O_SYNC * int(.Sync in flags))
|
||||
flags_i |= (_O_TRUNC * int(.Trunc in flags))
|
||||
flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
|
||||
|
||||
fd := unix.sys_open(name_cstr, flags_i, int(perm))
|
||||
if fd < 0 {
|
||||
return nil, _get_platform_error(fd)
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(fd: uintptr, _: string) -> ^File {
|
||||
file := new(File, _file_allocator())
|
||||
file.impl.fd = int(fd)
|
||||
file.impl.allocator = _file_allocator()
|
||||
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
|
||||
return file
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
delete(f.impl.name, f.impl.allocator)
|
||||
free(f, f.impl.allocator)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
res := unix.sys_close(f.impl.fd)
|
||||
return _ok_or_error(res)
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil {
|
||||
return ~uintptr(0)
|
||||
}
|
||||
return uintptr(f.impl.fd)
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return f.impl.name if f != nil else ""
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(int(res))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_read(f.impl.fd, &p[0], len(p))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
s: _Stat = ---
|
||||
res := unix.sys_fstat(f.impl.fd, &s)
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(res)
|
||||
}
|
||||
return s.size, nil
|
||||
}
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
|
||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
||||
if fd < 0 {
|
||||
return _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
|
||||
if _is_dir_fd(fd) {
|
||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_rename :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
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) {
|
||||
bufsz : uint = 256
|
||||
buf := make([]byte, bufsz, allocator)
|
||||
for {
|
||||
rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
|
||||
if rc < 0 {
|
||||
delete(buf)
|
||||
return "", _get_platform_error(rc)
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz *= 2
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz, allocator)
|
||||
} else {
|
||||
return strings.string_from_ptr(&buf[0], rc), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _read_link_cstr(name_cstr, allocator)
|
||||
}
|
||||
|
||||
_unlink :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chdir(name_cstr))
|
||||
}
|
||||
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
|
||||
}
|
||||
|
||||
_exists :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return unix.sys_access(name_cstr, F_OK) == 0
|
||||
}
|
||||
|
||||
_is_file :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_file_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_dir :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
_is_dir_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly
|
||||
// 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
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user