mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-12 22:31:25 -07:00
Compare commits
1758 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 79fe30321a | |||
| 35ee7f3cba | |||
| 4c2e86b063 | |||
| d52a9b61af | |||
| 8a5b39f734 | |||
| 775c9648f9 | |||
| f65bdf5733 | |||
| 213d930f8c | |||
| fae025aac8 | |||
| 97477ee51c | |||
| 99894686cf | |||
| 1162e30768 | |||
| cd910b1512 | |||
| efa86ddf46 | |||
| d8f60cd7f2 | |||
| c4d19dfa92 | |||
| 35e70f4be1 | |||
| eb6c388f13 | |||
| 42144d957b | |||
| d1c778680b | |||
| 0fe006157e | |||
| 4d208dc092 | |||
| 162e86663f | |||
| 83ffb68bb7 | |||
| 4705321988 | |||
| 37a2356485 | |||
| a7484f16cb | |||
| 6c8aad0afb | |||
| 12cc7388e7 | |||
| 2ff61bdfc7 | |||
| eb0d7465e2 | |||
| 07d798c61a | |||
| b426e8577b | |||
| 532133d648 | |||
| c056a0d108 | |||
| 6fe1825db9 | |||
| b15968f140 | |||
| 0ddf1bf660 | |||
| dade5b5ef2 | |||
| 3aea9a7c20 | |||
| 0dce7769f4 | |||
| 4b73438833 | |||
| 8c3f01fbfa | |||
| b7abacfa7e | |||
| 3383e9c556 | |||
| 0380601bb4 | |||
| 4b4c2a2abd | |||
| b1542d4e98 | |||
| d469c2da48 | |||
| 29c5e390aa | |||
| 3455e5690c | |||
| 0ca8a5e186 | |||
| cb85d00e9e | |||
| 8ce1ce85ad | |||
| a6d3cbe824 | |||
| 9b9aa9c353 | |||
| 831620bfc4 | |||
| 4f50988799 | |||
| ff97a73152 | |||
| 769d8dd038 | |||
| 1d793ea338 | |||
| 5337413c56 | |||
| 380905618a | |||
| 3ff56e4405 | |||
| 58297774f7 | |||
| 5e9ff85fa8 | |||
| eb7a9c55b0 | |||
| 6157af56e9 | |||
| d6f84887ff | |||
| 729ffeee09 | |||
| 0092995f9d | |||
| 3fb69d59bb | |||
| cb207afdf3 | |||
| 756c1b7bcb | |||
| cd484979a8 | |||
| 9e3ea92829 | |||
| c37de9459e | |||
| 4d512c2cf6 | |||
| 81f10f53ad | |||
| fbf036a654 | |||
| 40bcfc7c8d | |||
| bfe0ffd6e6 | |||
| 8ee6bb5d4b | |||
| 0ebc2add03 | |||
| 7840c1b89f | |||
| 0428d5ae2e | |||
| b967ae2739 | |||
| c462496bd5 | |||
| a903e5024c | |||
| 7cce55e2fc | |||
| 99a1a10286 | |||
| 9640b49319 | |||
| 1bc0e66ed1 | |||
| 117d32dfc4 | |||
| 320b84df4f | |||
| 98eaf5c6c0 | |||
| 9842019205 | |||
| 479278be4e | |||
| 4767311a22 | |||
| f50fc33749 | |||
| 8aba92da9b | |||
| 59f3e10f0a | |||
| 8b82bcef7d | |||
| 1e595f2e26 | |||
| 28ad4f8623 | |||
| a3c04db828 | |||
| 3ea7af4c9c | |||
| 190c3ab0cd | |||
| 53c7cf895c | |||
| db1b7b2d21 | |||
| 7cd7886111 | |||
| 164ba944ac | |||
| e7fb2cf73b | |||
| 023cc8b572 | |||
| 0ff5ff6ff2 | |||
| a35d6a6f8d | |||
| 663b62e45f | |||
| 6910182011 | |||
| bba47b6f54 | |||
| ef372bd861 | |||
| fbbfe438dc | |||
| 7e495a5fe5 | |||
| 0f036eebc0 | |||
| e008eeac6a | |||
| 25e330500f | |||
| fa20988f51 | |||
| 99f4cc3006 | |||
| a1487e4814 | |||
| 183a02c584 | |||
| 913e8b2e02 | |||
| 5800e085e8 | |||
| 623d687192 | |||
| fcb668663b | |||
| ad98efe1fd | |||
| 3fae8b49db | |||
| 8fb9db3deb | |||
| 0859ccc5c0 | |||
| 0c9ddd51a4 | |||
| f77709e67e | |||
| 81e3b64ecd | |||
| 39728b8bfb | |||
| 3b5998af12 | |||
| eea19f8112 | |||
| 268fb22bca | |||
| 37e23a19b5 | |||
| 7d55bfc120 | |||
| ab1741ab38 | |||
| af76d26771 | |||
| d2097e9fdd | |||
| d325c36eb8 | |||
| 79b55d5e2b | |||
| 0c9aaed9f7 | |||
| 82d5f48fa7 | |||
| 2239e43faf | |||
| 70b0ade8c3 | |||
| 86b6d01242 | |||
| 826a3b3012 | |||
| 35d622c131 | |||
| 4bdd2ff93c | |||
| 6fffed179b | |||
| e6b91d3d7c | |||
| 99a7bf9faa | |||
| 44eb478437 | |||
| fc2cd3e1d5 | |||
| a5a9347308 | |||
| b6ed117726 | |||
| 4b23decb08 | |||
| a70ea6579d | |||
| cade30b117 | |||
| 1ca641f718 | |||
| 6222e7be78 | |||
| c7deff4d2e | |||
| 590615ba52 | |||
| b1dafcfe6d | |||
| 4998cf80c1 | |||
| 37e23133e9 | |||
| 91fd9c1ef2 | |||
| 12687a63f4 | |||
| d699d872d9 | |||
| fb2cbe471b | |||
| 17894add95 | |||
| 23d93f6846 | |||
| 2e3dd8dd0b | |||
| 426f02906b | |||
| 2d12ba3ac0 | |||
| 21335e6459 | |||
| 8421cb6d21 | |||
| cac72a9423 | |||
| 9266b81aff | |||
| 52475b1761 | |||
| 2f6347b924 | |||
| eb5456f9c7 | |||
| f914fd0219 | |||
| c94ca4c0cb | |||
| 31a192454c | |||
| 4b2246ba9f | |||
| 0ffffb12da | |||
| 4c857bf031 | |||
| 3f3f4fafff | |||
| 4367ae4acf | |||
| 4eafb0ce7f | |||
| 7a4891b6b9 | |||
| 0171c276f0 | |||
| 0743dd195d | |||
| d1a204a784 | |||
| c2809c2948 | |||
| 99e5a14703 | |||
| 0efc79bcb9 | |||
| 57dea0e4d8 | |||
| 706d0c3a91 | |||
| 1637de3ebb | |||
| 45691a4622 | |||
| 9e47c72b98 | |||
| f5d13dc568 | |||
| a36c1cd54a | |||
| 74458ab096 | |||
| c39b1a31db | |||
| 635c7fa153 | |||
| b7ac0a9e8d | |||
| 3f3ae4b2b6 | |||
| c2423dc07f | |||
| 1296630160 | |||
| 63eec25044 | |||
| 7a9b0731cf | |||
| d45661c405 | |||
| 002bec256a | |||
| 01e8668357 | |||
| 000861cba8 | |||
| 36473b2774 | |||
| 4188f50105 | |||
| 3e3b9ae2df | |||
| e89f0de232 | |||
| 4858e16a11 | |||
| 902a6db0e1 | |||
| 19ae6122c7 | |||
| b82b91ea08 | |||
| 636f0d7063 | |||
| ed73441a4c | |||
| 0f3cebd2b7 | |||
| 4c2be6cd49 | |||
| f3f51bd643 | |||
| 7479ac48e8 | |||
| d5f94d73ad | |||
| 4c5672119a | |||
| 8482f943ea | |||
| 15aaf7dfa0 | |||
| 768abf83f6 | |||
| ca76d53452 | |||
| b0904d6598 | |||
| b0a09f7b9e | |||
| eb4891bcc8 | |||
| 803fd8f037 | |||
| 67bdb5b1a3 | |||
| acc635b535 | |||
| 4e8ce87792 | |||
| 2c8daa25dc | |||
| 054ee0a8b5 | |||
| d0cadaf1a6 | |||
| 317db2758a | |||
| 25102d4792 | |||
| d39f1c461e | |||
| 7a6fc3a93b | |||
| 83c002c197 | |||
| fc47b5dee0 | |||
| 6c2e0b09ba | |||
| 3d4698debe | |||
| 4a25cfb27c | |||
| 294bd6a446 | |||
| d0109db23b | |||
| ee3ee66aae | |||
| f74e281efa | |||
| c0cd02883f | |||
| d9adb0fd6b | |||
| 6363013dd8 | |||
| 934131abf8 | |||
| 4e5337412a | |||
| 00f2e911a7 | |||
| c82d7d3d87 | |||
| ecfea027a0 | |||
| 96be494730 | |||
| 12c8db927b | |||
| 027ea587fc | |||
| 026900c7f0 | |||
| ffa87f55c4 | |||
| c9eed04b51 | |||
| b50b6b9f33 | |||
| 8fd5bef0bd | |||
| d6b49994a2 | |||
| 776927709b | |||
| 96e033b22c | |||
| 3469178dc1 | |||
| af1b3b6368 | |||
| d56789e5a7 | |||
| aeacf3a9d8 | |||
| 4ba486baa2 | |||
| f1ffd90294 | |||
| 777aa8b118 | |||
| cb9e16f4df | |||
| 2908923db9 | |||
| 8c1dfabb6b | |||
| 7fe36de069 | |||
| 27d556735a | |||
| b70d211f21 | |||
| b3e3b6c656 | |||
| 1734286252 | |||
| c8c076f970 | |||
| e40b3ad338 | |||
| afec321db2 | |||
| 6e9f9e6f3c | |||
| f504b200a9 | |||
| 82e840a0ca | |||
| 82765ca96e | |||
| 5387ec5f29 | |||
| f2908cbc5a | |||
| 5337b0b471 | |||
| e51afc3509 | |||
| 2c004dbcc9 | |||
| e128ed7d26 | |||
| 9064ebfe97 | |||
| 4f7bbe0e4a | |||
| 208f168564 | |||
| 737bccbd5e | |||
| 87094ef96c | |||
| f5431a046d | |||
| 5a9422b6bc | |||
| d30198c99a | |||
| 0c8d59dd20 | |||
| a460d140fe | |||
| 881d18ee88 | |||
| d73a4aa34b | |||
| 5c298c1501 | |||
| a83ca2120e | |||
| 81799f7f78 | |||
| 7973f7e750 | |||
| 3dc62a67e0 | |||
| 081e36c909 | |||
| 3a1d364f59 | |||
| e50648279d | |||
| 929af320da | |||
| 8e7c7eeeba | |||
| b739044e69 | |||
| 9e0107c9fc | |||
| 107e016508 | |||
| 22d16c20f8 | |||
| 697c839c84 | |||
| de8bd88d2a | |||
| 595efba747 | |||
| c041d15569 | |||
| 57b20e634b | |||
| e285796fc1 | |||
| a19494d3a7 | |||
| d2a362fd52 | |||
| 0f3562ef02 | |||
| 03f683f9e7 | |||
| cecadce86d | |||
| a7c3906003 | |||
| 70dc0c15fd | |||
| 9eeed9d5bd | |||
| a054c2934e | |||
| 38102f14c1 | |||
| 0997df4fcf | |||
| a5a56e061c | |||
| 8b007ad55a | |||
| 57dd5ec4db | |||
| 5b621d5be1 | |||
| 7aee762f3a | |||
| 4ee50c5a35 | |||
| 28f440dd9e | |||
| 43b78e51a4 | |||
| 84f9fb706b | |||
| 812823cad8 | |||
| 0655260378 | |||
| cfc3723879 | |||
| 4c3281b3f2 | |||
| ff94c605e0 | |||
| cb0a59bb2c | |||
| 076700bd22 | |||
| bcccc8338f | |||
| 425dec8bb8 | |||
| 838554460b | |||
| 659c3c528d | |||
| 60aeab3c38 | |||
| 5e3cf45df3 | |||
| 4633591918 | |||
| 0e6a8b7c72 | |||
| 147848ca20 | |||
| cde002c579 | |||
| f23d93ba89 | |||
| c97a8418dc | |||
| 4aca9372a6 | |||
| 4912ecc3ea | |||
| c1c8ceafc2 | |||
| 9c0a3b6c60 | |||
| 7b539e3025 | |||
| b2b0043875 | |||
| 53e0d182af | |||
| a6fa41e290 | |||
| edba99d636 | |||
| 35674959f2 | |||
| dc8b7a0eb8 | |||
| 8d1f46d837 | |||
| a2117d23b2 | |||
| a58e4d0359 | |||
| 576914aee1 | |||
| 8171f8209a | |||
| 64ff05303c | |||
| 6caab6225d | |||
| 326411498a | |||
| d50fcf0020 | |||
| d354d36a3b | |||
| 483a72ac61 | |||
| dbec4b0d0e | |||
| 4cb489b9e4 | |||
| 28ec50d567 | |||
| 73beed0477 | |||
| e0ecdd4b24 | |||
| 0cb1a578d0 | |||
| 5168cf03a9 | |||
| b886ae6515 | |||
| 277a973b98 | |||
| 0ec4d97bfd | |||
| e6236e5c3e | |||
| e201280844 | |||
| 8e50a6c61b | |||
| 7e6f5f89d0 | |||
| 97acc57649 | |||
| 83c8c48ed7 | |||
| 0815b4d59f | |||
| a0135080b3 | |||
| f45e8e5d47 | |||
| 98ba4beede | |||
| a9304f2fef | |||
| a0697ab057 | |||
| 2e895c72d3 | |||
| 674ebe395f | |||
| 96eecaab54 | |||
| b1ae5bc9fe | |||
| 7258588ed5 | |||
| d913155972 | |||
| 9746e25784 | |||
| d26cfd2141 | |||
| 21f2c06f4b | |||
| 727a25f41f | |||
| 3f27cb2309 | |||
| f213622982 | |||
| 4aad835a66 | |||
| 4af8a64580 | |||
| c9c3611b1d | |||
| 220dfd7440 | |||
| bce8819ed5 | |||
| 5f2b220a85 | |||
| f174c805a9 | |||
| 25869b7504 | |||
| ecd81e8a53 | |||
| d7f9f7f170 | |||
| 08f5259d77 | |||
| a9f744cb64 | |||
| b02e42c6dc | |||
| cb0273b5d7 | |||
| 8dbf45a65a | |||
| 9f64de9568 | |||
| 0ebe9ba487 | |||
| efe00e1aa6 | |||
| 2bdbce55f9 | |||
| 9614ca92f0 | |||
| d30e59f539 | |||
| a3afe617c2 | |||
| 69daac583e | |||
| b28d4b753b | |||
| e6ab4f4856 | |||
| c8ab1b7ee1 | |||
| 9f10487678 | |||
| 2542983d70 | |||
| d492fb3501 | |||
| 1829aa1638 | |||
| 4cb4173ced | |||
| 00e704b216 | |||
| 227ee0f705 | |||
| 17f47a7ab0 | |||
| 4e8bc0786d | |||
| 3d3ccf061f | |||
| 3a8adc6721 | |||
| e1748a5dd1 | |||
| 59b4c889d3 | |||
| b6408d1b3f | |||
| 3db3047f47 | |||
| 7420fbd95b | |||
| 7c990b3833 | |||
| 9c059f1a12 | |||
| fb167d1d0a | |||
| 0992239d86 | |||
| 9eb3da0474 | |||
| e91f8feedf | |||
| 22a0c3fce1 | |||
| 6c7e5748a8 | |||
| 0b0c6da8b0 | |||
| 78826071c0 | |||
| e61b73d7ad | |||
| f886632bf1 | |||
| eafa5098aa | |||
| 0571b80d37 | |||
| 80c10644dd | |||
| 041625381c | |||
| 48f56d728b | |||
| 872d391cfb | |||
| 3e6ec65dd9 | |||
| 157c87b2a2 | |||
| d3081bd889 | |||
| 2ae5bf4395 | |||
| f28547cae1 | |||
| 5332705e31 | |||
| bfb082cda4 | |||
| 37d04198ab | |||
| ae9d540c1c | |||
| c90b7c38f1 | |||
| 9e376fbda7 | |||
| 00739bf06d | |||
| e8148055ad | |||
| dd0a20ab45 | |||
| babbc304b8 | |||
| a8b44f33bd | |||
| bd48561688 | |||
| 23842a8950 | |||
| 1676c643df | |||
| e2bfb024de | |||
| 4d06a54c0c | |||
| 0a8e6169d7 | |||
| 04ae87eaef | |||
| b313d09c2c | |||
| c0d2359a91 | |||
| 51a2f09032 | |||
| f60e8031f2 | |||
| ea42613fec | |||
| b89bb87759 | |||
| 2cbb3d5a24 | |||
| 9e288b7ce8 | |||
| 776b48c10d | |||
| 199dae6cd5 | |||
| e58f45bef7 | |||
| abe122ecb7 | |||
| f8744d87b0 | |||
| 3e7f6b8751 | |||
| 6ffe814ca7 | |||
| eec9be71f6 | |||
| 888913c739 | |||
| 33d96fd28a | |||
| f1e8738af2 | |||
| 9c52a11b1b | |||
| d89c4606bd | |||
| c6903fbcd5 | |||
| 13c8149046 | |||
| 665db0f778 | |||
| 173286de65 | |||
| f2ecda8fec | |||
| 6f1222e9bf | |||
| 84a424f21e | |||
| 9b7710488b | |||
| 9f413862e9 | |||
| b8802d7df7 | |||
| b13dad02a4 | |||
| f045f8d805 | |||
| 30b7c8ad66 | |||
| 89222a0ab2 | |||
| 21e637d2b3 | |||
| 6c196931d2 | |||
| d7195b0798 | |||
| b40998de9e | |||
| 8c0c327df9 | |||
| 50cbb8a1fc | |||
| ff9d058392 | |||
| 1acc8f438b | |||
| c53426fcb4 | |||
| 5187db525f | |||
| a8bd340267 | |||
| ca1f419dc2 | |||
| fae60a6b88 | |||
| 108558ddfc | |||
| 65b8cfae82 | |||
| 4055c31cf0 | |||
| e88af4e458 | |||
| 79eb7b52d9 | |||
| 83b2bf44c4 | |||
| 670f18ad1b | |||
| 9a81716936 | |||
| f013499eea | |||
| d04f732e68 | |||
| 35fd8e7f68 | |||
| 38ff2a3ed9 | |||
| bd502d16bc | |||
| a11e17fbc3 | |||
| 8b3b659433 | |||
| f33228fd6e | |||
| df5b693de8 | |||
| ab98108441 | |||
| 6f80174f84 | |||
| 0ebe81fce2 | |||
| c75dd14308 | |||
| c166b6a21d | |||
| 6ed5cbee12 | |||
| 5b200ccdf8 | |||
| cf0f0c4b31 | |||
| 1399ecb41c | |||
| 339d6cfd41 | |||
| 7bded4f189 | |||
| c7269b9ef0 | |||
| 85688015aa | |||
| 3c6cc575c6 | |||
| 0564cb6483 | |||
| 4be92c7eb8 | |||
| 88e9eb7d0c | |||
| d19fc54c3d | |||
| 5d4291d9fa | |||
| b70cd03e9e | |||
| e91e5e1fe9 | |||
| ae57a49915 | |||
| bfcb527b42 | |||
| a343fb171d | |||
| fa2296a124 | |||
| a996cfc536 | |||
| c1d55b9296 | |||
| 5c18cca1ca | |||
| 78e6cd0c60 | |||
| 176954a6d8 | |||
| ad6b3bd95f | |||
| 762895bc45 | |||
| 592e9afa5f | |||
| a2e0373934 | |||
| fb49841b1d | |||
| 01ea0d6f1e | |||
| bb7f291f5f | |||
| 174fa9b490 | |||
| dda2ed290a | |||
| ee9908b09e | |||
| 66de1856e3 | |||
| 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 | |||
| df233aee94 | |||
| 335b724209 | |||
| b2fdb881eb | |||
| 6ade9acc4d | |||
| 2081f8fcd6 | |||
| 964ab4814c | |||
| 7a032cf9f9 | |||
| 694c13fe86 | |||
| 8bd16c32f3 | |||
| be9b935953 | |||
| 9e69452327 | |||
| 234d529867 | |||
| dd8b71e353 | |||
| d24bebdb9e | |||
| 41a18f078d | |||
| 3978e7e1ca | |||
| b758c696f2 | |||
| d6a8216ce4 | |||
| 2720f64c06 | |||
| de2ebdd5cc | |||
| 4e39629a9a | |||
| 78a8da5fea | |||
| d5886c1572 | |||
| 7cc759a855 | |||
| dd6337224f | |||
| d2bac0c35e | |||
| 8c7f3fd1e6 | |||
| ae3deea153 | |||
| 40bea95fb0 | |||
| 0ad448f1c7 | |||
| 4911df9f99 | |||
| a223340c44 | |||
| 9c9c2b483c | |||
| 819345caa6 | |||
| 11ceb3973d | |||
| 36263399a0 | |||
| ff0f0c447f | |||
| aa681932a9 | |||
| 09e1c0fa27 | |||
| 957ef8e8fe | |||
| 2e11a8da5b | |||
| e9cfcf9ecc | |||
| 789ab99c4d | |||
| 0297db6f2e | |||
| 9ce64916e6 | |||
| 1289c96e2c | |||
| ba23bfb7b9 | |||
| 2fae6eda23 | |||
| e53ba3b116 | |||
| 1ed84a064b | |||
| 79019c7a09 | |||
| a1002e6960 | |||
| 62139cb5a4 | |||
| 127b0ba65e | |||
| 80878264b6 | |||
| 9fcba99ca2 | |||
| 03c9212600 | |||
| 5650087aa3 | |||
| 67689ecb21 | |||
| cd13dedb36 | |||
| 10cd294cf2 | |||
| d6cfb60506 | |||
| df0df73540 | |||
| 33f1418dec | |||
| 305510bea0 | |||
| beb698f31d | |||
| 6df21d6a9f | |||
| c5982e52d5 | |||
| bd73b2845b | |||
| da0f722aad | |||
| 904f0407f8 | |||
| fbbb0d7610 | |||
| 3a9b0a22e7 | |||
| c4e0d1efa1 | |||
| 9349dfba8f | |||
| 9692496989 | |||
| 5bc8164274 | |||
| a6cef2e50e | |||
| d262eda91c | |||
| 40f0f5ad8d | |||
| 1c03e68057 | |||
| f1c1cfb6d2 | |||
| ba5e33bc35 | |||
| 80df9fbc65 | |||
| b68ab0dd6d | |||
| 9cf7a31068 | |||
| 5e11ad2e1e | |||
| 07d1a42768 | |||
| ec8221cb5d | |||
| a5342a0126 | |||
| c81fd2e5dd | |||
| 3bd7122959 | |||
| 530401e5ee | |||
| a412d34574 | |||
| ee67a0b9a1 | |||
| ca6a1db757 | |||
| 1fb76ad768 | |||
| e01662c139 | |||
| 63331ef731 | |||
| a40a53b104 | |||
| 9f8d90f466 | |||
| 3d2856db31 | |||
| f4723aea4c | |||
| 76d48b38d3 | |||
| 3cab2592c3 | |||
| 5422a3b17e | |||
| b44b6e7e50 | |||
| 849efff070 | |||
| b022167df1 | |||
| ac9a358c65 | |||
| e799476f90 | |||
| b4f8efcbe6 | |||
| f026753692 | |||
| 71b1cce517 | |||
| d8f0da164b | |||
| 591732f347 | |||
| eee97f7f62 | |||
| 3dd9da1b66 | |||
| e8c0be23f2 | |||
| a30b9b17b3 | |||
| 29b2c04766 | |||
| d869ba7bcd | |||
| 581255bf23 | |||
| b51358a01c | |||
| 323e7a2d02 | |||
| 7654afc2db | |||
| ded8342f3f | |||
| 240fb9b953 | |||
| 4997a43763 | |||
| aa4eb35671 | |||
| d99ba9c073 | |||
| fdd24f787f | |||
| b6abd691f4 | |||
| 8d370fabdd | |||
| df4a0c62ad | |||
| e3e3309a9b | |||
| 7428e52264 | |||
| 20b70c3b7b | |||
| 4247ba67ed | |||
| e738e93da0 | |||
| b78f3a8069 | |||
| 939973acd7 | |||
| ed4cb72b19 | |||
| 70cbffd58b | |||
| 6d0ba8d189 | |||
| b6f3fa6ee1 | |||
| 91037766d2 | |||
| 8bf73950fa | |||
| 4f4793817c | |||
| 0a0440a6e8 | |||
| b9dc81d808 | |||
| 8e7ddccf00 | |||
| a5773f1657 | |||
| 44316401c9 | |||
| b05fbaacda | |||
| 1b4d5b73ab | |||
| d3fbf36df7 | |||
| de819cff94 | |||
| cfae39c29d | |||
| 989641a616 | |||
| fc3f62e3ed | |||
| 6b7c04e046 | |||
| cfeb16f917 | |||
| 9a2d9002e6 | |||
| ea0b02d9b9 | |||
| 0d621511e5 | |||
| e53c858855 | |||
| 8a9f7fc684 | |||
| 51db46551e | |||
| 600b79276a | |||
| a040be957f | |||
| f6fa553572 | |||
| 8310436350 | |||
| f92ffe60e7 | |||
| de72754d7a | |||
| bf712e9355 | |||
| ab9457346d | |||
| 15b440c4f1 | |||
| 1a2c36e482 | |||
| 56737c1431 | |||
| 9ae566adcc | |||
| f2f1330238 | |||
| c4a7739d13 | |||
| 8a8b5c753f | |||
| ad90f416a5 | |||
| 698fcb7813 | |||
| aadb4db211 | |||
| 426a6a9528 | |||
| 50b9c48609 | |||
| 767ed21bfe | |||
| bb9165edd2 | |||
| ad0a413b40 | |||
| 7f6c6945ae | |||
| ca549939f3 | |||
| cdb003bf23 | |||
| a4d2ff05a9 | |||
| 48012ec73c | |||
| e7dc00b758 | |||
| ef1fbbbce6 | |||
| 2a59aebe5b | |||
| 59025b75ba | |||
| 2289b7a33d | |||
| 79ec172797 | |||
| 2e6ad2a711 | |||
| 6be05f315d | |||
| b5aa50aaa4 | |||
| ab91fa6ad5 | |||
| 376327c87b | |||
| f8f91e52e0 | |||
| f4daf46ff4 | |||
| e80bee6867 | |||
| d10d54710c | |||
| 1ec997461d | |||
| ec5fc10988 | |||
| a232c0888c | |||
| cb5a6b531a | |||
| c930a3b4c8 | |||
| 4c14e92952 | |||
| 850d4a1e1b | |||
| 88de3a1c06 | |||
| dc012ed6dd | |||
| c21c993646 | |||
| c3a292a8c7 | |||
| 4044a577cc | |||
| 581d53b96b | |||
| 2bc89260f1 | |||
| c78b83f142 | |||
| e28525e28c | |||
| 73f9d12d47 | |||
| b21cf05d44 | |||
| 107bede9fd | |||
| 75cbb09744 | |||
| 76cf667a29 | |||
| 78ee97ec74 | |||
| c686133172 | |||
| bfcd7a35bf | |||
| 4484a3433d | |||
| 0c4f905d82 | |||
| 77de7ebde5 | |||
| 2ec3fa93b4 | |||
| 9f2d710c35 | |||
| 22b961ea53 | |||
| 9ea45d35db | |||
| 06e8476efc | |||
| 94dbac9a64 | |||
| 97a183f412 | |||
| b2f5b73532 | |||
| 1eac3482a6 | |||
| 6636376a81 | |||
| ed6bf28004 | |||
| 6bc0c611ab | |||
| ba1930eb01 | |||
| 203382461b | |||
| 6456618891 | |||
| 4eb4ae6305 | |||
| 72ae061769 | |||
| 46161f7e19 | |||
| 0c55596f0f | |||
| 5f3bfa66c5 | |||
| 561b725b0e | |||
| 3a4630e6b4 | |||
| abf0fd7efc | |||
| a632db3618 | |||
| a3c81374be | |||
| 6a3ec5eb36 | |||
| 740ba6ad47 | |||
| df32b5b46c | |||
| 085fa199ea | |||
| 412c9a99d5 | |||
| 6e701ef36d | |||
| 24c48d22bc | |||
| 3cb8bb6672 | |||
| b1c2c0ea7a | |||
| 2c498c132e | |||
| 880d330cca | |||
| a2a503847a | |||
| 58f4d533b7 | |||
| d2ff6f424d | |||
| 92f985abd5 | |||
| 3ce17607c6 | |||
| 76277f83c1 | |||
| 2b7529977e | |||
| f4125d2d88 | |||
| 87e50e5e4d | |||
| 86a1c34c3a | |||
| 3f3cc342b4 | |||
| 3bf820cf99 | |||
| f2b4087d80 | |||
| 3b6d72bb94 | |||
| 9080fa4a9d | |||
| 5616ff9a40 | |||
| 73b81184fa | |||
| f8d3f86d8b | |||
| 2f9a410a45 | |||
| 8661457512 | |||
| 5d7b92d391 | |||
| 1d8bc3e917 | |||
| a2ad16b609 | |||
| a3b1ac3133 | |||
| e7b96cf286 | |||
| 01181517dc | |||
| f702c782f1 | |||
| 7203560b06 | |||
| 1baeb9406f | |||
| 17e36bd5e1 | |||
| b6b3377786 | |||
| 13cb894b30 | |||
| 3f935bea25 | |||
| 3e66eec735 | |||
| 277e0ac124 | |||
| 2ccfaa7d4e | |||
| 4bd5de34ea | |||
| 374e71e9b0 | |||
| 07bb93bb5d | |||
| e252d3bedf | |||
| b9efd09d17 | |||
| 507b718cb3 | |||
| 82f9cbecf8 | |||
| a8ac59a6e7 | |||
| 3d389ee028 | |||
| 10c5825715 | |||
| f89ebce807 | |||
| 64601ac439 | |||
| edce27812f | |||
| 193822b45d | |||
| 43640a8b59 | |||
| 0446d9721b | |||
| bae13b6387 | |||
| e48c0eee74 | |||
| 47e9857eb7 | |||
| 559fcfa291 | |||
| 84cee5d9d5 | |||
| 6d354524e2 | |||
| ae6441182d | |||
| a68f0b2d72 | |||
| fdbbf24271 | |||
| df233f72a9 | |||
| bff5a67f79 | |||
| 4f9df50dc1 | |||
| 34187424b8 | |||
| 50503cb405 | |||
| 5e04ddd653 | |||
| 4f5203e661 | |||
| d9ca4eb4d6 | |||
| 5534c031b3 | |||
| 19dc84e300 | |||
| a932168f50 | |||
| 36c22393a4 | |||
| 6d6e840bc2 | |||
| 4b1822ade8 | |||
| b21e7e4518 | |||
| 1f4e5e919f | |||
| c293e88f2e | |||
| 1d147ba993 | |||
| 6ea9ba16e7 | |||
| 286549693e | |||
| 34727f99e3 | |||
| ffe6d81ecd | |||
| 8605833781 | |||
| 4474144c24 | |||
| ef3f448861 | |||
| a882260db6 | |||
| 633157f4f8 | |||
| 9fa69c3d3b | |||
| 743a461aa9 | |||
| fc0291d745 | |||
| 77eaf8e1e4 | |||
| a7adb2fb6e | |||
| 036900da51 | |||
| ed4c9335db | |||
| ca67cf032c | |||
| f907516cbd | |||
| c12c7d5370 | |||
| f7c8b40ea2 | |||
| 15f9795ab0 | |||
| 8982ae34e3 | |||
| e6d3e893a5 | |||
| e008b5a160 | |||
| 3da8fa9b27 | |||
| 32ba5e7ad2 | |||
| 52df80dccd | |||
| 7f845bb165 | |||
| 0e6de5673b | |||
| 7a7b87181d | |||
| c6dc517004 | |||
| 0b61215f7b | |||
| b91c0ec715 | |||
| bad295cf69 | |||
| d2bc41a2df | |||
| 7dbcaf792d | |||
| 2652c2d7a5 | |||
| a2250a5d49 | |||
| 7f8a9587e0 | |||
| 1306c53fb1 | |||
| 3bd1ac4c82 | |||
| dc8d28c383 | |||
| 7adaa4dc2b | |||
| 6d1a91f5b3 | |||
| 17eebf338c | |||
| c543ecd64c | |||
| 34a9f55f37 | |||
| 9aea990184 | |||
| d5b0632e4f | |||
| db169a4334 | |||
| f5cc8bd7bf | |||
| 005d52cab7 | |||
| d1477bcfa7 | |||
| 3092fb2ff3 | |||
| 5eebdebec8 | |||
| 8e4d6b3e5d | |||
| ea9c2fed57 | |||
| ba412fd87b | |||
| 0278ac85a0 | |||
| ff60b752bd | |||
| 832003dd4b | |||
| 9848e883c7 | |||
| 64705ddd1d | |||
| 2a41814985 | |||
| 26ffec845b | |||
| 52e60526ef | |||
| 76b10b5f5d | |||
| b94a7a87fa | |||
| 2b43387a9d | |||
| e76a5d8e12 | |||
| 6d7217f37a | |||
| 17dab04422 | |||
| 29e660b16f | |||
| 31959b0751 | |||
| 1f19610fd6 | |||
| 8f897de267 | |||
| 2855ff6df3 | |||
| deed20dea6 | |||
| a6c5143993 | |||
| 758d1e2a03 | |||
| ce057ff755 | |||
| ad719e7c3a | |||
| bff3426d25 | |||
| 4315033220 | |||
| 1cd89b2da3 | |||
| 658a605c75 | |||
| 7e8b9862b9 | |||
| 07062324d7 | |||
| 2e8f2e6dbc | |||
| 1abd95094d | |||
| 913d802e33 | |||
| bee475c38a | |||
| b4ca99ead9 | |||
| dfe2c0a600 | |||
| fad851d80c | |||
| 832961d539 | |||
| 499c657ffa | |||
| e51bb4ef12 | |||
| 8c6f39a68d | |||
| 09f5713cf8 | |||
| 26c0c6a525 | |||
| 6d9f84ba03 | |||
| 8af08f2153 | |||
| 2944969ca0 | |||
| bd1b54e0db | |||
| fcab5508be | |||
| 0b05650366 | |||
| 96e36c7c39 | |||
| 16c6dbcbe5 | |||
| 92a78c83d9 | |||
| 6b2302fa8b | |||
| ee28945e09 | |||
| 3dcea60f5b | |||
| f126e05034 | |||
| 68b74eb7c7 | |||
| 3b4199a669 | |||
| 562901aedf | |||
| 793117ed63 | |||
| 5b783d6376 | |||
| d3f3528d1d | |||
| 3145935d6b | |||
| 25430333ba | |||
| 2ca2b32dd0 | |||
| a5dde78f08 | |||
| fd415f0b45 | |||
| 507722954c | |||
| 524d23d45d | |||
| e9ee6f5291 | |||
| 7e5342f41f | |||
| 18607e53cb | |||
| ed933b3f21 | |||
| 49fecbdc5e | |||
| f971126183 | |||
| d4ccb69ccc | |||
| 37b4e0de6c | |||
| 737f440c7f | |||
| cba3f1e374 | |||
| a70dde34da | |||
| 410b85b5c7 | |||
| 0ae012ba08 | |||
| fc4eb4152c | |||
| 4f3b5d8dcb | |||
| f76f70c7cf | |||
| 15d783e920 | |||
| 09e4fff5b1 | |||
| 2d89faa17c | |||
| dd9843aa21 | |||
| 882116e358 | |||
| f3adbae1ed | |||
| 278e239973 | |||
| fda803b46a | |||
| 01162e08b5 | |||
| a7ae197a55 | |||
| 3c72cb67d3 | |||
| 37bba4c0a6 | |||
| ab9d1f99fd | |||
| 45124e4d5c | |||
| 7681c43b14 | |||
| 7e43cd7d97 | |||
| c223fc1766 | |||
| 04297bb680 | |||
| 553292ffd0 | |||
| 57862846a2 | |||
| 8e270d3a99 | |||
| ae5cb09041 | |||
| 093b2288c3 | |||
| ffe17a471d | |||
| e9f901b82d | |||
| ed3004f8a0 | |||
| d97df080f9 | |||
| 0e5c7e08fc | |||
| 83523badb7 | |||
| 0a90994403 | |||
| 376906e0ae | |||
| 47c79a2f25 | |||
| 32988b0363 | |||
| 00c138ce9f | |||
| 5676c9e7eb | |||
| 3a469dc13e | |||
| d3c70f2206 | |||
| 14f1793b3e | |||
| 8cecb6b9f5 | |||
| 4a66c3c420 | |||
| c3c88633a5 | |||
| aeaf1199ec | |||
| d4f62f52db | |||
| 384fb76a1b | |||
| 4a04a32e0a | |||
| 196bd735d4 | |||
| 493bc653b5 | |||
| 3d209798c9 | |||
| dd0d61e97c | |||
| 4b9324ff76 | |||
| e81ed9a960 | |||
| 83f7a887b7 | |||
| ad2f1ac24e | |||
| 62d232d798 | |||
| 8906a0120c | |||
| cad753e398 | |||
| fd627dc13b | |||
| 70e8d97ee1 | |||
| cdecb0ccc3 | |||
| 460b5149af | |||
| b6dc253d8b | |||
| e7be9493ba | |||
| ea34f321ed | |||
| 2b5bc1d558 | |||
| e6a7b85da4 | |||
| 6145185478 | |||
| 6630d703f8 | |||
| 9c3cdc4620 | |||
| 345032f804 | |||
| 2abba6e057 | |||
| db5a1b0c78 | |||
| 14cb19c2df | |||
| 46bcd18946 | |||
| 3e5c60f746 | |||
| 0fa487f468 | |||
| 1bec9e5331 | |||
| ba61d911da | |||
| 3b69c6b204 | |||
| d7eabf571c | |||
| ddf9c4a65b | |||
| b3d797598e | |||
| 31c7945444 | |||
| 276e014d18 | |||
| 27f206784c | |||
| 54a6637d38 | |||
| 23be56af59 | |||
| 71df46456a | |||
| cd89d8a3c4 | |||
| 4c62a32b04 | |||
| 5f8137025d | |||
| 1843d52217 | |||
| 454c92dc64 | |||
| 7e33a86d54 | |||
| 197b832992 | |||
| 8f13724a4b | |||
| 746d5fc322 | |||
| ffc45e8cc2 | |||
| bea2f36443 | |||
| f138f71fa6 | |||
| 89b7a3f7ac | |||
| 75e15b05b0 | |||
| 78eb388110 | |||
| df23cf47c6 | |||
| 002ac6a1b7 | |||
| 40e4536887 | |||
| 536bf61323 | |||
| c76bdced55 | |||
| 459ea5f4f6 | |||
| 8e8a075a22 | |||
| db6bd9b358 | |||
| 42ad54c28e | |||
| 1857bc7b02 | |||
| e011d812ca | |||
| 0738822dda | |||
| 2213722776 | |||
| 65dedbb1ca | |||
| 0e69993d39 | |||
| 135091ddbe | |||
| d64e3b672c | |||
| a3bcacee27 | |||
| 855e7beab1 | |||
| edc13c29df | |||
| 21864d8d51 | |||
| d45ff0694d | |||
| d695a8a526 | |||
| 0380a288a9 | |||
| 1d4d0a3e1a | |||
| 9e98494fff | |||
| 86d334282c | |||
| 9cf937fef0 | |||
| f5697dd7f2 | |||
| a23ee1edc1 | |||
| 9dcb5c075a | |||
| 803648be89 | |||
| 3ab5db8297 | |||
| 3380ece4a1 | |||
| 0e5928ff39 | |||
| e6e04fc6c8 | |||
| 753cceea82 | |||
| 32c7e81745 | |||
| 03aec70287 | |||
| e69738c079 | |||
| 1afa7967f2 | |||
| 643e36b87b | |||
| 3d2405ac2c | |||
| 7392a3047a | |||
| 60f4d8f1ec | |||
| 7b42cbea20 | |||
| 4cc597f4df | |||
| 934e66ab3b | |||
| b755609438 | |||
| 83d63e572a | |||
| 5212f62f54 | |||
| 4d0fd4cf19 | |||
| e47953f7ca | |||
| 47f3773146 | |||
| 31c6ecad34 | |||
| af6e53c05c | |||
| 153140eb8f | |||
| 6fef44c041 | |||
| a88d149903 | |||
| f9fc488399 | |||
| bef806bef4 | |||
| 95e9bbf99f | |||
| 5936fa8871 | |||
| 02646b789c | |||
| 09e9dca869 | |||
| 9a43c0672e | |||
| 83a6169463 | |||
| debe2de5fe | |||
| ff7d591ebf | |||
| 7386ca9272 | |||
| fd8b2e0b88 | |||
| c34ae884ad | |||
| 2e7157ae9c | |||
| 441365b388 | |||
| f561147190 | |||
| 2958c1d6aa | |||
| 9dc83bc1b3 | |||
| 88b1b2c629 | |||
| 23bc643a81 | |||
| 41854bacf5 | |||
| cf528431f5 | |||
| e59064dd59 | |||
| 8966294823 | |||
| b8479ea79d | |||
| e2aa8f426d | |||
| 39a0f8c96a | |||
| b647b45ba5 | |||
| 5fe9aa919b | |||
| ff5d6a994b | |||
| ae3b95b194 | |||
| acaae1357c | |||
| f8afda3b22 | |||
| 31f544c258 | |||
| 1c57d1c019 | |||
| 251edf7bc7 | |||
| f77cd5533d | |||
| 416413bebf | |||
| c3809d7b84 | |||
| 42a1c58a80 | |||
| b6abaf739c | |||
| ef98e92e8d | |||
| 768c2684d0 | |||
| 5f2514db63 | |||
| b95ade40c0 | |||
| 340838c878 | |||
| c5d348515d | |||
| 05dd3d490d | |||
| 0cc40db565 | |||
| 546faab0cb | |||
| 83e9a6b417 | |||
| 30bb2382aa | |||
| 61a0b4ec5a | |||
| accb35506f | |||
| 817bc7434d | |||
| 3c2ed3bb69 | |||
| 9cbf46e689 | |||
| ad6ea3d6aa | |||
| cda9fd5271 | |||
| 0c16f27814 | |||
| 19aec13a10 | |||
| e896956275 | |||
| c59c6e98a5 | |||
| 8b1100bf2b | |||
| a3d99765cc | |||
| a724573bb3 | |||
| 25769f139a | |||
| 3edf638cc6 | |||
| de7e612186 | |||
| a571153458 | |||
| ada58c66fa | |||
| 697f8c7ee6 | |||
| b6ebfe4b2c | |||
| bccbdefde9 | |||
| 445ca70521 | |||
| c6ab8f82c8 | |||
| 67ce0ec29f | |||
| 23c3573c30 | |||
| a4308e7246 | |||
| 3439139b1c | |||
| cf246f65ff | |||
| dd84b61cc8 | |||
| b8c4bf2afb | |||
| e870041fe6 | |||
| 6418ec3b21 | |||
| 2bcc7b0064 | |||
| 97be867103 | |||
| fb710f8cbf | |||
| 1553137c23 | |||
| d5384c5aa4 | |||
| 3a81f2ab89 | |||
| b54fc96b1e | |||
| 48af78e469 | |||
| a315e7c962 | |||
| 42364f2fce | |||
| 8f600798ef | |||
| abb26e0bea | |||
| 76edfae0e0 | |||
| a5298e17ec | |||
| cf9f3d5e2d | |||
| 35c90fe124 | |||
| df8bdac33f | |||
| b4f7a527c2 | |||
| 35533a7baa | |||
| 2c9ed7464f | |||
| e190c024fd | |||
| e250475bf9 | |||
| 5db603ded2 | |||
| 78815778ee | |||
| ff5e036773 | |||
| 4dc29d141f | |||
| 8ecee32e1c | |||
| 6a7d821fcc | |||
| f28c268d97 | |||
| f1cff20249 | |||
| 1243b1a58c | |||
| ab3bae5c02 | |||
| 540c5400a0 | |||
| 01e29bf27e | |||
| 6223f48c3f | |||
| f2f20def37 | |||
| 77b91352ae | |||
| 3d7d347192 | |||
| e5868e3205 | |||
| 96d7c4ffdf | |||
| f19325cbe0 | |||
| d57ec4a11d | |||
| 865d88dd56 | |||
| 069c05669f | |||
| 516065d7c2 | |||
| ebdb3ab43a | |||
| 29ca6ee420 | |||
| 6e61abc7d0 | |||
| 7ec88d2430 | |||
| d7200f6144 | |||
| d65d6edb0e | |||
| 3d72e80ccf | |||
| 2dd67dba89 | |||
| 5807214406 | |||
| 23baf56c87 | |||
| beff90e1d1 | |||
| ec63d0bbd2 | |||
| 32eab04d66 | |||
| 682783a2aa | |||
| 46a4927aca | |||
| 9b2e67df67 | |||
| b5c828fe4e | |||
| a4ba91a554 | |||
| cf390bf8b9 |
@@ -1,3 +1,4 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: odin-lang
|
||||||
patreon: gingerbill
|
patreon: gingerbill
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on: [push, pull_request]
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_linux:
|
build_linux:
|
||||||
@@ -7,9 +7,9 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Download LLVM, botan
|
- name: Download LLVM, botan
|
||||||
run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan
|
run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make release
|
run: ./build_odin.sh release
|
||||||
- name: Odin version
|
- name: Odin version
|
||||||
run: ./odin version
|
run: ./odin version
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
@@ -38,6 +38,15 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Linux i386
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for FreeBSD amd64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for OpenBSD amd64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||||
|
timeout-minutes: 10
|
||||||
build_macOS:
|
build_macOS:
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -49,7 +58,7 @@ jobs:
|
|||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make release
|
run: ./build_odin.sh release
|
||||||
- name: Odin version
|
- name: Odin version
|
||||||
run: ./odin version
|
run: ./odin version
|
||||||
timeout-minutes: 1
|
timeout-minutes: 1
|
||||||
@@ -78,8 +87,19 @@ jobs:
|
|||||||
cd tests/vendor
|
cd tests/vendor
|
||||||
make
|
make
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin issues tests
|
||||||
|
run: |
|
||||||
|
cd tests/issues
|
||||||
|
./run.sh
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Darwin arm64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Linux arm64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||||
|
timeout-minutes: 10
|
||||||
build_windows:
|
build_windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: build Odin
|
- name: build Odin
|
||||||
@@ -138,3 +158,16 @@ jobs:
|
|||||||
cd tests\core\math\big
|
cd tests\core\math\big
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
- name: Odin issues tests
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
cd tests\issues
|
||||||
|
call run.bat
|
||||||
|
timeout-minutes: 10
|
||||||
|
- name: Odin check examples/all for Windows 32bits
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
odin check examples/all -strict-style -target:windows_i386
|
||||||
|
timeout-minutes: 10
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_windows:
|
build_windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-2019
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: build Odin
|
- name: build Odin
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin run examples/demo/demo.odin
|
odin run examples/demo
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
rm bin/llvm/windows/LLVM-C.lib
|
rm bin/llvm/windows/LLVM-C.lib
|
||||||
@@ -41,15 +41,16 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: (Linux) Download LLVM
|
- name: (Linux) Download LLVM
|
||||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
run: sudo apt-get install llvm-11 clang-11
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo/demo.odin
|
run: ./odin run examples/demo
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
mkdir dist
|
||||||
cp odin dist
|
cp odin dist
|
||||||
|
cp libLLVM* dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
@@ -72,7 +73,7 @@ jobs:
|
|||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo/demo.odin
|
run: ./odin run examples/demo
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
mkdir dist
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: "Close Stale Issues & PRs"
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 21 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Close Stale Issues
|
||||||
|
uses: actions/stale@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"
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
|
# For macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
[Dd]ebug/
|
[Dd]ebug/
|
||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
@@ -266,6 +269,9 @@ bin/
|
|||||||
# - Linux/MacOS
|
# - Linux/MacOS
|
||||||
odin
|
odin
|
||||||
odin.dSYM
|
odin.dSYM
|
||||||
|
*.bin
|
||||||
|
demo.bin
|
||||||
|
libLLVM*.so*
|
||||||
|
|
||||||
# shared collection
|
# shared collection
|
||||||
shared/
|
shared/
|
||||||
@@ -276,3 +282,6 @@ shared/
|
|||||||
*.ll
|
*.ll
|
||||||
|
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
examples/bug/
|
||||||
|
build.sh
|
||||||
|
!core/debug/
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|||||||
@@ -1,80 +1,19 @@
|
|||||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
all: debug
|
||||||
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
|
|
||||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
|
||||||
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
|
||||||
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
|
||||||
CC=clang
|
|
||||||
|
|
||||||
OS=$(shell uname)
|
|
||||||
|
|
||||||
ifeq ($(OS), Darwin)
|
|
||||||
|
|
||||||
ARCH=$(shell uname -m)
|
|
||||||
LLVM_CONFIG=
|
|
||||||
|
|
||||||
# allow for arm only llvm's with version 13
|
|
||||||
ifeq ($(ARCH), arm64)
|
|
||||||
LLVM_VERSIONS = "13.%.%"
|
|
||||||
else
|
|
||||||
# allow for x86 / amd64 all llvm versions begining from 11
|
|
||||||
LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
|
|
||||||
endif
|
|
||||||
|
|
||||||
LLVM_VERSION_PATTERN_SEPERATOR = )|(
|
|
||||||
LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
|
|
||||||
LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
|
|
||||||
LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
|
|
||||||
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
|
|
||||||
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
|
|
||||||
|
|
||||||
ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),)
|
|
||||||
LLVM_CONFIG=llvm-config
|
|
||||||
else
|
|
||||||
ifeq ($(ARCH), arm64)
|
|
||||||
$(error "Requirement: llvm-config must be base version 13 for arm64")
|
|
||||||
else
|
|
||||||
$(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
LDFLAGS:=$(LDFLAGS) -liconv
|
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
|
||||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
|
||||||
endif
|
|
||||||
ifeq ($(OS), Linux)
|
|
||||||
LLVM_CONFIG=llvm-config-11
|
|
||||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
|
||||||
LLVM_CONFIG=llvm-config-11
|
|
||||||
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
|
||||||
LLVM_CONFIG=llvm-config-11-64
|
|
||||||
else
|
|
||||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
|
||||||
LLVM_CONFIG=llvm-config
|
|
||||||
else
|
|
||||||
$(error "Requirement: llvm-config must be version 11")
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
|
||||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
|
||||||
endif
|
|
||||||
|
|
||||||
all: debug demo
|
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
./odin run examples/demo/demo.odin
|
./odin run examples/demo/demo.odin -file
|
||||||
|
|
||||||
report:
|
report:
|
||||||
./odin report
|
./odin report
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
./build_odin.sh debug
|
||||||
|
|
||||||
release:
|
release:
|
||||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
|
./build_odin.sh release
|
||||||
|
|
||||||
release_native:
|
release_native:
|
||||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
./build_odin.sh release-native
|
||||||
|
|
||||||
nightly:
|
nightly:
|
||||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
./build_odin.sh nightly
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||||
</a>
|
</a>
|
||||||
<br>
|
<br>
|
||||||
<a href="https://discord.gg/hnwN2Rj">
|
<a href="https://discord.gg/odinlang">
|
||||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/odin-lang/odin/actions">
|
<a href="https://github.com/odin-lang/odin/actions">
|
||||||
@@ -58,6 +58,10 @@ main :: proc() {
|
|||||||
|
|
||||||
Instructions for downloading and installing the Odin compiler and libraries.
|
Instructions for downloading and installing the Odin compiler and libraries.
|
||||||
|
|
||||||
|
#### [Nightly Builds](https://odin-lang.org/docs/nightly/)
|
||||||
|
|
||||||
|
Get the latest nightly builds of Odin.
|
||||||
|
|
||||||
### Learning Odin
|
### Learning Odin
|
||||||
|
|
||||||
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
||||||
@@ -68,6 +72,10 @@ An overview of the Odin programming language.
|
|||||||
|
|
||||||
Answers to common questions about Odin.
|
Answers to common questions about Odin.
|
||||||
|
|
||||||
|
#### [Packages](https://pkg.odin-lang.org/)
|
||||||
|
|
||||||
|
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||||
|
|
||||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||||
|
|
||||||
A wiki maintained by the Odin community.
|
A wiki maintained by the Odin community.
|
||||||
@@ -76,12 +84,6 @@ A wiki maintained by the Odin community.
|
|||||||
|
|
||||||
Get live support and talk with other odiners on the Odin Discord.
|
Get live support and talk with other odiners on the Odin Discord.
|
||||||
|
|
||||||
### References
|
|
||||||
|
|
||||||
#### [Language Specification](https://odin-lang.org/docs/spec/)
|
|
||||||
|
|
||||||
The official Odin Language specification.
|
|
||||||
|
|
||||||
### Articles
|
### Articles
|
||||||
|
|
||||||
#### [The Odin Blog](https://odin-lang.org/news/)
|
#### [The Odin Blog](https://odin-lang.org/news/)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ set libs= ^
|
|||||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set linker_flags=%linker_flags% -debug
|
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||||
) else ( rem Release
|
) else ( rem Release
|
||||||
set linker_flags=%linker_flags% -debug
|
set linker_flags=%linker_flags% -debug
|
||||||
)
|
)
|
||||||
|
|||||||
Executable
+179
@@ -0,0 +1,179 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
: ${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"
|
||||||
|
OS=$(uname)
|
||||||
|
|
||||||
|
panic() {
|
||||||
|
printf "%s\n" "$1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
|
||||||
|
|
||||||
|
config_darwin() {
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
: ${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 beginning from 11
|
||||||
|
MIN_LLVM_VERSION=("11.1.0")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
|
||||||
|
if [ ARCH == arm64 ]; then
|
||||||
|
panic "Requirement: llvm-config must be base version 13 for arm64"
|
||||||
|
else
|
||||||
|
panic "Requirement: llvm-config must be base version greater than 11 for amd64/x86"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||||
|
}
|
||||||
|
|
||||||
|
config_freebsd() {
|
||||||
|
: ${LLVM_CONFIG=}
|
||||||
|
|
||||||
|
if [ ! "$LLVM_CONFIG" ]; then
|
||||||
|
if which llvm-config11 > /dev/null 2>&1; then
|
||||||
|
LLVM_CONFIG=llvm-config11
|
||||||
|
elif which llvm-config12 > /dev/null 2>&1; then
|
||||||
|
LLVM_CONFIG=llvm-config12
|
||||||
|
elif which llvm-config13 > /dev/null 2>&1; then
|
||||||
|
LLVM_CONFIG=llvm-config13
|
||||||
|
else
|
||||||
|
panic "Unable to find LLVM-config"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
|
}
|
||||||
|
|
||||||
|
config_openbsd() {
|
||||||
|
: ${LLVM_CONFIG=/usr/local/bin/llvm-config}
|
||||||
|
|
||||||
|
LDFLAGS="$LDFLAGS -liconv"
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
|
}
|
||||||
|
|
||||||
|
config_linux() {
|
||||||
|
: ${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")
|
||||||
|
if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
|
||||||
|
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||||
|
panic "Requirement: llvm-config must be base version greater than 11"
|
||||||
|
fi
|
||||||
|
|
||||||
|
LDFLAGS="$LDFLAGS -ldl"
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
|
||||||
|
|
||||||
|
# Creates a copy of the llvm library in the build dir, this is meant to support compiler explorer.
|
||||||
|
# The annoyance is that this copy can be cluttering the development folder. TODO: split staging folders
|
||||||
|
# for development and compiler explorer builds
|
||||||
|
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||||
|
}
|
||||||
|
|
||||||
|
build_odin() {
|
||||||
|
case $1 in
|
||||||
|
debug)
|
||||||
|
EXTRAFLAGS="-g"
|
||||||
|
;;
|
||||||
|
release)
|
||||||
|
EXTRAFLAGS="-O3"
|
||||||
|
;;
|
||||||
|
release-native)
|
||||||
|
EXTRAFLAGS="-O3 -march=native"
|
||||||
|
;;
|
||||||
|
nightly)
|
||||||
|
EXTRAFLAGS="-DNIGHTLY -O3"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
panic "Build mode unsupported!"
|
||||||
|
esac
|
||||||
|
|
||||||
|
set -x
|
||||||
|
$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||||
|
set +x
|
||||||
|
}
|
||||||
|
|
||||||
|
run_demo() {
|
||||||
|
./odin run examples/demo/demo.odin -file
|
||||||
|
}
|
||||||
|
|
||||||
|
case $OS in
|
||||||
|
Linux)
|
||||||
|
config_linux
|
||||||
|
;;
|
||||||
|
Darwin)
|
||||||
|
config_darwin
|
||||||
|
;;
|
||||||
|
OpenBSD)
|
||||||
|
config_openbsd
|
||||||
|
;;
|
||||||
|
FreeBSD)
|
||||||
|
config_freebsd
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
panic "Platform unsupported!"
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ $# -eq 0 ]]; then
|
||||||
|
build_odin debug
|
||||||
|
run_demo
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $# -eq 1 ]]; then
|
||||||
|
case $1 in
|
||||||
|
report)
|
||||||
|
if [[ ! -f "./odin" ]]; then
|
||||||
|
build_odin debug
|
||||||
|
fi
|
||||||
|
|
||||||
|
./odin report
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
build_odin $1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
run_demo
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
panic "Too many arguments!"
|
||||||
|
fi
|
||||||
@@ -15,20 +15,16 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
|||||||
|
|
||||||
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||||
s.stream_data = rw
|
s.stream_data = rw
|
||||||
s.stream_vtable = _read_writer_vtable
|
s.stream_vtable = &_read_writer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_read_writer_vtable := &io.Stream_VTable{
|
_read_writer_vtable := io.Stream_VTable{
|
||||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
b := (^Read_Writer)(s.stream_data).r
|
b := (^Read_Writer)(s.stream_data).r
|
||||||
return reader_read(b, p)
|
return reader_read(b, p)
|
||||||
},
|
},
|
||||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
|
||||||
b := (^Read_Writer)(s.stream_data).r
|
|
||||||
return reader_read_byte(b)
|
|
||||||
},
|
|
||||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Read_Writer)(s.stream_data).r
|
b := (^Read_Writer)(s.stream_data).r
|
||||||
return reader_unread_byte(b)
|
return reader_unread_byte(b)
|
||||||
|
|||||||
@@ -353,14 +353,14 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
|||||||
// reader_to_stream converts a Reader into an io.Stream
|
// reader_to_stream converts a Reader into an io.Stream
|
||||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _reader_vtable
|
s.stream_vtable = &_reader_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_reader_vtable := &io.Stream_VTable{
|
_reader_vtable := io.Stream_VTable{
|
||||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Reader)(s.stream_data)
|
b := (^Reader)(s.stream_data)
|
||||||
reader_destroy(b)
|
reader_destroy(b)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import "core:intrinsics"
|
|||||||
|
|
||||||
// Extra errors returns by scanning procedures
|
// Extra errors returns by scanning procedures
|
||||||
Scanner_Extra_Error :: enum i32 {
|
Scanner_Extra_Error :: enum i32 {
|
||||||
|
None,
|
||||||
Negative_Advance,
|
Negative_Advance,
|
||||||
Advanced_Too_Far,
|
Advanced_Too_Far,
|
||||||
Bad_Read_Count,
|
Bad_Read_Count,
|
||||||
@@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 {
|
|||||||
Too_Short,
|
Too_Short,
|
||||||
}
|
}
|
||||||
|
|
||||||
Scanner_Error :: union {
|
Scanner_Error :: union #shared_nil {
|
||||||
io.Error,
|
io.Error,
|
||||||
Scanner_Extra_Error,
|
Scanner_Extra_Error,
|
||||||
}
|
}
|
||||||
@@ -65,10 +66,10 @@ scanner_destroy :: proc(s: ^Scanner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns the first non-EOF error that was encounted by the scanner
|
// Returns the first non-EOF error that was encountered by the scanner
|
||||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||||
switch s._err {
|
switch s._err {
|
||||||
case .EOF, .None:
|
case .EOF, nil:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return s._err
|
return s._err
|
||||||
@@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string {
|
|||||||
// scanner_scan advances the scanner
|
// scanner_scan advances the scanner
|
||||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||||
err := err
|
|
||||||
if err == .None {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
switch s._err {
|
switch s._err {
|
||||||
case nil, .EOF:
|
case nil, .EOF:
|
||||||
s._err = err
|
s._err = err
|
||||||
|
|||||||
@@ -223,14 +223,14 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
|||||||
// writer_to_stream converts a Writer into an io.Stream
|
// writer_to_stream converts a Writer into an io.Stream
|
||||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _writer_vtable
|
s.stream_vtable = &_writer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_writer_vtable := &io.Stream_VTable{
|
_writer_vtable := io.Stream_VTable{
|
||||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||||
b := (^Writer)(s.stream_data)
|
b := (^Writer)(s.stream_data)
|
||||||
writer_destroy(b)
|
writer_destroy(b)
|
||||||
|
|||||||
+66
-66
@@ -1,90 +1,90 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
nil :: nil;
|
nil :: nil
|
||||||
false :: 0!=0;
|
false :: 0!=0
|
||||||
true :: 0==0;
|
true :: 0==0
|
||||||
|
|
||||||
ODIN_OS :: ODIN_OS;
|
ODIN_OS :: ODIN_OS
|
||||||
ODIN_ARCH :: ODIN_ARCH;
|
ODIN_ARCH :: ODIN_ARCH
|
||||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||||
ODIN_VENDOR :: ODIN_VENDOR;
|
ODIN_VENDOR :: ODIN_VENDOR
|
||||||
ODIN_VERSION :: ODIN_VERSION;
|
ODIN_VERSION :: ODIN_VERSION
|
||||||
ODIN_ROOT :: ODIN_ROOT;
|
ODIN_ROOT :: ODIN_ROOT
|
||||||
ODIN_DEBUG :: ODIN_DEBUG;
|
ODIN_DEBUG :: ODIN_DEBUG
|
||||||
|
|
||||||
byte :: u8; // alias
|
byte :: u8 // alias
|
||||||
|
|
||||||
bool :: bool;
|
bool :: bool
|
||||||
b8 :: b8;
|
b8 :: b8
|
||||||
b16 :: b16;
|
b16 :: b16
|
||||||
b32 :: b32;
|
b32 :: b32
|
||||||
b64 :: b64;
|
b64 :: b64
|
||||||
|
|
||||||
i8 :: i8;
|
i8 :: i8
|
||||||
u8 :: u8;
|
u8 :: u8
|
||||||
i16 :: i16;
|
i16 :: i16
|
||||||
u16 :: u16;
|
u16 :: u16
|
||||||
i32 :: i32;
|
i32 :: i32
|
||||||
u32 :: u32;
|
u32 :: u32
|
||||||
i64 :: i64;
|
i64 :: i64
|
||||||
u64 :: u64;
|
u64 :: u64
|
||||||
|
|
||||||
i128 :: i128;
|
i128 :: i128
|
||||||
u128 :: u128;
|
u128 :: u128
|
||||||
|
|
||||||
rune :: rune;
|
rune :: rune
|
||||||
|
|
||||||
f16 :: f16;
|
f16 :: f16
|
||||||
f32 :: f32;
|
f32 :: f32
|
||||||
f64 :: f64;
|
f64 :: f64
|
||||||
|
|
||||||
complex32 :: complex32;
|
complex32 :: complex32
|
||||||
complex64 :: complex64;
|
complex64 :: complex64
|
||||||
complex128 :: complex128;
|
complex128 :: complex128
|
||||||
|
|
||||||
quaternion64 :: quaternion64;
|
quaternion64 :: quaternion64
|
||||||
quaternion128 :: quaternion128;
|
quaternion128 :: quaternion128
|
||||||
quaternion256 :: quaternion256;
|
quaternion256 :: quaternion256
|
||||||
|
|
||||||
int :: int;
|
int :: int
|
||||||
uint :: uint;
|
uint :: uint
|
||||||
uintptr :: uintptr;
|
uintptr :: uintptr
|
||||||
|
|
||||||
rawptr :: rawptr;
|
rawptr :: rawptr
|
||||||
string :: string;
|
string :: string
|
||||||
cstring :: cstring;
|
cstring :: cstring
|
||||||
any :: any;
|
any :: any
|
||||||
|
|
||||||
typeid :: typeid;
|
typeid :: typeid
|
||||||
|
|
||||||
// Endian Specific Types
|
// Endian Specific Types
|
||||||
i16le :: i16le;
|
i16le :: i16le
|
||||||
u16le :: u16le;
|
u16le :: u16le
|
||||||
i32le :: i32le;
|
i32le :: i32le
|
||||||
u32le :: u32le;
|
u32le :: u32le
|
||||||
i64le :: i64le;
|
i64le :: i64le
|
||||||
u64le :: u64le;
|
u64le :: u64le
|
||||||
i128le :: i128le;
|
i128le :: i128le
|
||||||
u128le :: u128le;
|
u128le :: u128le
|
||||||
|
|
||||||
i16be :: i16be;
|
i16be :: i16be
|
||||||
u16be :: u16be;
|
u16be :: u16be
|
||||||
i32be :: i32be;
|
i32be :: i32be
|
||||||
u32be :: u32be;
|
u32be :: u32be
|
||||||
i64be :: i64be;
|
i64be :: i64be
|
||||||
u64be :: u64be;
|
u64be :: u64be
|
||||||
i128be :: i128be;
|
i128be :: i128be
|
||||||
u128be :: u128be;
|
u128be :: u128be
|
||||||
|
|
||||||
|
|
||||||
f16le :: f16le;
|
f16le :: f16le
|
||||||
f32le :: f32le;
|
f32le :: f32le
|
||||||
f64le :: f64le;
|
f64le :: f64le
|
||||||
|
|
||||||
f16be :: f16be;
|
f16be :: f16be
|
||||||
f32be :: f32be;
|
f32be :: f32be
|
||||||
f64be :: f64be;
|
f64be :: f64be
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return copy(b.buf[m:], p), nil
|
return copy(b.buf[m:], p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||||
|
return buffer_write(b, ([^]byte)(ptr)[:size])
|
||||||
|
}
|
||||||
|
|
||||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, len(s))
|
m, ok := _buffer_try_grow(b, len(s))
|
||||||
@@ -229,17 +233,18 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||||
|
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||||
|
}
|
||||||
|
|
||||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
|
|
||||||
if offset < 0 || offset >= len(b.buf) {
|
if uint(offset) >= len(b.buf) {
|
||||||
err = .Invalid_Offset
|
err = .Invalid_Offset
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if 0 <= offset && offset < len(b.buf) {
|
|
||||||
n = copy(p, b.buf[offset:])
|
n = copy(p, b.buf[offset:])
|
||||||
}
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
b.last_read = .Read
|
b.last_read = .Read
|
||||||
}
|
}
|
||||||
@@ -366,12 +371,12 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
|
|||||||
|
|
||||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||||
s.stream_data = b
|
s.stream_data = b
|
||||||
s.stream_vtable = _buffer_vtable
|
s.stream_vtable = &_buffer_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_buffer_vtable := &io.Stream_VTable{
|
_buffer_vtable := io.Stream_VTable{
|
||||||
impl_size = proc(s: io.Stream) -> i64 {
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
b := (^Buffer)(s.stream_data)
|
b := (^Buffer)(s.stream_data)
|
||||||
return i64(buffer_capacity(b))
|
return i64(buffer_capacity(b))
|
||||||
|
|||||||
+55
-35
@@ -10,7 +10,14 @@ clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location
|
|||||||
return c[:len(s)]
|
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
|
d := transmute(mem.Raw_String)str
|
||||||
return d.data
|
return d.data
|
||||||
}
|
}
|
||||||
@@ -134,6 +141,25 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
|||||||
return b
|
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 {
|
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -151,6 +177,24 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
|||||||
return b
|
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
|
@private
|
||||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||||
s, n := s, n
|
s, n := s, n
|
||||||
@@ -218,61 +262,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) ->
|
|||||||
|
|
||||||
|
|
||||||
@private
|
@private
|
||||||
_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) {
|
_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) {
|
||||||
s, n := s, n
|
if len(sep) == 0 {
|
||||||
|
|
||||||
if n == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if sep == nil {
|
|
||||||
res = s[:]
|
res = s[:]
|
||||||
ok = true
|
ok = true
|
||||||
s^ = s[len(s):]
|
s^ = s[len(s):]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if n < 0 {
|
|
||||||
n = count(s^, sep) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
n -= 1
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for ; i < n; i += 1 {
|
|
||||||
m := index(s^, sep)
|
m := index(s^, sep)
|
||||||
if m < 0 {
|
if m < 0 {
|
||||||
break
|
// not found
|
||||||
}
|
res = s[:]
|
||||||
|
ok = len(res) != 0
|
||||||
|
s^ = s[len(s):]
|
||||||
|
} else {
|
||||||
res = s[:m+sep_save]
|
res = s[:m+sep_save]
|
||||||
ok = true
|
ok = true
|
||||||
s^ = s[m+len(sep):]
|
s^ = s[m+len(sep):]
|
||||||
return
|
|
||||||
}
|
}
|
||||||
res = s[:]
|
|
||||||
ok = res != nil
|
|
||||||
s^ = s[len(s):]
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
||||||
return _split_iterator(s, sep, 0, -1)
|
return _split_iterator(s, sep, 0)
|
||||||
}
|
|
||||||
|
|
||||||
split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
|
|
||||||
return _split_iterator(s, sep, 0, n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
||||||
return _split_iterator(s, sep, len(sep), -1)
|
return _split_iterator(s, sep, len(sep))
|
||||||
}
|
}
|
||||||
|
|
||||||
split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
|
|
||||||
return _split_iterator(s, sep, len(sep), n)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
index_byte :: proc(s: []byte, c: byte) -> int {
|
index_byte :: proc(s: []byte, c: byte) -> int {
|
||||||
for i := 0; i < len(s); i += 1 {
|
for i := 0; i < len(s); i += 1 {
|
||||||
@@ -618,7 +638,7 @@ trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
|
|||||||
|
|
||||||
index_rune :: proc(s: []byte, r: rune) -> int {
|
index_rune :: proc(s: []byte, r: rune) -> int {
|
||||||
switch {
|
switch {
|
||||||
case 0 <= r && r < utf8.RUNE_SELF:
|
case u32(r) < utf8.RUNE_SELF:
|
||||||
return index_byte(s, byte(r))
|
return index_byte(s, byte(r))
|
||||||
|
|
||||||
case r == utf8.RUNE_ERROR:
|
case r == utf8.RUNE_ERROR:
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ reader_init :: proc(r: ^Reader, s: []byte) {
|
|||||||
|
|
||||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
s.stream_data = r
|
s.stream_data = r
|
||||||
s.stream_vtable = _reader_vtable
|
s.stream_vtable = &_reader_vtable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
|||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_reader_vtable := &io.Stream_VTable{
|
_reader_vtable := io.Stream_VTable{
|
||||||
impl_size = proc(s: io.Stream) -> i64 {
|
impl_size = proc(s: io.Stream) -> i64 {
|
||||||
r := (^Reader)(s.stream_data)
|
r := (^Reader)(s.stream_data)
|
||||||
return reader_size(r)
|
return reader_size(r)
|
||||||
|
|||||||
+4
-4
@@ -7,20 +7,20 @@ char :: builtin.u8 // assuming -funsigned-char
|
|||||||
schar :: builtin.i8
|
schar :: builtin.i8
|
||||||
short :: builtin.i16
|
short :: builtin.i16
|
||||||
int :: builtin.i32
|
int :: builtin.i32
|
||||||
long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64
|
long :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64
|
||||||
longlong :: builtin.i64
|
longlong :: builtin.i64
|
||||||
|
|
||||||
uchar :: builtin.u8
|
uchar :: builtin.u8
|
||||||
ushort :: builtin.u16
|
ushort :: builtin.u16
|
||||||
uint :: builtin.u32
|
uint :: builtin.u32
|
||||||
ulong :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64
|
ulong :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64
|
||||||
ulonglong :: builtin.u64
|
ulonglong :: builtin.u64
|
||||||
|
|
||||||
bool :: builtin.bool
|
bool :: builtin.bool
|
||||||
|
|
||||||
size_t :: builtin.uint
|
size_t :: builtin.uint
|
||||||
ssize_t :: builtin.int
|
ssize_t :: builtin.int
|
||||||
wchar_t :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32
|
wchar_t :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32
|
||||||
|
|
||||||
float :: builtin.f32
|
float :: builtin.f32
|
||||||
double :: builtin.f64
|
double :: builtin.f64
|
||||||
@@ -48,7 +48,7 @@ int_least64_t :: builtin.i64
|
|||||||
uint_least64_t :: builtin.u64
|
uint_least64_t :: builtin.u64
|
||||||
|
|
||||||
// Same on Windows, Linux, and FreeBSD
|
// Same on Windows, Linux, and FreeBSD
|
||||||
when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
|
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||||
int_fast8_t :: builtin.i8
|
int_fast8_t :: builtin.i8
|
||||||
uint_fast8_t :: builtin.u8
|
uint_fast8_t :: builtin.u8
|
||||||
int_fast16_t :: builtin.i32
|
int_fast16_t :: builtin.i32
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
|
|||||||
|
|
||||||
|
|
||||||
quote_string :: proc(s: string) -> []byte {
|
quote_string :: proc(s: string) -> []byte {
|
||||||
b := strings.make_builder(0, len(s)+2)
|
b := strings.builder_make(0, len(s)+2)
|
||||||
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
||||||
return b.buf[:]
|
return b.buf[:]
|
||||||
}
|
}
|
||||||
@@ -1276,7 +1276,7 @@ preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
|
|||||||
if start.file != nil {
|
if start.file != nil {
|
||||||
dir = filepath.dir(start.file.name)
|
dir = filepath.dir(start.file.name)
|
||||||
}
|
}
|
||||||
path := filepath.join(dir, filename)
|
path := filepath.join({dir, filename})
|
||||||
if os.exists(path) {
|
if os.exists(path) {
|
||||||
tok = include_file(cpp, tok, path, start.next.next)
|
tok = include_file(cpp, tok, path, start.next.next)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.3 Complex arithmetic
|
// 7.3 Complex arithmetic
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -49,8 +49,8 @@ foreign libc {
|
|||||||
// 7.3.8 Power and absolute-value functions
|
// 7.3.8 Power and absolute-value functions
|
||||||
cabs :: proc(z: complex_double) -> complex_double ---
|
cabs :: proc(z: complex_double) -> complex_double ---
|
||||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
cabsf :: proc(z: complex_float) -> complex_float ---
|
||||||
cpow :: proc(z: complex_double) -> complex_double ---
|
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||||
cpowf :: proc(z: complex_float) -> complex_float ---
|
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||||
csqrtf :: proc(z: complex_float) -> complex_float ---
|
csqrtf :: proc(z: complex_float) -> complex_float ---
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
+18
-5
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.5 Errors
|
// 7.5 Errors
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -14,7 +14,7 @@ when ODIN_OS == "windows" {
|
|||||||
// EDOM,
|
// EDOM,
|
||||||
// EILSEQ
|
// EILSEQ
|
||||||
// ERANGE
|
// ERANGE
|
||||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@@ -27,7 +27,20 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
|||||||
ERANGE :: 34
|
ERANGE :: 34
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .OpenBSD {
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="__errno")
|
||||||
|
_get_errno :: proc() -> ^int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
EDOM :: 33
|
||||||
|
EILSEQ :: 84
|
||||||
|
ERANGE :: 34
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@@ -40,7 +53,7 @@ when ODIN_OS == "windows" {
|
|||||||
ERANGE :: 34
|
ERANGE :: 34
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
when ODIN_OS == .Darwin {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
|
|||||||
+13
-13
@@ -4,9 +4,9 @@ package libc
|
|||||||
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -211,19 +211,19 @@ _signbitf :: #force_inline proc(x: float) -> int {
|
|||||||
return int(transmute(uint32_t)x >> 31)
|
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
|
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
|
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
|
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
|
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
|
// implemented as the relational comparisons, as that would produce an invalid
|
||||||
// "sticky" state that propagates and affects maths results. These need
|
// "sticky" state that propagates and affects maths results. These need
|
||||||
// to be implemented natively in Odin assuming isunordered to prevent that.
|
// 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
|
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
|
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
|
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
|
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
|
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) {
|
if isnan(x) {
|
||||||
// Force evaluation of y to propagate exceptions for ordering semantics.
|
// Force evaluation of y to propagate exceptions for ordering semantics.
|
||||||
// To ensure correct semantics of IEEE 754 this cannot be compiled away.
|
// 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}
|
fma :: proc{libc_fma, libc_fmaf}
|
||||||
|
|
||||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
// But retain the 'f' suffix-variant functions as well so they can be used,
|
||||||
// a trick is used here where we use explicit procedrual overloading of one
|
// a trick is used here where we use explicit procedural overloading of one
|
||||||
// procedure. This is done because the foreign block is marked @(private) and
|
// procedure. This is done because the foreign block is marked @(private) and
|
||||||
// aliasing functions does not remove privateness from the entity.
|
// aliasing functions does not remove privateness from the entity.
|
||||||
acosf :: proc{libc_acosf}
|
acosf :: proc{libc_acosf}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package libc
|
|||||||
|
|
||||||
// 7.13 Nonlocal jumps
|
// 7.13 Nonlocal jumps
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.13.1 Save calling environment
|
// 7.13.1 Save calling environment
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.14 Signal handling
|
// 7.14 Signal handling
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -21,7 +21,7 @@ foreign libc {
|
|||||||
raise :: proc(sig: int) -> int ---
|
raise :: proc(sig: int) -> int ---
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
SIG_ERR :: rawptr(~uintptr(0))
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
SIG_DFL :: rawptr(uintptr(0))
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
SIG_IGN :: rawptr(uintptr(1))
|
||||||
@@ -34,7 +34,7 @@ when ODIN_OS == "windows" {
|
|||||||
SIGTERM :: 15
|
SIGTERM :: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
SIG_ERR :: rawptr(~uintptr(0))
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
SIG_DFL :: rawptr(uintptr(0))
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
SIG_IGN :: rawptr(uintptr(1))
|
||||||
@@ -47,7 +47,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
|||||||
SIGTERM :: 15
|
SIGTERM :: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
when ODIN_OS == .Darwin {
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
SIG_ERR :: rawptr(~uintptr(0))
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
SIG_DFL :: rawptr(uintptr(0))
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
SIG_IGN :: rawptr(uintptr(1))
|
||||||
|
|||||||
+111
-146
@@ -47,29 +47,30 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
|
|||||||
|
|
||||||
// 7.17.4 Fences
|
// 7.17.4 Fences
|
||||||
atomic_thread_fence :: #force_inline proc(order: memory_order) {
|
atomic_thread_fence :: #force_inline proc(order: memory_order) {
|
||||||
switch (order) {
|
assert(order != .relaxed)
|
||||||
case .relaxed:
|
assert(order != .consume)
|
||||||
return
|
#partial switch order {
|
||||||
case .consume:
|
case .acquire: intrinsics.atomic_thread_fence(.Acquire)
|
||||||
intrinsics.atomic_fence_acq()
|
case .release: intrinsics.atomic_thread_fence(.Release)
|
||||||
case .acquire:
|
case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
|
||||||
intrinsics.atomic_fence_acq()
|
case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
|
||||||
case .release:
|
|
||||||
intrinsics.atomic_fence_rel()
|
|
||||||
case .acq_rel:
|
|
||||||
intrinsics.atomic_fence_acqrel()
|
|
||||||
case .seq_cst:
|
|
||||||
intrinsics.atomic_fence_acqrel()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_signal_fence :: #force_inline proc(order: memory_order) {
|
atomic_signal_fence :: #force_inline proc(order: memory_order) {
|
||||||
atomic_thread_fence(order)
|
assert(order != .relaxed)
|
||||||
|
assert(order != .consume)
|
||||||
|
#partial switch order {
|
||||||
|
case .acquire: intrinsics.atomic_signal_fence(.Acquire)
|
||||||
|
case .release: intrinsics.atomic_signal_fence(.Release)
|
||||||
|
case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
|
||||||
|
case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.17.5 Lock-free property
|
// 7.17.5 Lock-free property
|
||||||
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
|
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
|
||||||
return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T))
|
return intrinsics.atomic_type_is_lock_free(T)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.17.6 Atomic integer types
|
// 7.17.6 Atomic integer types
|
||||||
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
|
|||||||
assert(order != .acquire)
|
assert(order != .acquire)
|
||||||
assert(order != .acq_rel)
|
assert(order != .acq_rel)
|
||||||
|
|
||||||
#partial switch (order) {
|
#partial switch order {
|
||||||
case .relaxed:
|
case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
|
||||||
intrinsics.atomic_store_relaxed(object, desired)
|
case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
|
||||||
case .release:
|
case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
|
||||||
intrinsics.atomic_store_rel(object, desired)
|
|
||||||
case .seq_cst:
|
|
||||||
intrinsics.atomic_store(object, desired)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
|
|||||||
assert(order != .release)
|
assert(order != .release)
|
||||||
assert(order != .acq_rel)
|
assert(order != .acq_rel)
|
||||||
|
|
||||||
#partial switch (order) {
|
#partial switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
|
||||||
return intrinsics.atomic_load_relaxed(object)
|
case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
|
||||||
return intrinsics.atomic_load_acq(object)
|
case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
|
||||||
case .acquire:
|
|
||||||
return intrinsics.atomic_load_acq(object)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_load(object)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
|
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
|
||||||
return intrinsics.atomic_xchg(object, desired)
|
return intrinsics.atomic_exchange(object, desired)
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
|
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
|
||||||
return intrinsics.atomic_xchg_relaxed(object, desired)
|
case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
|
||||||
return intrinsics.atomic_xchg_acq(object, desired)
|
case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
|
||||||
return intrinsics.atomic_xchg_acq(object, desired)
|
case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
|
||||||
case .release:
|
|
||||||
return intrinsics.atomic_xchg_rel(object, desired)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_xchg_acqrel(object, desired)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_xchg(object, desired)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
|
|||||||
// [success = seq_cst, failure = acquire] => failacq
|
// [success = seq_cst, failure = acquire] => failacq
|
||||||
// [success = acquire, failure = relaxed] => acq_failrelaxed
|
// [success = acquire, failure = relaxed] => acq_failrelaxed
|
||||||
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
|
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
|
||||||
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
|
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
|
||||||
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
|
value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
|
||||||
if !ok { expected^ = value }
|
if !ok { expected^ = value }
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
|
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
|
||||||
assert(failure != .release)
|
assert(failure != .release)
|
||||||
assert(failure != .acq_rel)
|
assert(failure != .acq_rel)
|
||||||
|
|
||||||
value: T; ok: bool
|
value: T; ok: bool
|
||||||
#partial switch (failure) {
|
#partial switch failure {
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
assert(success != .relaxed)
|
assert(success != .relaxed)
|
||||||
#partial switch (success) {
|
#partial switch success {
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
|
||||||
case .consume:
|
case .consume:
|
||||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
|
||||||
case .release:
|
case .release:
|
||||||
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
|
||||||
case .acq_rel:
|
case .acq_rel:
|
||||||
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
|
||||||
}
|
}
|
||||||
case .relaxed:
|
case .relaxed:
|
||||||
assert(success != .release)
|
assert(success != .release)
|
||||||
#partial switch (success) {
|
#partial switch success {
|
||||||
case .relaxed:
|
case .relaxed:
|
||||||
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
|
||||||
case .consume:
|
case .consume:
|
||||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
|
||||||
case .acq_rel:
|
case .acq_rel:
|
||||||
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
|
||||||
}
|
}
|
||||||
case .consume:
|
case .consume:
|
||||||
fallthrough
|
assert(success == .seq_cst)
|
||||||
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
assert(success == .seq_cst)
|
assert(success == .seq_cst)
|
||||||
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
|
||||||
|
|
||||||
}
|
}
|
||||||
if !ok { expected^ = value }
|
if !ok { expected^ = value }
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
|
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
|
||||||
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
|
value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
|
||||||
if !ok { expected^ = value }
|
if !ok { expected^ = value }
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
|
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
|
||||||
assert(failure != .release)
|
assert(failure != .release)
|
||||||
assert(failure != .acq_rel)
|
assert(failure != .acq_rel)
|
||||||
|
|
||||||
value: T; ok: bool
|
value: T; ok: bool
|
||||||
#partial switch (failure) {
|
#partial switch failure {
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
assert(success != .relaxed)
|
assert(success != .relaxed)
|
||||||
#partial switch (success) {
|
#partial switch success {
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
|
||||||
case .consume:
|
case .consume:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
|
||||||
case .release:
|
case .release:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
|
||||||
case .acq_rel:
|
case .acq_rel:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
|
||||||
}
|
}
|
||||||
case .relaxed:
|
case .relaxed:
|
||||||
assert(success != .release)
|
assert(success != .release)
|
||||||
#partial switch (success) {
|
#partial switch success {
|
||||||
case .relaxed:
|
case .relaxed:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
|
||||||
case .seq_cst:
|
case .seq_cst:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
|
||||||
case .consume:
|
case .consume:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
|
||||||
case .acq_rel:
|
case .acq_rel:
|
||||||
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
|
||||||
}
|
}
|
||||||
case .consume:
|
case .consume:
|
||||||
fallthrough
|
assert(success == .seq_cst)
|
||||||
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
|
||||||
case .acquire:
|
case .acquire:
|
||||||
assert(success == .seq_cst)
|
assert(success == .seq_cst)
|
||||||
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
|
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
|
||||||
|
|
||||||
}
|
}
|
||||||
if !ok { expected^ = value }
|
if !ok { expected^ = value }
|
||||||
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
|
||||||
return intrinsics.atomic_add_relaxed(object, operand)
|
case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
|
||||||
return intrinsics.atomic_add_acq(object, operand)
|
case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
|
||||||
return intrinsics.atomic_add_acq(object, operand)
|
case: fallthrough
|
||||||
case .release:
|
case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
|
||||||
return intrinsics.atomic_add_rel(object, operand)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_add_acqrel(object, operand)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_add(object, operand)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
|
||||||
return intrinsics.atomic_sub_relaxed(object, operand)
|
case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
|
||||||
return intrinsics.atomic_sub_acq(object, operand)
|
case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
|
||||||
return intrinsics.atomic_sub_acq(object, operand)
|
case: fallthrough
|
||||||
case .release:
|
case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
|
||||||
return intrinsics.atomic_sub_rel(object, operand)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_sub_acqrel(object, operand)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_sub(object, operand)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
|
||||||
return intrinsics.atomic_or_relaxed(object, operand)
|
case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
|
||||||
return intrinsics.atomic_or_acq(object, operand)
|
case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
|
||||||
return intrinsics.atomic_or_acq(object, operand)
|
case: fallthrough
|
||||||
case .release:
|
case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
|
||||||
return intrinsics.atomic_or_rel(object, operand)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_or_acqrel(object, operand)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_or(object, operand)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
|
||||||
return intrinsics.atomic_xor_relaxed(object, operand)
|
case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
|
||||||
return intrinsics.atomic_xor_acq(object, operand)
|
case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
|
||||||
return intrinsics.atomic_xor_acq(object, operand)
|
case: fallthrough
|
||||||
case .release:
|
case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
|
||||||
return intrinsics.atomic_xor_rel(object, operand)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_xor_acqrel(object, operand)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_xor(object, operand)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
|
|||||||
return intrinsics.atomic_and(object, operand)
|
return intrinsics.atomic_and(object, operand)
|
||||||
}
|
}
|
||||||
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||||
switch (order) {
|
switch order {
|
||||||
case .relaxed:
|
case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
|
||||||
return intrinsics.atomic_and_relaxed(object, operand)
|
case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
|
||||||
case .consume:
|
case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
|
||||||
return intrinsics.atomic_and_acq(object, operand)
|
case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
|
||||||
case .acquire:
|
case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
|
||||||
return intrinsics.atomic_and_acq(object, operand)
|
case: fallthrough
|
||||||
case .release:
|
case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
|
||||||
return intrinsics.atomic_and_rel(object, operand)
|
|
||||||
case .acq_rel:
|
|
||||||
return intrinsics.atomic_and_acqrel(object, operand)
|
|
||||||
case .seq_cst:
|
|
||||||
return intrinsics.atomic_and(object, operand)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+60
-7
@@ -1,8 +1,11 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc {
|
||||||
} else when ODIN_OS == "darwin" {
|
"system:libucrt.lib",
|
||||||
|
"system:legacy_stdio_definitions.lib",
|
||||||
|
}
|
||||||
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -13,7 +16,7 @@ when ODIN_OS == "windows" {
|
|||||||
FILE :: struct {}
|
FILE :: struct {}
|
||||||
|
|
||||||
// MSVCRT compatible.
|
// MSVCRT compatible.
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
_IOFBF :: 0x0000
|
_IOFBF :: 0x0000
|
||||||
_IONBF :: 0x0004
|
_IONBF :: 0x0004
|
||||||
_IOLBF :: 0x0040
|
_IOLBF :: 0x0040
|
||||||
@@ -48,7 +51,7 @@ when ODIN_OS == "windows" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GLIBC and MUSL compatible.
|
// GLIBC and MUSL compatible.
|
||||||
when ODIN_OS == "linux" {
|
when ODIN_OS == .Linux {
|
||||||
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
||||||
|
|
||||||
_IOFBF :: 0
|
_IOFBF :: 0
|
||||||
@@ -78,7 +81,57 @@ when ODIN_OS == "linux" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
when ODIN_OS == .OpenBSD {
|
||||||
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 1
|
||||||
|
|
||||||
|
BUFSIZ :: 1024
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 20
|
||||||
|
FILENAME_MAX :: 1024
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
stderr: ^FILE
|
||||||
|
stdin: ^FILE
|
||||||
|
stdout: ^FILE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .FreeBSD {
|
||||||
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 1
|
||||||
|
|
||||||
|
BUFSIZ :: 1024
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 20
|
||||||
|
FILENAME_MAX :: 1024
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
stderr: ^FILE
|
||||||
|
stdin: ^FILE
|
||||||
|
stdout: ^FILE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin {
|
||||||
fpos_t :: distinct i64
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
_IOFBF :: 0
|
_IOFBF :: 0
|
||||||
@@ -146,7 +199,7 @@ foreign libc {
|
|||||||
getc :: proc(stream: ^FILE) -> int ---
|
getc :: proc(stream: ^FILE) -> int ---
|
||||||
getchar :: proc() -> int ---
|
getchar :: proc() -> int ---
|
||||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||||
putchar :: proc() -> int ---
|
putchar :: proc(c: int) -> int ---
|
||||||
puts :: proc(s: cstring) -> int ---
|
puts :: proc(s: cstring) -> int ---
|
||||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||||
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||||
|
|||||||
+32
-6
@@ -2,15 +2,15 @@ package libc
|
|||||||
|
|
||||||
// 7.22 General utilities
|
// 7.22 General utilities
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
RAND_MAX :: 0x7fff
|
RAND_MAX :: 0x7fff
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@@ -24,7 +24,7 @@ when ODIN_OS == "windows" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "linux" {
|
when ODIN_OS == .Linux {
|
||||||
RAND_MAX :: 0x7fffffff
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
// GLIBC and MUSL only
|
// GLIBC and MUSL only
|
||||||
@@ -40,7 +40,7 @@ when ODIN_OS == "linux" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
when ODIN_OS == .Darwin {
|
||||||
RAND_MAX :: 0x7fffffff
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
// GLIBC and MUSL only
|
// GLIBC and MUSL only
|
||||||
@@ -88,7 +88,6 @@ foreign libc {
|
|||||||
srand :: proc(seed: uint) ---
|
srand :: proc(seed: uint) ---
|
||||||
|
|
||||||
// 7.22.3 Memory management functions
|
// 7.22.3 Memory management functions
|
||||||
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
|
|
||||||
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
||||||
free :: proc(ptr: rawptr) ---
|
free :: proc(ptr: rawptr) ---
|
||||||
malloc :: proc(size: size_t) -> rawptr ---
|
malloc :: proc(size: size_t) -> rawptr ---
|
||||||
@@ -125,3 +124,30 @@ foreign libc {
|
|||||||
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
||||||
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
|
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
foreign libc {
|
||||||
|
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||||
|
}
|
||||||
|
return _aligned_malloc(size=size, alignment=alignment)
|
||||||
|
} else {
|
||||||
|
foreign libc {
|
||||||
|
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||||
|
}
|
||||||
|
return aligned_alloc(alignment=alignment, size=size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
foreign libc {
|
||||||
|
_aligned_free :: proc(ptr: rawptr) ---
|
||||||
|
}
|
||||||
|
_aligned_free(ptr)
|
||||||
|
} else {
|
||||||
|
free(ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,9 @@ import "core:runtime"
|
|||||||
|
|
||||||
// 7.24 String handling
|
// 7.24 String handling
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ package libc
|
|||||||
thrd_start_t :: proc "c" (rawptr) -> int
|
thrd_start_t :: proc "c" (rawptr) -> int
|
||||||
tss_dtor_t :: proc "c" (rawptr)
|
tss_dtor_t :: proc "c" (rawptr)
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:libucrt.lib",
|
"system:libucrt.lib",
|
||||||
"system:msvcprt.lib"
|
"system:msvcprt.lib",
|
||||||
}
|
}
|
||||||
|
|
||||||
thrd_success :: 0 // _Thrd_success
|
thrd_success :: 0 // _Thrd_success
|
||||||
@@ -74,10 +74,10 @@ when ODIN_OS == "windows" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GLIBC and MUSL compatible constants and types.
|
// GLIBC and MUSL compatible constants and types.
|
||||||
when ODIN_OS == "linux" {
|
when ODIN_OS == .Linux {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:c",
|
"system:c",
|
||||||
"system:pthread"
|
"system:pthread",
|
||||||
}
|
}
|
||||||
|
|
||||||
thrd_success :: 0
|
thrd_success :: 0
|
||||||
@@ -138,6 +138,6 @@ when ODIN_OS == "linux" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
when ODIN_OS == .Darwin {
|
||||||
// TODO: find out what this is meant to be!
|
// TODO: find out what this is meant to be!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.27 Date and time
|
// 7.27 Date and time
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
@@ -12,7 +12,7 @@ when ODIN_OS == "windows" {
|
|||||||
|
|
||||||
// We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
|
// We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
|
||||||
// we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
|
// we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
clock :: proc() -> clock_t ---
|
clock :: proc() -> clock_t ---
|
||||||
@@ -45,7 +45,7 @@ when ODIN_OS == "windows" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
@@ -63,7 +63,12 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
|
|||||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .OpenBSD {
|
||||||
|
CLOCKS_PER_SEC :: 100
|
||||||
|
} else {
|
||||||
CLOCKS_PER_SEC :: 1000000
|
CLOCKS_PER_SEC :: 1000000
|
||||||
|
}
|
||||||
|
|
||||||
TIME_UTC :: 1
|
TIME_UTC :: 1
|
||||||
|
|
||||||
time_t :: distinct i64
|
time_t :: distinct i64
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.28 Unicode utilities
|
// 7.28 Unicode utilities
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.29 Extended multibyte and wide character utilities
|
// 7.29 Extended multibyte and wide character utilities
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
+14
-7
@@ -2,27 +2,34 @@ package libc
|
|||||||
|
|
||||||
// 7.30 Wide character classification and mapping utilities
|
// 7.30 Wide character classification and mapping utilities
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
} else when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
foreign import libc "system:System.framework"
|
foreign import libc "system:System.framework"
|
||||||
} else {
|
} else {
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == "windows" {
|
when ODIN_OS == .Windows {
|
||||||
wctrans_t :: distinct wchar_t
|
wctrans_t :: distinct wchar_t
|
||||||
wctype_t :: distinct ushort
|
wctype_t :: distinct ushort
|
||||||
}
|
|
||||||
|
|
||||||
when ODIN_OS == "linux" {
|
} else when ODIN_OS == .Linux {
|
||||||
wctrans_t :: distinct intptr_t
|
wctrans_t :: distinct intptr_t
|
||||||
wctype_t :: distinct ulong
|
wctype_t :: distinct ulong
|
||||||
}
|
|
||||||
|
|
||||||
when ODIN_OS == "darwin" {
|
} else when ODIN_OS == .Darwin {
|
||||||
wctrans_t :: distinct int
|
wctrans_t :: distinct int
|
||||||
wctype_t :: distinct u32
|
wctype_t :: distinct u32
|
||||||
|
|
||||||
|
} else when ODIN_OS == .OpenBSD {
|
||||||
|
wctrans_t :: distinct rawptr
|
||||||
|
wctype_t :: distinct rawptr
|
||||||
|
|
||||||
|
} else when ODIN_OS == .FreeBSD {
|
||||||
|
wctrans_t :: distinct int
|
||||||
|
wctype_t :: distinct ulong
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ when size_of(uintptr) == 8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Error :: union {
|
Error :: union #shared_nil {
|
||||||
General_Error,
|
General_Error,
|
||||||
Deflate_Error,
|
Deflate_Error,
|
||||||
ZLIB_Error,
|
ZLIB_Error,
|
||||||
@@ -58,6 +58,7 @@ Error :: union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
General_Error :: enum {
|
General_Error :: enum {
|
||||||
|
None = 0,
|
||||||
File_Not_Found,
|
File_Not_Found,
|
||||||
Cannot_Open_File,
|
Cannot_Open_File,
|
||||||
File_Too_Short,
|
File_Too_Short,
|
||||||
@@ -76,6 +77,7 @@ General_Error :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GZIP_Error :: enum {
|
GZIP_Error :: enum {
|
||||||
|
None = 0,
|
||||||
Invalid_GZIP_Signature,
|
Invalid_GZIP_Signature,
|
||||||
Reserved_Flag_Set,
|
Reserved_Flag_Set,
|
||||||
Invalid_Extra_Data,
|
Invalid_Extra_Data,
|
||||||
@@ -100,6 +102,7 @@ GZIP_Error :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ZIP_Error :: enum {
|
ZIP_Error :: enum {
|
||||||
|
None = 0,
|
||||||
Invalid_ZIP_File_Signature,
|
Invalid_ZIP_File_Signature,
|
||||||
Unexpected_Signature,
|
Unexpected_Signature,
|
||||||
Insert_Next_Disk,
|
Insert_Next_Disk,
|
||||||
@@ -107,6 +110,7 @@ ZIP_Error :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ZLIB_Error :: enum {
|
ZLIB_Error :: enum {
|
||||||
|
None = 0,
|
||||||
Unsupported_Window_Size,
|
Unsupported_Window_Size,
|
||||||
FDICT_Unsupported,
|
FDICT_Unsupported,
|
||||||
Unsupported_Compression_Level,
|
Unsupported_Compression_Level,
|
||||||
@@ -114,6 +118,7 @@ ZLIB_Error :: enum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Deflate_Error :: enum {
|
Deflate_Error :: enum {
|
||||||
|
None = 0,
|
||||||
Huffman_Bad_Sizes,
|
Huffman_Bad_Sizes,
|
||||||
Huffman_Bad_Code_Lengths,
|
Huffman_Bad_Code_Lengths,
|
||||||
Inflate_Error,
|
Inflate_Error,
|
||||||
@@ -123,7 +128,6 @@ Deflate_Error :: enum {
|
|||||||
BType_3,
|
BType_3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// General I/O context for ZLIB, LZW, etc.
|
// General I/O context for ZLIB, LZW, etc.
|
||||||
Context_Memory_Input :: struct #packed {
|
Context_Memory_Input :: struct #packed {
|
||||||
input_data: []u8,
|
input_data: []u8,
|
||||||
@@ -139,7 +143,12 @@ Context_Memory_Input :: struct #packed {
|
|||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
}
|
}
|
||||||
|
when size_of(rawptr) == 8 {
|
||||||
#assert(size_of(Context_Memory_Input) == 64)
|
#assert(size_of(Context_Memory_Input) == 64)
|
||||||
|
} else {
|
||||||
|
// e.g. `-target:windows_i386`
|
||||||
|
#assert(size_of(Context_Memory_Input) == 52)
|
||||||
|
}
|
||||||
|
|
||||||
Context_Stream_Input :: struct #packed {
|
Context_Stream_Input :: struct #packed {
|
||||||
input_data: []u8,
|
input_data: []u8,
|
||||||
@@ -174,8 +183,6 @@ Context_Stream_Input :: struct #packed {
|
|||||||
This simplifies end-of-stream handling where bits may be left in the bit buffer.
|
This simplifies end-of-stream handling where bits may be left in the bit buffer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: Make these return compress.Error errors.
|
|
||||||
|
|
||||||
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
|
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
|
||||||
return i64(len(z.input_data)), nil
|
return i64(len(z.input_data)), nil
|
||||||
}
|
}
|
||||||
@@ -287,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")
|
@(optimization_mode="speed")
|
||||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
@@ -314,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
|||||||
return res, .None
|
return res, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
@(optimization_mode="speed")
|
||||||
|
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
|
size :: size_of(T)
|
||||||
|
|
||||||
|
// Get current position to return to.
|
||||||
|
cur_pos, e1 := z.input->impl_seek(0, .Current)
|
||||||
|
if e1 != .None {
|
||||||
|
return T{}, e1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seek to offset.
|
||||||
|
pos, e2 := z.input->impl_seek(offset, .Start)
|
||||||
|
if e2 != .None {
|
||||||
|
return T{}, e2
|
||||||
|
}
|
||||||
|
|
||||||
|
r, e3 := io.to_reader_at(z.input)
|
||||||
|
if !e3 {
|
||||||
|
return T{}, .Empty
|
||||||
|
}
|
||||||
|
when size <= 128 {
|
||||||
|
b: [size]u8
|
||||||
|
} else {
|
||||||
|
b := make([]u8, size, context.temp_allocator)
|
||||||
|
}
|
||||||
|
_, e4 := io.read_at(r, b[:], pos)
|
||||||
|
if e4 != .None {
|
||||||
|
return T{}, .Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return read head to original position.
|
||||||
|
z.input->impl_seek(cur_pos, .Start)
|
||||||
|
|
||||||
|
res = (^T)(&b[0])^
|
||||||
|
return res, .None
|
||||||
|
}
|
||||||
|
|
||||||
|
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -473,4 +535,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
|||||||
consume_bits_lsb(z, discard)
|
consume_bits_lsb(z, discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
|
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ main :: proc() {
|
|||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
stderr("No input file specified.\n")
|
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 {
|
if err == nil {
|
||||||
stdout("Displaying test vector: ")
|
stdout("Displaying test vector: ")
|
||||||
stdout(bytes.buffer_to_string(&buf))
|
stdout(bytes.buffer_to_string(&buf))
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ OS :: enum u8 {
|
|||||||
_Unknown = 14,
|
_Unknown = 14,
|
||||||
Unknown = 255,
|
Unknown = 255,
|
||||||
}
|
}
|
||||||
OS_Name :: #partial [OS]string{
|
OS_Name :: #sparse[OS]string{
|
||||||
|
._Unknown = "",
|
||||||
.FAT = "FAT",
|
.FAT = "FAT",
|
||||||
.Amiga = "Amiga",
|
.Amiga = "Amiga",
|
||||||
.VMS = "VMS/OpenVMS",
|
.VMS = "VMS/OpenVMS",
|
||||||
@@ -99,9 +100,9 @@ E_GZIP :: compress.GZIP_Error
|
|||||||
E_ZLIB :: compress.ZLIB_Error
|
E_ZLIB :: compress.ZLIB_Error
|
||||||
E_Deflate :: compress.Deflate_Error
|
E_Deflate :: compress.Deflate_Error
|
||||||
|
|
||||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
|
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) {
|
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
@@ -111,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz
|
|||||||
|
|
||||||
err = E_General.File_Not_Found
|
err = E_General.File_Not_Found
|
||||||
if ok {
|
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
|
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
|
buf := buf
|
||||||
|
|
||||||
z := &compress.Context_Memory_Input{
|
z := &compress.Context_Memory_Input{
|
||||||
input_data = slice,
|
input_data = data,
|
||||||
output = buf,
|
output = buf,
|
||||||
}
|
}
|
||||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
||||||
@@ -135,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
|
|||||||
|
|
||||||
z.output = buf
|
z.output = buf
|
||||||
|
|
||||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) {
|
||||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
This file was generated, so don't edit this by hand.
|
||||||
|
Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
|
||||||
|
which is an English word model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// package shoco is an implementation of the shoco short string compressor
|
||||||
|
package shoco
|
||||||
|
|
||||||
|
DEFAULT_MODEL :: Shoco_Model {
|
||||||
|
min_char = 39,
|
||||||
|
max_char = 122,
|
||||||
|
characters_by_id = {
|
||||||
|
'e', 'a', 'i', 'o', 't', 'h', 'n', 'r', 's', 'l', 'u', 'c', 'w', 'm', 'd', 'b', 'p', 'f', 'g', 'v', 'y', 'k', '-', 'H', 'M', 'T', '\'', 'B', 'x', 'I', 'W', 'L',
|
||||||
|
},
|
||||||
|
ids_by_character = {
|
||||||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, 23, 29, -1, -1, 31, 24, -1, -1, -1, -1, -1, -1, 25, -1, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 15, 11, 14, 0, 17, 18, 5, 2, -1, 21, 9, 13, 6, 3, 16, -1, 7, 8, 4, 10, 19, 12, 28, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
},
|
||||||
|
successors_by_bigram = {
|
||||||
|
7, 4, 12, -1, 6, -1, 1, 0, 3, 5, -1, 9, -1, 8, 2, -1, 15, 14, -1, 10, 11, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1,
|
||||||
|
1, -1, 6, -1, 1, -1, 0, 3, 2, 4, 15, 11, -1, 9, 5, 10, 13, -1, 12, 8, 7, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
9, 11, -1, 4, 2, -1, 0, 8, 1, 5, -1, 6, -1, 3, 7, 15, -1, 12, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, 14, 7, 5, -1, 1, 2, 8, 9, 0, 15, 6, 4, 11, -1, 12, 3, -1, 10, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
2, 4, 3, 1, 5, 0, -1, 6, 10, 9, 7, 12, 11, -1, -1, -1, -1, 13, -1, -1, 8, -1, 15, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||||
|
0, 1, 2, 3, 4, -1, -1, 5, 9, 10, 6, -1, -1, 8, 15, 11, -1, 14, -1, -1, 7, -1, 13, -1, -1, -1, 12, -1, -1, -1, -1, -1,
|
||||||
|
2, 8, 7, 4, 3, -1, 9, -1, 6, 11, -1, 5, -1, -1, 0, -1, -1, 14, 1, 15, 10, 12, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||||
|
0, 3, 1, 2, 6, -1, 9, 8, 4, 12, 13, 10, -1, 11, 7, -1, -1, 15, 14, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 6, 3, 4, 1, 2, -1, -1, 5, 10, 7, 9, 11, 12, -1, -1, 8, 14, -1, -1, 15, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 6, 2, 5, 9, -1, -1, -1, 10, 1, 8, -1, 12, 14, 4, -1, 15, 7, -1, 13, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
8, 10, 9, 15, 1, -1, 4, 0, 3, 2, -1, 6, -1, 12, 11, 13, 7, 14, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
1, 3, 6, 0, 4, 2, -1, 7, 13, 8, 9, 11, -1, -1, 15, -1, -1, -1, -1, -1, 10, 5, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
3, 0, 1, 4, -1, 2, 5, 6, 7, 8, -1, 14, -1, -1, 9, 15, -1, 12, -1, -1, -1, 10, 11, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||||
|
0, 1, 3, 2, 15, -1, 12, -1, 7, 14, 4, -1, -1, 9, -1, 8, 5, 10, -1, -1, 6, -1, 13, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||||
|
0, 3, 1, 2, -1, -1, 12, 6, 4, 9, 7, -1, -1, 14, 8, -1, -1, 15, 11, 13, 5, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 5, 7, 2, 10, 13, -1, 6, 8, 1, 3, -1, -1, 14, 15, 11, -1, -1, -1, 12, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 2, 6, 3, 7, 10, -1, 1, 9, 4, 8, -1, -1, 15, -1, 12, 5, -1, -1, -1, 11, -1, 13, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||||
|
1, 3, 4, 0, 7, -1, 12, 2, 11, 8, 6, 13, -1, -1, -1, -1, -1, 5, -1, -1, 10, 15, 9, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||||
|
1, 3, 5, 2, 13, 0, 9, 4, 7, 6, 8, -1, -1, 15, -1, 11, -1, -1, 10, -1, 14, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 2, 1, 3, -1, -1, -1, 6, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
1, 11, 4, 0, 3, -1, 13, 12, 2, 7, -1, -1, 15, 10, 5, 8, 14, -1, -1, -1, -1, -1, 9, -1, -1, -1, 6, -1, -1, -1, -1, -1,
|
||||||
|
0, 9, 2, 14, 15, 4, 1, 13, 3, 5, -1, -1, 10, -1, -1, -1, -1, 6, 12, -1, 7, -1, 8, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||||
|
-1, 2, 14, -1, 1, 5, 8, 7, 4, 12, -1, 6, 9, 11, 13, 3, 10, 15, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
0, 1, 3, 2, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
4, 3, 1, 5, -1, -1, -1, 0, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
2, 8, 4, 1, -1, 0, -1, 6, -1, -1, 5, -1, 7, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
12, 5, -1, -1, 1, -1, -1, 7, 0, 3, -1, 2, -1, 4, 6, -1, -1, -1, -1, 8, -1, -1, 15, -1, 13, 9, -1, -1, -1, -1, -1, 11,
|
||||||
|
1, 3, 2, 4, -1, -1, -1, 5, -1, 7, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1,
|
||||||
|
5, 3, 4, 12, 1, 6, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 0, 9, -1, -1, 11, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||||
|
-1, -1, -1, -1, 0, -1, 1, 12, 3, -1, -1, -1, -1, 5, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, 6, -1, 10,
|
||||||
|
2, 3, 1, 4, -1, 0, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1,
|
||||||
|
5, 1, 3, 0, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 9, -1, -1, 6, -1, 7,
|
||||||
|
},
|
||||||
|
successors_reversed = {
|
||||||
|
's', 't', 'c', 'l', 'm', 'a', 'd', 'r', 'v', 'T', 'A', 'L', 'e', 'M', 'Y', '-',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'-', 't', 'a', 'b', 's', 'h', 'c', 'r', 'n', 'w', 'p', 'm', 'l', 'd', 'i', 'f',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'u', 'e', 'i', 'a', 'o', 'r', 'y', 'l', 'I', 'E', 'R', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'e', 'a', 'o', 'i', 'u', 'A', 'y', 'E', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
't', 'n', 'f', 's', '\'', 'm', 'I', 'N', 'A', 'E', 'L', 'Z', 'r', 'V', 'R', 'C',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'o', 'a', 'y', 'i', 'u', 'e', 'I', 'L', 'D', '\'', 'E', 'Y', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'r', 'i', 'y', 'a', 'e', 'o', 'u', 'Y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'h', 'o', 'e', 'E', 'i', 'u', 'r', 'w', 'a', 'H', 'y', 'R', 'Z', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'h', 'i', 'e', 'a', 'o', 'r', 'I', 'y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'n', 't', 's', 'r', 'l', 'd', 'i', 'y', 'v', 'm', 'b', 'c', 'g', 'p', 'k', 'u',
|
||||||
|
'e', 'l', 'o', 'u', 'y', 'a', 'r', 'i', 's', 'j', 't', 'b', 'v', 'h', 'm', 'd',
|
||||||
|
'o', 'e', 'h', 'a', 't', 'k', 'i', 'r', 'l', 'u', 'y', 'c', 'q', 's', '-', 'd',
|
||||||
|
'e', 'i', 'o', 'a', 's', 'y', 'r', 'u', 'd', 'l', '-', 'g', 'n', 'v', 'm', 'f',
|
||||||
|
'r', 'n', 'd', 's', 'a', 'l', 't', 'e', 'm', 'c', 'v', 'y', 'i', 'x', 'f', 'p',
|
||||||
|
'o', 'e', 'r', 'a', 'i', 'f', 'u', 't', 'l', '-', 'y', 's', 'n', 'c', '\'', 'k',
|
||||||
|
'h', 'e', 'o', 'a', 'r', 'i', 'l', 's', 'u', 'n', 'g', 'b', '-', 't', 'y', 'm',
|
||||||
|
'e', 'a', 'i', 'o', 't', 'r', 'u', 'y', 'm', 's', 'l', 'b', '\'', '-', 'f', 'd',
|
||||||
|
'n', 's', 't', 'm', 'o', 'l', 'c', 'd', 'r', 'e', 'g', 'a', 'f', 'v', 'z', 'b',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'e', 'n', 'i', 's', 'h', 'l', 'f', 'y', '-', 'a', 'w', '\'', 'g', 'r', 'o', 't',
|
||||||
|
'e', 'l', 'i', 'y', 'd', 'o', 'a', 'f', 'u', 't', 's', 'k', 'w', 'v', 'm', 'p',
|
||||||
|
'e', 'a', 'o', 'i', 'u', 'p', 'y', 's', 'b', 'm', 'f', '\'', 'n', '-', 'l', 't',
|
||||||
|
'd', 'g', 'e', 't', 'o', 'c', 's', 'i', 'a', 'n', 'y', 'l', 'k', '\'', 'f', 'v',
|
||||||
|
'u', 'n', 'r', 'f', 'm', 't', 'w', 'o', 's', 'l', 'v', 'd', 'p', 'k', 'i', 'c',
|
||||||
|
'e', 'r', 'a', 'o', 'l', 'p', 'i', 't', 'u', 's', 'h', 'y', 'b', '-', '\'', 'm',
|
||||||
|
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'e', 'i', 'o', 'a', 's', 'y', 't', 'd', 'r', 'n', 'c', 'm', 'l', 'u', 'g', 'f',
|
||||||
|
'e', 't', 'h', 'i', 'o', 's', 'a', 'u', 'p', 'c', 'l', 'w', 'm', 'k', 'f', 'y',
|
||||||
|
'h', 'o', 'e', 'i', 'a', 't', 'r', 'u', 'y', 'l', 's', 'w', 'c', 'f', '\'', '-',
|
||||||
|
'r', 't', 'l', 's', 'n', 'g', 'c', 'p', 'e', 'i', 'a', 'd', 'm', 'b', 'f', 'o',
|
||||||
|
'e', 'i', 'a', 'o', 'y', 'u', 'r', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||||
|
'a', 'i', 'h', 'e', 'o', 'n', 'r', 's', 'l', 'd', 'k', '-', 'f', '\'', 'c', 'b',
|
||||||
|
'p', 't', 'c', 'a', 'i', 'e', 'h', 'q', 'u', 'f', '-', 'y', 'o', '\x00', '\x00', '\x00',
|
||||||
|
'o', 'e', 's', 't', 'i', 'd', '\'', 'l', 'b', '-', 'm', 'a', 'r', 'n', 'p', 'w',
|
||||||
|
},
|
||||||
|
|
||||||
|
character_count = 32,
|
||||||
|
successor_count = 16,
|
||||||
|
|
||||||
|
max_successor_n = 7,
|
||||||
|
packs = {
|
||||||
|
{ 0x80000000, 1, 2, { 26, 24, 24, 24, 24, 24, 24, 24 }, { 15, 3, 0, 0, 0, 0, 0, 0 }, 0xc0, 0x80 },
|
||||||
|
{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
|
||||||
|
{ 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
|
||||||
|
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// package shoco is an implementation of the shoco short string compressor
|
||||||
|
package shoco
|
||||||
|
|
||||||
|
import "core:intrinsics"
|
||||||
|
import "core:compress"
|
||||||
|
|
||||||
|
Shoco_Pack :: struct {
|
||||||
|
word: u32,
|
||||||
|
bytes_packed: i8,
|
||||||
|
bytes_unpacked: i8,
|
||||||
|
offsets: [8]u16,
|
||||||
|
masks: [8]i16,
|
||||||
|
header_mask: u8,
|
||||||
|
header: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
Shoco_Model :: struct {
|
||||||
|
min_char: u8,
|
||||||
|
max_char: u8,
|
||||||
|
characters_by_id: []u8,
|
||||||
|
ids_by_character: [256]i16,
|
||||||
|
successors_by_bigram: []i8,
|
||||||
|
successors_reversed: []u8,
|
||||||
|
|
||||||
|
character_count: u8,
|
||||||
|
successor_count: u8,
|
||||||
|
max_successor_n: i8,
|
||||||
|
packs: []Shoco_Pack,
|
||||||
|
}
|
||||||
|
|
||||||
|
compress_bound :: proc(uncompressed_size: int) -> (worst_case_compressed_size: int) {
|
||||||
|
// Worst case compression happens when input is non-ASCII (128-255)
|
||||||
|
// Encoded as 0x00 + the byte in question.
|
||||||
|
return uncompressed_size * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
decompress_bound :: proc(compressed_size: int, model := DEFAULT_MODEL) -> (maximum_decompressed_size: int) {
|
||||||
|
// Best case compression is 2:1
|
||||||
|
most: f64
|
||||||
|
for pack in model.packs {
|
||||||
|
val := f64(compressed_size) / f64(pack.bytes_packed) * f64(pack.bytes_unpacked)
|
||||||
|
most = max(most, val)
|
||||||
|
}
|
||||||
|
return int(most)
|
||||||
|
}
|
||||||
|
|
||||||
|
find_best_encoding :: proc(indices: []i16, n_consecutive: i8, model := DEFAULT_MODEL) -> (res: int) {
|
||||||
|
for p := len(model.packs); p > 0; p -= 1 {
|
||||||
|
pack := model.packs[p - 1]
|
||||||
|
if n_consecutive >= pack.bytes_unpacked {
|
||||||
|
have_index := true
|
||||||
|
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||||
|
if indices[i] > pack.masks[i] {
|
||||||
|
have_index = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if have_index {
|
||||||
|
return p - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_model :: proc(model: Shoco_Model) -> (int, compress.Error) {
|
||||||
|
if len(model.characters_by_id) != int(model.character_count) {
|
||||||
|
return 0, .Unknown_Compression_Method
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(model.successors_by_bigram) != int(model.character_count) * int(model.character_count) {
|
||||||
|
return 0, .Unknown_Compression_Method
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(model.successors_reversed) != int(model.successor_count) * int(model.max_char - model.min_char) {
|
||||||
|
return 0, .Unknown_Compression_Method
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model seems legit.
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompresses into provided buffer.
|
||||||
|
decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DEFAULT_MODEL) -> (size: int, err: compress.Error) {
|
||||||
|
inp, inp_end := 0, len(input)
|
||||||
|
out, out_end := 0, len(output)
|
||||||
|
|
||||||
|
validate_model(model) or_return
|
||||||
|
|
||||||
|
for inp < inp_end {
|
||||||
|
val := transmute(i8)input[inp]
|
||||||
|
mark := int(-1)
|
||||||
|
|
||||||
|
for val < 0 {
|
||||||
|
val <<= 1
|
||||||
|
mark += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if mark > len(model.packs) {
|
||||||
|
return out, .Unknown_Compression_Method
|
||||||
|
}
|
||||||
|
|
||||||
|
if mark < 0 {
|
||||||
|
if out >= out_end {
|
||||||
|
return out, .Output_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore the sentinel value for non-ASCII chars
|
||||||
|
if input[inp] == 0x00 {
|
||||||
|
inp += 1
|
||||||
|
if inp >= inp_end {
|
||||||
|
return out, .Stream_Too_Short
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output[out] = input[inp]
|
||||||
|
inp, out = inp + 1, out + 1
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pack := model.packs[mark]
|
||||||
|
|
||||||
|
if out + int(pack.bytes_unpacked) > out_end {
|
||||||
|
return out, .Output_Too_Short
|
||||||
|
} else if inp + int(pack.bytes_packed) > inp_end {
|
||||||
|
return out, .Stream_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
code := intrinsics.unaligned_load((^u32)(&input[inp]))
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
code = intrinsics.byte_swap(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unpack the leading char
|
||||||
|
offset := pack.offsets[0]
|
||||||
|
mask := pack.masks[0]
|
||||||
|
|
||||||
|
last_chr := model.characters_by_id[(code >> offset) & u32(mask)]
|
||||||
|
output[out] = last_chr
|
||||||
|
|
||||||
|
// Unpack the successor chars
|
||||||
|
for i := 1; i < int(pack.bytes_unpacked); i += 1 {
|
||||||
|
offset = pack.offsets[i]
|
||||||
|
mask = pack.masks[i]
|
||||||
|
|
||||||
|
index_major := u32(last_chr - model.min_char) * u32(model.successor_count)
|
||||||
|
index_minor := (code >> offset) & u32(mask)
|
||||||
|
|
||||||
|
last_chr = model.successors_reversed[index_major + index_minor]
|
||||||
|
|
||||||
|
output[out + i] = last_chr
|
||||||
|
}
|
||||||
|
|
||||||
|
out += int(pack.bytes_unpacked)
|
||||||
|
inp += int(pack.bytes_packed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (res: string, err: compress.Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
if len(input) == 0 {
|
||||||
|
return "", .Stream_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
max_output_size := decompress_bound(len(input), model)
|
||||||
|
|
||||||
|
buf: [dynamic]u8
|
||||||
|
if !resize(&buf, max_output_size) {
|
||||||
|
return "", .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
length, result := decompress_slice_to_output_buffer(input, buf[:])
|
||||||
|
resize(&buf, length)
|
||||||
|
return string(buf[:]), result
|
||||||
|
}
|
||||||
|
decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string}
|
||||||
|
|
||||||
|
compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (size: int, err: compress.Error) {
|
||||||
|
inp, inp_end := 0, len(input)
|
||||||
|
out, out_end := 0, len(output)
|
||||||
|
output := output
|
||||||
|
|
||||||
|
validate_model(model) or_return
|
||||||
|
|
||||||
|
indices := make([]i16, model.max_successor_n + 1)
|
||||||
|
defer delete(indices)
|
||||||
|
|
||||||
|
last_resort := false
|
||||||
|
|
||||||
|
encode: for inp < inp_end {
|
||||||
|
if last_resort {
|
||||||
|
last_resort = false
|
||||||
|
|
||||||
|
if input[inp] & 0x80 == 0x80 {
|
||||||
|
// Non-ASCII case
|
||||||
|
if out + 2 > out_end {
|
||||||
|
return out, .Output_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put in a sentinel byte
|
||||||
|
output[out] = 0x00
|
||||||
|
out += 1
|
||||||
|
} else {
|
||||||
|
// An ASCII byte
|
||||||
|
if out + 1 > out_end {
|
||||||
|
return out, .Output_Too_Short
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output[out] = input[inp]
|
||||||
|
out, inp = out + 1, inp + 1
|
||||||
|
} else {
|
||||||
|
// Find the longest string of known successors
|
||||||
|
indices[0] = model.ids_by_character[input[inp]]
|
||||||
|
last_chr_index := indices[0]
|
||||||
|
|
||||||
|
if last_chr_index < 0 {
|
||||||
|
last_resort = true
|
||||||
|
continue encode
|
||||||
|
}
|
||||||
|
|
||||||
|
rest := inp_end - inp
|
||||||
|
n_consecutive: i8 = 1
|
||||||
|
for ; n_consecutive <= model.max_successor_n; n_consecutive += 1 {
|
||||||
|
if inp_end > 0 && int(n_consecutive) == rest {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
current_index := model.ids_by_character[input[inp + int(n_consecutive)]]
|
||||||
|
if current_index < 0 { // '\0' is always -1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
successor_index := model.successors_by_bigram[last_chr_index * i16(model.character_count) + current_index]
|
||||||
|
if successor_index < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
indices[n_consecutive] = i16(successor_index)
|
||||||
|
last_chr_index = current_index
|
||||||
|
}
|
||||||
|
|
||||||
|
if n_consecutive < 2 {
|
||||||
|
last_resort = true
|
||||||
|
continue encode
|
||||||
|
}
|
||||||
|
|
||||||
|
pack_n := find_best_encoding(indices, n_consecutive)
|
||||||
|
if pack_n >= 0 {
|
||||||
|
if out + int(model.packs[pack_n].bytes_packed) > out_end {
|
||||||
|
return out, .Output_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
pack := model.packs[pack_n]
|
||||||
|
code := pack.word
|
||||||
|
|
||||||
|
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||||
|
code |= u32(indices[i]) << pack.offsets[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the little-endian world, we need to swap what's in the register to match the memory representation.
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
code = intrinsics.byte_swap(code)
|
||||||
|
}
|
||||||
|
out_ptr := raw_data(output[out:])
|
||||||
|
|
||||||
|
switch pack.bytes_packed {
|
||||||
|
case 4:
|
||||||
|
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
|
||||||
|
case 2:
|
||||||
|
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
|
||||||
|
case 1:
|
||||||
|
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
|
||||||
|
case:
|
||||||
|
return out, .Unknown_Compression_Method
|
||||||
|
}
|
||||||
|
|
||||||
|
out += int(pack.bytes_packed)
|
||||||
|
inp += int(pack.bytes_unpacked)
|
||||||
|
} else {
|
||||||
|
last_resort = true
|
||||||
|
continue encode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := context.allocator) -> (output: []u8, err: compress.Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
if len(input) == 0 {
|
||||||
|
return {}, .Stream_Too_Short
|
||||||
|
}
|
||||||
|
|
||||||
|
max_output_size := compress_bound(len(input))
|
||||||
|
|
||||||
|
buf: [dynamic]u8
|
||||||
|
if !resize(&buf, max_output_size) {
|
||||||
|
return {}, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
length, result := compress_string_to_buffer(input, buf[:])
|
||||||
|
resize(&buf, length)
|
||||||
|
return buf[:length], result
|
||||||
|
}
|
||||||
|
compress :: proc{compress_string_to_buffer, compress_string}
|
||||||
@@ -48,9 +48,9 @@ Options :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error :: compress.Error
|
Error :: compress.Error
|
||||||
E_General :: compress.General_Error
|
General_Error :: compress.General_Error
|
||||||
E_ZLIB :: compress.ZLIB_Error
|
ZLIB_Error :: compress.ZLIB_Error
|
||||||
E_Deflate :: compress.Deflate_Error
|
Deflate_Error :: compress.Deflate_Error
|
||||||
|
|
||||||
DEFLATE_MAX_CHUNK_SIZE :: 65535
|
DEFLATE_MAX_CHUNK_SIZE :: 65535
|
||||||
DEFLATE_MAX_LITERAL_SIZE :: 65535
|
DEFLATE_MAX_LITERAL_SIZE :: 65535
|
||||||
@@ -258,7 +258,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
|
|
||||||
for i in 1 ..< HUFFMAN_MAX_BITS {
|
for i in 1 ..< HUFFMAN_MAX_BITS {
|
||||||
if sizes[i] > (1 << uint(i)) {
|
if sizes[i] > (1 << uint(i)) {
|
||||||
return E_Deflate.Huffman_Bad_Sizes
|
return .Huffman_Bad_Sizes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code := int(0)
|
code := int(0)
|
||||||
@@ -270,7 +270,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
code = code + sizes[i]
|
code = code + sizes[i]
|
||||||
if sizes[i] != 0 {
|
if sizes[i] != 0 {
|
||||||
if code - 1 >= (1 << u16(i)) {
|
if code - 1 >= (1 << u16(i)) {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
|
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
|
||||||
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
|||||||
s += 1
|
s += 1
|
||||||
}
|
}
|
||||||
if s >= 16 {
|
if s >= 16 {
|
||||||
return 0, E_Deflate.Bad_Huffman_Code
|
return 0, .Bad_Huffman_Code
|
||||||
}
|
}
|
||||||
// code size is s, so:
|
// code size is s, so:
|
||||||
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
|
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
|
||||||
if b >= size_of(t.size) {
|
if b >= size_of(t.size) {
|
||||||
return 0, E_Deflate.Bad_Huffman_Code
|
return 0, .Bad_Huffman_Code
|
||||||
}
|
}
|
||||||
if t.size[b] != s {
|
if t.size[b] != s {
|
||||||
return 0, E_Deflate.Bad_Huffman_Code
|
return 0, .Bad_Huffman_Code
|
||||||
}
|
}
|
||||||
|
|
||||||
compress.consume_bits_lsb(z, s)
|
compress.consume_bits_lsb(z, s)
|
||||||
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
|||||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
if z.num_bits < 16 {
|
if z.num_bits < 16 {
|
||||||
if z.num_bits > 63 {
|
if z.num_bits > 63 {
|
||||||
return 0, E_ZLIB.Code_Buffer_Malformed
|
return 0, .Code_Buffer_Malformed
|
||||||
}
|
}
|
||||||
compress.refill_lsb(z)
|
compress.refill_lsb(z)
|
||||||
if z.num_bits > 63 {
|
if z.num_bits > 63 {
|
||||||
return 0, E_General.Stream_Too_Short
|
return 0, .Stream_Too_Short
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
|
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
|
||||||
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
if value < 256 {
|
if value < 256 {
|
||||||
e := write_byte(z, u8(value))
|
e := write_byte(z, u8(value))
|
||||||
if e != .None {
|
if e != .None {
|
||||||
return E_General.Output_Too_Short
|
return .Output_Too_Short
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if value == 256 {
|
if value == 256 {
|
||||||
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
|
|
||||||
value, e = decode_huffman(z, z_offset)
|
value, e = decode_huffman(z, z_offset)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return E_Deflate.Bad_Huffman_Code
|
return .Bad_Huffman_Code
|
||||||
}
|
}
|
||||||
|
|
||||||
distance := Z_DIST_BASE[value]
|
distance := Z_DIST_BASE[value]
|
||||||
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
|
|
||||||
if z.bytes_written < i64(distance) {
|
if z.bytes_written < i64(distance) {
|
||||||
// Distance is longer than we've decoded so far.
|
// Distance is longer than we've decoded so far.
|
||||||
return E_Deflate.Bad_Distance
|
return .Bad_Distance
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
c := z.output.buf[z.bytes_written - i64(distance)]
|
c := z.output.buf[z.bytes_written - i64(distance)]
|
||||||
e := repl_byte(z, length, c)
|
e := repl_byte(z, length, c)
|
||||||
if e != .None {
|
if e != .None {
|
||||||
return E_General.Output_Too_Short
|
return .Output_Too_Short
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
e := repl_bytes(z, length, distance)
|
e := repl_bytes(z, length, distance)
|
||||||
if e != .None {
|
if e != .None {
|
||||||
return E_General.Output_Too_Short
|
return .Output_Too_Short
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
|||||||
if !raw {
|
if !raw {
|
||||||
size, size_err := compress.input_size(ctx)
|
size, size_err := compress.input_size(ctx)
|
||||||
if size < 6 || size_err != nil {
|
if size < 6 || size_err != nil {
|
||||||
return E_General.Stream_Too_Short
|
return .Stream_Too_Short
|
||||||
}
|
}
|
||||||
|
|
||||||
cmf, _ := compress.read_u8(ctx)
|
cmf, _ := compress.read_u8(ctx)
|
||||||
|
|
||||||
method := Compression_Method(cmf & 0xf)
|
method := Compression_Method(cmf & 0xf)
|
||||||
if method != .DEFLATE {
|
if method != .DEFLATE {
|
||||||
return E_General.Unknown_Compression_Method
|
return .Unknown_Compression_Method
|
||||||
}
|
}
|
||||||
|
|
||||||
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
|
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
|
||||||
return E_ZLIB.Unsupported_Window_Size
|
return .Unsupported_Window_Size
|
||||||
}
|
}
|
||||||
flg, _ := compress.read_u8(ctx)
|
flg, _ := compress.read_u8(ctx)
|
||||||
|
|
||||||
fcheck := flg & 0x1f
|
fcheck := flg & 0x1f
|
||||||
fcheck_computed := (cmf << 8 | flg) & 0x1f
|
fcheck_computed := (cmf << 8 | flg) & 0x1f
|
||||||
if fcheck != fcheck_computed {
|
if fcheck != fcheck_computed {
|
||||||
return E_General.Checksum_Failed
|
return .Checksum_Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
|||||||
They're application specific and PNG doesn't use them.
|
They're application specific and PNG doesn't use them.
|
||||||
*/
|
*/
|
||||||
if fdict := (flg >> 5) & 1; fdict != 0 {
|
if fdict := (flg >> 5) & 1; fdict != 0 {
|
||||||
return E_ZLIB.FDICT_Unsupported
|
return .FDICT_Unsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// flevel := Compression_Level((flg >> 6) & 3);
|
// flevel := Compression_Level((flg >> 6) & 3);
|
||||||
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
|||||||
output_hash := hash.adler32(ctx.output.buf[:])
|
output_hash := hash.adler32(ctx.output.buf[:])
|
||||||
|
|
||||||
if output_hash != u32(adler) {
|
if output_hash != u32(adler) {
|
||||||
return E_General.Checksum_Failed
|
return .Checksum_Failed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -555,7 +555,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
|
|
||||||
|
|
||||||
if ~uncompressed_len != length_check {
|
if ~uncompressed_len != length_check {
|
||||||
return E_Deflate.Len_Nlen_Mismatch
|
return .Len_Nlen_Mismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -571,7 +571,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
assert(uncompressed_len == 0)
|
assert(uncompressed_len == 0)
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
return E_Deflate.BType_3
|
return .BType_3
|
||||||
case:
|
case:
|
||||||
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
|
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
|
||||||
if type == 1 {
|
if type == 1 {
|
||||||
@@ -604,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
c = decode_huffman(z, codelength_ht) or_return
|
c = decode_huffman(z, codelength_ht) or_return
|
||||||
|
|
||||||
if c < 0 || c >= 19 {
|
if c < 0 || c >= 19 {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
if c < 16 {
|
if c < 16 {
|
||||||
lencodes[n] = u8(c)
|
lencodes[n] = u8(c)
|
||||||
@@ -616,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
case 16:
|
case 16:
|
||||||
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
|
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
fill = lencodes[n - 1]
|
fill = lencodes[n - 1]
|
||||||
case 17:
|
case 17:
|
||||||
@@ -624,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
case 18:
|
case 18:
|
||||||
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
|
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
|
||||||
case:
|
case:
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
|
|
||||||
if ntot - n < u32(c) {
|
if ntot - n < u32(c) {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
|
|
||||||
nc := n + u32(c)
|
nc := n + u32(c)
|
||||||
@@ -639,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
|||||||
}
|
}
|
||||||
|
|
||||||
if n != ntot {
|
if n != ntot {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
return .Huffman_Bad_Code_Lengths
|
||||||
}
|
}
|
||||||
|
|
||||||
build_huffman(z_repeat, lencodes[:hlit]) or_return
|
build_huffman(z_repeat, lencodes[:hlit]) or_return
|
||||||
@@ -677,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
|
|||||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
inflate :: proc{inflate_from_context, inflate_from_byte_array};
|
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dynamic_bit_array
|
package dynamic_bit_array
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note that these constants are dependent on the backing being a u64.
|
Note that these constants are dependent on the backing being a u64.
|
||||||
@@ -11,11 +12,120 @@ INDEX_SHIFT :: 6
|
|||||||
@(private="file")
|
@(private="file")
|
||||||
INDEX_MASK :: 63
|
INDEX_MASK :: 63
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
NUM_BITS :: 64
|
||||||
|
|
||||||
Bit_Array :: struct {
|
Bit_Array :: struct {
|
||||||
bits: [dynamic]u64,
|
bits: [dynamic]u64,
|
||||||
bias: int,
|
bias: int,
|
||||||
|
max_index: int,
|
||||||
|
free_pointer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bit_Array_Iterator :: struct {
|
||||||
|
array: ^Bit_Array,
|
||||||
|
word_idx: int,
|
||||||
|
bit_idx: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- ba: ^Bit_Array - the array to iterate over
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- it: ^Bit_Array_Iterator - the iterator that holds iteration state
|
||||||
|
*/
|
||||||
|
make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
|
||||||
|
return Bit_Array_Iterator { array = ba }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- set: bool - the state of the bit at `index`
|
||||||
|
- index: int - the next bit of the Bit_Array referenced by `it`.
|
||||||
|
- ok: bool - `true` if the iterator returned a valid index,
|
||||||
|
`false` if there were no more bits
|
||||||
|
*/
|
||||||
|
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
|
||||||
|
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
||||||
|
if index > it.array.max_index { return false, 0, false }
|
||||||
|
|
||||||
|
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||||
|
set = (word >> it.bit_idx & 1) == 1
|
||||||
|
|
||||||
|
it.bit_idx += 1
|
||||||
|
if it.bit_idx >= NUM_BITS {
|
||||||
|
it.bit_idx = 0
|
||||||
|
it.word_idx += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return set, index, true
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- index: int - the next set bit of the Bit_Array referenced by `it`.
|
||||||
|
- ok: bool - `true` if the iterator returned a valid index,
|
||||||
|
`false` if there were no more bits set
|
||||||
|
*/
|
||||||
|
iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
|
||||||
|
return iterate_internal_(it, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- index: int - the next unset bit of the Bit_Array referenced by `it`.
|
||||||
|
- ok: bool - `true` if the iterator returned a valid index,
|
||||||
|
`false` if there were no more unset bits
|
||||||
|
*/
|
||||||
|
iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
|
||||||
|
return iterate_internal_(it, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
|
||||||
|
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||||
|
when ! ITERATE_SET_BITS { word = ~word }
|
||||||
|
|
||||||
|
// if the word is empty or we have already gone over all the bits in it,
|
||||||
|
// b.bit_idx is greater than the index of any set bit in the word,
|
||||||
|
// meaning that word >> b.bit_idx == 0.
|
||||||
|
for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
|
||||||
|
it.word_idx += 1
|
||||||
|
it.bit_idx = 0
|
||||||
|
word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||||
|
when ! ITERATE_SET_BITS { word = ~word }
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are iterating the set bits, reaching the end of the array means we have no more bits to check
|
||||||
|
when ITERATE_SET_BITS {
|
||||||
|
if it.word_idx >= len(it.array.bits) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reaching here means that the word has some set bits
|
||||||
|
it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
|
||||||
|
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
||||||
|
|
||||||
|
it.bit_idx += 1
|
||||||
|
if it.bit_idx >= NUM_BITS {
|
||||||
|
it.bit_idx = 0
|
||||||
|
it.word_idx += 1
|
||||||
|
}
|
||||||
|
return index, index <= it.array.max_index
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
In:
|
In:
|
||||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||||
@@ -70,14 +180,42 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
|
|||||||
|
|
||||||
resize_if_needed(ba, leg_index) or_return
|
resize_if_needed(ba, leg_index) or_return
|
||||||
|
|
||||||
|
ba.max_index = max(idx, ba.max_index)
|
||||||
ba.bits[leg_index] |= 1 << uint(bit_index)
|
ba.bits[leg_index] |= 1 << uint(bit_index)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
In:
|
||||||
|
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||||
|
- index: The bit index. Can be an enum member.
|
||||||
|
|
||||||
|
Out:
|
||||||
|
- ok: Whether or not we managed to unset requested bit.
|
||||||
|
|
||||||
|
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||||
|
*/
|
||||||
|
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||||
|
|
||||||
|
idx := int(index) - ba.bias
|
||||||
|
|
||||||
|
if ba == nil || int(index) < ba.bias { return false }
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
leg_index := idx >> INDEX_SHIFT
|
||||||
|
bit_index := idx & INDEX_MASK
|
||||||
|
|
||||||
|
resize_if_needed(ba, leg_index) or_return
|
||||||
|
|
||||||
|
ba.max_index = max(idx, ba.max_index)
|
||||||
|
ba.bits[leg_index] &= ~(1 << uint(bit_index))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
*/
|
*/
|
||||||
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
|
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
size_in_bits := max_index - min_index
|
size_in_bits := max_index - min_index
|
||||||
|
|
||||||
@@ -85,10 +223,11 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
|
|||||||
|
|
||||||
legs := size_in_bits >> INDEX_SHIFT
|
legs := size_in_bits >> INDEX_SHIFT
|
||||||
|
|
||||||
res = Bit_Array{
|
res = new(Bit_Array)
|
||||||
bias = min_index,
|
res.bias = min_index
|
||||||
}
|
res.max_index = max_index
|
||||||
return res, resize_if_needed(&res, legs)
|
res.free_pointer = true
|
||||||
|
return res, resize_if_needed(res, legs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -96,7 +235,7 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
|
|||||||
*/
|
*/
|
||||||
clear :: proc(ba: ^Bit_Array) {
|
clear :: proc(ba: ^Bit_Array) {
|
||||||
if ba == nil { return }
|
if ba == nil { return }
|
||||||
ba.bits = {}
|
mem.zero_slice(ba.bits[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -105,6 +244,9 @@ clear :: proc(ba: ^Bit_Array) {
|
|||||||
destroy :: proc(ba: ^Bit_Array) {
|
destroy :: proc(ba: ^Bit_Array) {
|
||||||
if ba == nil { return }
|
if ba == nil { return }
|
||||||
delete(ba.bits)
|
delete(ba.bits)
|
||||||
|
if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
|
||||||
|
free(ba)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ package dynamic_bit_array
|
|||||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||||
was_set, was_retrieved := get(&bits, -1)
|
was_set, was_retrieved := get(&bits, -1)
|
||||||
fmt.println(was_set, was_retrieved)
|
fmt.println(was_set, was_retrieved)
|
||||||
|
destroy(&bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
||||||
@@ -40,13 +41,13 @@ package dynamic_bit_array
|
|||||||
using bit_array
|
using bit_array
|
||||||
|
|
||||||
bits := create(int(max(Foo)), int(min(Foo)))
|
bits := create(int(max(Foo)), int(min(Foo)))
|
||||||
defer destroy(&bits)
|
defer destroy(bits)
|
||||||
|
|
||||||
fmt.printf("Set(Bar): %v\n", set(&bits, Foo.Bar))
|
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
||||||
fmt.printf("Get(Bar): %v, %v\n", get(&bits, Foo.Bar))
|
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
||||||
fmt.printf("Set(Negative_Test): %v\n", set(&bits, Foo.Negative_Test))
|
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
||||||
fmt.printf("Get(Leaves): %v, %v\n", get(&bits, Foo.Leaves))
|
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
||||||
fmt.printf("Get(Negative_Test): %v, %v\n", get(&bits, Foo.Negative_Test))
|
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
||||||
fmt.printf("Freed.\n")
|
fmt.printf("Freed.\n")
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
package container_intrusive_list
|
||||||
|
|
||||||
|
import "core:intrinsics"
|
||||||
|
|
||||||
|
// An intrusive doubly-linked list
|
||||||
|
//
|
||||||
|
// As this is an intrusive container, a `Node` must be embedded in your own
|
||||||
|
// structure which is conventionally called a "link". The use of `push_front`
|
||||||
|
// and `push_back` take the address of this node. Retrieving the data
|
||||||
|
// associated with the node requires finding the relative offset of the node
|
||||||
|
// of the parent structure. The parent type and field name are given to
|
||||||
|
// `iterator_*` procedures, or to the built-in `container_of` procedure.
|
||||||
|
//
|
||||||
|
// This data structure is two-pointers in size:
|
||||||
|
// 8 bytes on 32-bit platforms and 16 bytes on 64-bit platforms
|
||||||
|
List :: struct {
|
||||||
|
head: ^Node,
|
||||||
|
tail: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node :: struct {
|
||||||
|
next, prev: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
push_front :: proc(list: ^List, node: ^Node) {
|
||||||
|
if list.head != nil {
|
||||||
|
list.head.prev = node
|
||||||
|
node.prev, node.next = nil, list.head
|
||||||
|
list.head = node
|
||||||
|
} else {
|
||||||
|
list.head, list.tail = node, node
|
||||||
|
node.prev, node.next = nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
push_back :: proc(list: ^List, node: ^Node) {
|
||||||
|
if list.tail != nil {
|
||||||
|
list.tail.next = node
|
||||||
|
node.prev, node.next = list.tail, nil
|
||||||
|
list.tail = node
|
||||||
|
} else {
|
||||||
|
list.head, list.tail = node, node
|
||||||
|
node.prev, node.next = nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove :: proc(list: ^List, node: ^Node) {
|
||||||
|
if node != nil {
|
||||||
|
if node.next != nil {
|
||||||
|
node.next.prev = node.prev
|
||||||
|
}
|
||||||
|
if node.prev != nil {
|
||||||
|
node.prev.next = node.next
|
||||||
|
}
|
||||||
|
if list.head == node {
|
||||||
|
list.head = node.next
|
||||||
|
}
|
||||||
|
if list.tail == node {
|
||||||
|
list.tail = node.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
||||||
|
for node := list.head; node != nil; {
|
||||||
|
next := node.next
|
||||||
|
if to_erase(node) {
|
||||||
|
if node.next != nil {
|
||||||
|
node.next.prev = node.prev
|
||||||
|
}
|
||||||
|
if node.prev != nil {
|
||||||
|
node.prev.next = node.next
|
||||||
|
}
|
||||||
|
if list.head == node {
|
||||||
|
list.head = node.next
|
||||||
|
}
|
||||||
|
if list.tail == node {
|
||||||
|
list.tail = node.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
is_empty :: proc(list: ^List) -> bool {
|
||||||
|
return list.head == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pop_front :: proc(list: ^List) -> ^Node {
|
||||||
|
link := list.head
|
||||||
|
if link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if link.next != nil {
|
||||||
|
link.next.prev = link.prev
|
||||||
|
}
|
||||||
|
if link.prev != nil {
|
||||||
|
link.prev.next = link.next
|
||||||
|
}
|
||||||
|
if link == list.head {
|
||||||
|
list.head = link.next
|
||||||
|
}
|
||||||
|
if link == list.tail {
|
||||||
|
list.tail = link.prev
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
|
||||||
|
}
|
||||||
|
pop_back :: proc(list: ^List) -> ^Node {
|
||||||
|
link := list.tail
|
||||||
|
if link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if link.next != nil {
|
||||||
|
link.next.prev = link.prev
|
||||||
|
}
|
||||||
|
if link.prev != nil {
|
||||||
|
link.prev.next = link.next
|
||||||
|
}
|
||||||
|
if link == list.head {
|
||||||
|
list.head = link.next
|
||||||
|
}
|
||||||
|
if link == list.tail {
|
||||||
|
list.tail = link.prev
|
||||||
|
}
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Iterator :: struct($T: typeid) {
|
||||||
|
curr: ^Node,
|
||||||
|
offset: uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_head :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {list.head, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_tail :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {list.tail, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator_from_node :: proc(node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
|
where intrinsics.type_has_field(T, field_name),
|
||||||
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
|
return {node, offset_of_by_string(T, field_name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_next :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
|
node := it.curr
|
||||||
|
if node == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
it.curr = node.next
|
||||||
|
|
||||||
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_prev :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
|
node := it.curr
|
||||||
|
if node == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
it.curr = node.prev
|
||||||
|
|
||||||
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package container_lru
|
package container_lru
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
import "core:mem"
|
_ :: runtime
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
_ :: mem
|
|
||||||
|
|
||||||
Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||||
prev, next: ^Node(Key, Value),
|
prev, next: ^Node(Key, Value),
|
||||||
@@ -23,11 +23,10 @@ Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key
|
|||||||
count: int,
|
count: int,
|
||||||
capacity: int,
|
capacity: int,
|
||||||
|
|
||||||
node_allocator: mem.Allocator,
|
node_allocator: runtime.Allocator,
|
||||||
|
|
||||||
on_remove: proc(key: Key, value: Value, user_data: rawptr),
|
on_remove: proc(key: Key, value: Value, user_data: rawptr),
|
||||||
on_remove_user_data: rawptr,
|
on_remove_user_data: rawptr,
|
||||||
call_on_remove_on_destroy: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init initializes a Cache
|
// init initializes a Cache
|
||||||
@@ -37,38 +36,51 @@ init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := con
|
|||||||
c.capacity = capacity
|
c.capacity = capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy deinitializes a Cache
|
// destroy deinitializes a Cachem
|
||||||
destroy :: proc(c: ^$C/Cache($Key, $Value)) {
|
destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
||||||
|
clear(c, call_on_remove)
|
||||||
|
delete(c.entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear the contents of a Cache
|
||||||
|
clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
||||||
for _, node in c.entries {
|
for _, node in c.entries {
|
||||||
if c.call_on_remove_on_destroy && c.on_remove != nil {
|
if call_on_remove {
|
||||||
c.on_remove(node.key, node.value, c.on_remove_user_data)
|
_call_on_remove(c, node)
|
||||||
}
|
}
|
||||||
free(node, c.node_allocator)
|
free(node, c.node_allocator)
|
||||||
}
|
}
|
||||||
clear(&c.entries)
|
runtime.clear(&c.entries)
|
||||||
delete(c.entries)
|
|
||||||
c.head = nil
|
c.head = nil
|
||||||
c.tail = nil
|
c.tail = nil
|
||||||
c.count = 0
|
c.count = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the given key value pair. This operation updates the recent usage of the item.
|
// set the given key value pair. This operation updates the recent usage of the item.
|
||||||
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> mem.Allocator_Error {
|
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
|
||||||
if e, ok := c.entries[key]; ok {
|
if e, ok := c.entries[key]; ok {
|
||||||
e.value = value
|
e.value = value
|
||||||
|
_pop_node(c, e)
|
||||||
|
_push_front_node(c, e)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
e := new(Node(Key, Value), c.node_allocator) or_return
|
e : ^Node(Key, Value) = nil
|
||||||
e.key = key
|
assert(c.count <= c.capacity)
|
||||||
e.value = value
|
if c.count == c.capacity {
|
||||||
|
e = c.tail
|
||||||
_push_front_node(c, e)
|
_remove_node(c, e)
|
||||||
if c.count > c.capacity {
|
}
|
||||||
_remove_node(c, c.tail)
|
else {
|
||||||
|
c.count += 1
|
||||||
|
e = new(Node(Key, Value), c.node_allocator) or_return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.key = key
|
||||||
|
e.value = value
|
||||||
|
_push_front_node(c, e)
|
||||||
c.entries[key] = e
|
c.entries[key] = e
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +130,8 @@ remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
_remove_node(c, e)
|
_remove_node(c, e)
|
||||||
|
free(node, c.node_allocator)
|
||||||
|
c.count -= 1
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +153,16 @@ _remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
|||||||
node.prev = nil
|
node.prev = nil
|
||||||
node.next = nil
|
node.next = nil
|
||||||
|
|
||||||
c.count -= 1
|
|
||||||
|
|
||||||
delete_key(&c.entries, node.key)
|
delete_key(&c.entries, node.key)
|
||||||
|
|
||||||
|
_call_on_remove(c, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
||||||
if c.on_remove != nil {
|
if c.on_remove != nil {
|
||||||
c.on_remove(node.key, node.value, c.on_remove_user_data)
|
c.on_remove(node.key, node.value, c.on_remove_user_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(node, c.node_allocator)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -162,8 +176,6 @@ _push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
|||||||
c.tail = e
|
c.tail = e
|
||||||
}
|
}
|
||||||
e.prev = nil
|
e.prev = nil
|
||||||
|
|
||||||
c.count += 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -171,6 +183,12 @@ _pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
|||||||
if e == nil {
|
if e == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if c.head == e {
|
||||||
|
c.head = e.next
|
||||||
|
}
|
||||||
|
if c.tail == e {
|
||||||
|
c.tail = e.prev
|
||||||
|
}
|
||||||
if e.prev != nil {
|
if e.prev != nil {
|
||||||
e.prev.next = e.next
|
e.prev.next = e.next
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
|
|||||||
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||||
j := j
|
j := j
|
||||||
queue := pq.queue[:]
|
queue := pq.queue[:]
|
||||||
n := builtin.len(queue)
|
|
||||||
for 0 <= j {
|
for 0 <= j {
|
||||||
i := (j-1)/2
|
i := (j-1)/2
|
||||||
if i == j || !pq.less(queue[j], queue[i]) {
|
if i == j || !pq.less(queue[j], queue[i]) {
|
||||||
|
|||||||
@@ -69,6 +69,23 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
|||||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||||
return q.data[idx]
|
return q.data[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||||
|
return q.data[q.offset]
|
||||||
|
}
|
||||||
|
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||||
|
return &q.data[q.offset]
|
||||||
|
}
|
||||||
|
|
||||||
|
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||||
|
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||||
|
return q.data[idx]
|
||||||
|
}
|
||||||
|
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||||
|
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||||
|
return &q.data[idx]
|
||||||
|
}
|
||||||
|
|
||||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||||
|
|
||||||
@@ -82,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
|||||||
return &q.data[idx]
|
return &q.data[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||||
|
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
|
||||||
|
idx := q.offset%builtin.len(q.data)
|
||||||
|
return &q.data[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||||
|
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
|
||||||
|
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
|
||||||
|
return &q.data[idx]
|
||||||
|
}
|
||||||
|
|
||||||
// Push an element to the back of the queue
|
// Push an element to the back of the queue
|
||||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||||
if space(q^) == 0 {
|
if space(q^) == 0 {
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
|
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||||
return a.data[index]
|
return a.data[index]
|
||||||
}
|
}
|
||||||
get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
|
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||||
return &a.data[index]
|
return &a.data[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
|
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||||
a.data[index] = item
|
a.data[index] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (T, bool) {
|
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||||
if N > 0 && a.len > 0 {
|
if N > 0 && a.len > 0 {
|
||||||
item = a.data[0]
|
item = a.data[0]
|
||||||
s := slice(a)
|
s := slice(a)
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// The following is a generic O(V+E) topological sorter implementation.
|
||||||
|
// This is the fastest known method for topological sorting and Odin's
|
||||||
|
// map type is being used to accelerate lookups.
|
||||||
|
package container_topological_sort
|
||||||
|
|
||||||
|
import "core:intrinsics"
|
||||||
|
import "core:runtime"
|
||||||
|
_ :: intrinsics
|
||||||
|
_ :: runtime
|
||||||
|
|
||||||
|
|
||||||
|
Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) {
|
||||||
|
dependents: map[K]bool,
|
||||||
|
dependencies: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K) {
|
||||||
|
relations: map[K]Relations(K),
|
||||||
|
dependents_allocator: runtime.Allocator,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) {
|
||||||
|
r.dependents.allocator = sorter.dependents_allocator
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
init :: proc(sorter: ^$S/Sorter($K)) {
|
||||||
|
sorter.relations = make(map[K]Relations(K))
|
||||||
|
sorter.dependents_allocator = context.allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy :: proc(sorter: ^$S/Sorter($K)) {
|
||||||
|
for _, v in &sorter.relations {
|
||||||
|
delete(v.dependents)
|
||||||
|
}
|
||||||
|
delete(sorter.relations)
|
||||||
|
}
|
||||||
|
|
||||||
|
add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool {
|
||||||
|
if key in sorter.relations {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sorter.relations[key] = make_relations(sorter)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
|
||||||
|
if key == dependency {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
find := &sorter.relations[dependency]
|
||||||
|
if find == nil {
|
||||||
|
find = map_insert(&sorter.relations, dependency, make_relations(sorter))
|
||||||
|
}
|
||||||
|
|
||||||
|
if find.dependents[key] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
find.dependents[key] = true
|
||||||
|
|
||||||
|
find = &sorter.relations[key]
|
||||||
|
if find == nil {
|
||||||
|
find = map_insert(&sorter.relations, key, make_relations(sorter))
|
||||||
|
}
|
||||||
|
|
||||||
|
find.dependencies += 1
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
|
||||||
|
relations := &sorter.relations
|
||||||
|
|
||||||
|
for k, v in relations {
|
||||||
|
if v.dependencies == 0 {
|
||||||
|
append(&sorted, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for root in &sorted do for k, _ in relations[root].dependents {
|
||||||
|
relation := &relations[k]
|
||||||
|
relation.dependencies -= 1
|
||||||
|
if relation.dependencies == 0 {
|
||||||
|
append(&sorted, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v in relations {
|
||||||
|
if v.dependencies != 0 {
|
||||||
|
append(&cycled, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
|
|||||||
### Disclaimer
|
### Disclaimer
|
||||||
The algorithms were ported out of curiosity and due to interest in the field.
|
The algorithms were ported out of curiosity and due to interest in the field.
|
||||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
||||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||||
|
|
||||||
### ToDo
|
### ToDo
|
||||||
|
|||||||
@@ -30,6 +30,6 @@ equivalence.
|
|||||||
|
|
||||||
For the most part, alterations to the base fiat-crypto generated code was
|
For the most part, alterations to the base fiat-crypto generated code was
|
||||||
kept to a minimum, to aid auditability. This results in a somewhat
|
kept to a minimum, to aid auditability. This results in a somewhat
|
||||||
ideosyncratic style, and in some cases minor performance penalties.
|
idiosyncratic style, and in some cases minor performance penalties.
|
||||||
|
|
||||||
[1]: https://github.com/mit-plv/fiat-crypto
|
[1]: https://github.com/mit-plv/fiat-crypto
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
|
|||||||
|
|
||||||
assert(len(arg1) == 16)
|
assert(len(arg1) == 16)
|
||||||
|
|
||||||
when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
|
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||||
// While it may be unwise to do deserialization here on our
|
// While it may be unwise to do deserialization here on our
|
||||||
// own when fiat-crypto provides equivalent functionality,
|
// own when fiat-crypto provides equivalent functionality,
|
||||||
// doing it this way provides a little under 3x performance
|
// doing it this way provides a little under 3x performance
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
|||||||
// Until dedicated assembly can be written leverage the fact that
|
// Until dedicated assembly can be written leverage the fact that
|
||||||
// the callers of this routine ensure that src/dst are valid.
|
// the callers of this routine ensure that src/dst are valid.
|
||||||
|
|
||||||
when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
|
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||||
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
||||||
// systems that also happen to not be strict about aligned
|
// systems that also happen to not be strict about aligned
|
||||||
// memory access.
|
// memory access.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
when ODIN_OS != "linux" {
|
when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
|
||||||
_rand_bytes :: proc (dst: []byte) {
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
foreign libc {
|
||||||
|
arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_rand_bytes :: proc (dst: []byte) {
|
||||||
|
arc4random_buf(raw_data(dst), len(dst))
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import win32 "core:sys/windows"
|
||||||
|
import "core:os"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
|
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||||
|
if ret != os.ERROR_NONE {
|
||||||
|
switch ret {
|
||||||
|
case os.ERROR_INVALID_HANDLE:
|
||||||
|
// The handle to the first parameter is invalid.
|
||||||
|
// This should not happen here, since we explicitly pass nil to it
|
||||||
|
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
|
||||||
|
case os.ERROR_INVALID_PARAMETER:
|
||||||
|
// One of the parameters was invalid
|
||||||
|
panic("crypto: BCryptGenRandom Invalid parameter")
|
||||||
|
case:
|
||||||
|
// Unknown error
|
||||||
|
panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -419,8 +419,10 @@ update :: proc(ctx: ^$T, data: []byte) {
|
|||||||
sha2_transf(ctx, shifted_message, block_nb)
|
sha2_transf(ctx, shifted_message, block_nb)
|
||||||
|
|
||||||
rem_len = new_len % CURR_BLOCK_SIZE
|
rem_len = new_len % CURR_BLOCK_SIZE
|
||||||
|
if rem_len > 0 {
|
||||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
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])}
|
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.length = rem_len
|
ctx.length = rem_len
|
||||||
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
||||||
|
|||||||
@@ -0,0 +1,335 @@
|
|||||||
|
package siphash
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2022 zhibog
|
||||||
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
zhibog: Initial implementation.
|
||||||
|
|
||||||
|
Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
|
||||||
|
|
||||||
|
Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:crypto/util"
|
||||||
|
|
||||||
|
/*
|
||||||
|
High level API
|
||||||
|
*/
|
||||||
|
|
||||||
|
KEY_SIZE :: 16
|
||||||
|
DIGEST_SIZE :: 8
|
||||||
|
|
||||||
|
// sum_string_1_3 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_1_3 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_1_3 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 1, 3)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_1_3 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_1_3 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_1_3(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_1_3 :: proc {
|
||||||
|
sum_string_1_3,
|
||||||
|
sum_bytes_1_3,
|
||||||
|
sum_string_to_buffer_1_3,
|
||||||
|
sum_bytes_to_buffer_1_3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_1_3 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_1_3(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_1_3 :: proc {
|
||||||
|
verify_bytes_1_3,
|
||||||
|
verify_u64_1_3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_2_4 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_2_4 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_2_4 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 2, 4)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_2_4 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_2_4 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_2_4(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_2_4 :: proc {
|
||||||
|
sum_string_2_4,
|
||||||
|
sum_bytes_2_4,
|
||||||
|
sum_string_to_buffer_2_4,
|
||||||
|
sum_bytes_to_buffer_2_4,
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_string :: sum_string_2_4
|
||||||
|
sum_bytes :: sum_bytes_2_4
|
||||||
|
sum_string_to_buffer :: sum_string_to_buffer_2_4
|
||||||
|
sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4
|
||||||
|
sum :: proc {
|
||||||
|
sum_string,
|
||||||
|
sum_bytes,
|
||||||
|
sum_string_to_buffer,
|
||||||
|
sum_bytes_to_buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_2_4 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_2_4(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_2_4 :: proc {
|
||||||
|
verify_bytes_2_4,
|
||||||
|
verify_u64_2_4,
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_bytes :: verify_bytes_2_4
|
||||||
|
verify_u64 :: verify_u64_2_4
|
||||||
|
verify :: proc {
|
||||||
|
verify_bytes,
|
||||||
|
verify_u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_4_8 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_string_4_8 :: proc(msg, key: string) -> u64 {
|
||||||
|
return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_4_8 will hash the given message with the key and return
|
||||||
|
// the computed hash as a u64
|
||||||
|
sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
|
||||||
|
ctx: Context
|
||||||
|
hash: u64
|
||||||
|
init(&ctx, key, 4, 8)
|
||||||
|
update(&ctx, msg)
|
||||||
|
final(&ctx, &hash)
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_string_to_buffer_4_8 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
|
||||||
|
sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum_bytes_to_buffer_4_8 will hash the given message with the key and write
|
||||||
|
// the computed hash into the provided destination buffer
|
||||||
|
sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
|
||||||
|
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||||
|
hash := sum_bytes_4_8(msg, key)
|
||||||
|
_collect_output(dst[:], hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum_4_8 :: proc {
|
||||||
|
sum_string_4_8,
|
||||||
|
sum_bytes_4_8,
|
||||||
|
sum_string_to_buffer_4_8,
|
||||||
|
sum_bytes_to_buffer_4_8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_u64_4_8 will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||||
|
return sum_bytes_4_8(msg, key) == tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify_bytes will check if the supplied tag matches with the output you
|
||||||
|
// will get from the provided message and key
|
||||||
|
verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
|
||||||
|
derived_tag: [8]byte
|
||||||
|
sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
|
||||||
|
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_4_8 :: proc {
|
||||||
|
verify_bytes_4_8,
|
||||||
|
verify_u64_4_8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Low level API
|
||||||
|
*/
|
||||||
|
|
||||||
|
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||||
|
assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
|
||||||
|
ctx.c_rounds = c_rounds
|
||||||
|
ctx.d_rounds = d_rounds
|
||||||
|
is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
|
||||||
|
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
|
||||||
|
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
|
||||||
|
assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
|
||||||
|
ctx.k0 = util.U64_LE(key[:8])
|
||||||
|
ctx.k1 = util.U64_LE(key[8:])
|
||||||
|
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
|
||||||
|
ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
|
||||||
|
ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
|
||||||
|
ctx.v3 = 0x7465646279746573 ~ ctx.k1
|
||||||
|
ctx.is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
|
assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
|
||||||
|
ctx.last_block = len(data) / 8 * 8
|
||||||
|
ctx.buf = data
|
||||||
|
i := 0
|
||||||
|
m: u64
|
||||||
|
for i < ctx.last_block {
|
||||||
|
m = u64(ctx.buf[i] & 0xff)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
for r in u64(1)..<8 {
|
||||||
|
m |= u64(ctx.buf[i] & 0xff) << (r * 8)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v3 ~= m
|
||||||
|
for _ in 0..<ctx.c_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v0 ~= m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final :: proc(ctx: ^Context, dst: ^u64) {
|
||||||
|
m: u64
|
||||||
|
for i := len(ctx.buf) - 1; i >= ctx.last_block; i -= 1 {
|
||||||
|
m <<= 8
|
||||||
|
m |= u64(ctx.buf[i] & 0xff)
|
||||||
|
}
|
||||||
|
m |= u64(len(ctx.buf) << 56)
|
||||||
|
|
||||||
|
ctx.v3 ~= m
|
||||||
|
|
||||||
|
for _ in 0..<ctx.c_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.v0 ~= m
|
||||||
|
ctx.v2 ~= 0xff
|
||||||
|
|
||||||
|
for _ in 0..<ctx.d_rounds {
|
||||||
|
_compress(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
|
||||||
|
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
ctx.k0, ctx.k1 = 0, 0
|
||||||
|
ctx.v0, ctx.v1 = 0, 0
|
||||||
|
ctx.v2, ctx.v3 = 0, 0
|
||||||
|
ctx.last_block = 0
|
||||||
|
ctx.c_rounds = 0
|
||||||
|
ctx.d_rounds = 0
|
||||||
|
ctx.is_initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
Context :: struct {
|
||||||
|
v0, v1, v2, v3: u64, // State values
|
||||||
|
k0, k1: u64, // Split key
|
||||||
|
c_rounds: int, // Number of message rounds
|
||||||
|
d_rounds: int, // Number of finalization rounds
|
||||||
|
buf: []byte, // Provided data
|
||||||
|
last_block: int, // Offset from the last block
|
||||||
|
is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byte {
|
||||||
|
return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
|
||||||
|
}
|
||||||
|
|
||||||
|
_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
|
||||||
|
dst[0] = _get_byte(7, hash)
|
||||||
|
dst[1] = _get_byte(6, hash)
|
||||||
|
dst[2] = _get_byte(5, hash)
|
||||||
|
dst[3] = _get_byte(4, hash)
|
||||||
|
dst[4] = _get_byte(3, hash)
|
||||||
|
dst[5] = _get_byte(2, hash)
|
||||||
|
dst[6] = _get_byte(1, hash)
|
||||||
|
dst[7] = _get_byte(0, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
_compress :: #force_inline proc "contextless" (ctx: ^Context) {
|
||||||
|
ctx.v0 += ctx.v1
|
||||||
|
ctx.v1 = util.ROTL64(ctx.v1, 13)
|
||||||
|
ctx.v1 ~= ctx.v0
|
||||||
|
ctx.v0 = util.ROTL64(ctx.v0, 32)
|
||||||
|
ctx.v2 += ctx.v3
|
||||||
|
ctx.v3 = util.ROTL64(ctx.v3, 16)
|
||||||
|
ctx.v3 ~= ctx.v2
|
||||||
|
ctx.v0 += ctx.v3
|
||||||
|
ctx.v3 = util.ROTL64(ctx.v3, 21)
|
||||||
|
ctx.v3 ~= ctx.v0
|
||||||
|
ctx.v2 += ctx.v1
|
||||||
|
ctx.v1 = util.ROTL64(ctx.v1, 17)
|
||||||
|
ctx.v1 ~= ctx.v2
|
||||||
|
ctx.v2 = util.ROTL64(ctx.v2, 32)
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
package debug_pe
|
||||||
|
|
||||||
|
PE_SIGNATURE_OFFSET_INDEX_POS :: 0x3c
|
||||||
|
PE_SIGNATURE :: u32le(0x0000_4550) // "PE\x00\x00"
|
||||||
|
PE_SIGNATURE_STRING :: "PE\x00\x00"
|
||||||
|
|
||||||
|
OPTIONAL_HEADER_MAGIC :: enum u16le {
|
||||||
|
PE32 = 0x010b,
|
||||||
|
PE32_PLUS = 0x020b,
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional_Header_Base :: struct #packed {
|
||||||
|
magic: OPTIONAL_HEADER_MAGIC,
|
||||||
|
major_linker_version: u8,
|
||||||
|
minor_linker_version: u8,
|
||||||
|
size_of_code: u32le,
|
||||||
|
size_of_initialized_data: u32le,
|
||||||
|
size_of_uninitialized_data: u32le,
|
||||||
|
address_of_entry_point: u32le,
|
||||||
|
base_of_code: u32le,
|
||||||
|
}
|
||||||
|
|
||||||
|
File_Header :: struct #packed {
|
||||||
|
machine: IMAGE_FILE_MACHINE,
|
||||||
|
number_of_sections: u16le,
|
||||||
|
time_date_stamp: u32le,
|
||||||
|
pointer_to_symbol_table: u32le,
|
||||||
|
number_of_symbols: u32le,
|
||||||
|
size_of_optional_header: u16le,
|
||||||
|
characteristics: IMAGE_FILE_CHARACTERISTICS,
|
||||||
|
}
|
||||||
|
|
||||||
|
Data_Directory :: struct #packed {
|
||||||
|
virtual_address: u32le,
|
||||||
|
size: u32le,
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional_Header32 :: struct #packed {
|
||||||
|
using base: Optional_Header_Base,
|
||||||
|
base_of_data: u32le,
|
||||||
|
image_base: u32le,
|
||||||
|
section_alignment: u32le,
|
||||||
|
file_alignment: u32le,
|
||||||
|
major_operating_system_version: u16le,
|
||||||
|
minor_operating_system_version: u16le,
|
||||||
|
major_image_version: u16le,
|
||||||
|
minor_image_version: u16le,
|
||||||
|
major_subsystem_version: u16le,
|
||||||
|
minor_subsystem_version: u16le,
|
||||||
|
win32_version_value: u32le,
|
||||||
|
size_of_image: u32le,
|
||||||
|
size_of_headers: u32le,
|
||||||
|
check_sum: u32le,
|
||||||
|
subsystem: IMAGE_SUBSYSTEM,
|
||||||
|
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||||
|
size_of_stack_reserve: u32le,
|
||||||
|
size_of_stack_commit: u32le,
|
||||||
|
size_of_heap_reserve: u32le,
|
||||||
|
size_of_heap_commit: u32le,
|
||||||
|
loader_flags: u32le,
|
||||||
|
number_of_rva_and_sizes: u32le,
|
||||||
|
data_directory: [16]Data_Directory,
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional_Header64 :: struct #packed {
|
||||||
|
using base: Optional_Header_Base,
|
||||||
|
image_base: u64le,
|
||||||
|
section_alignment: u32le,
|
||||||
|
file_alignment: u32le,
|
||||||
|
major_operating_system_version: u16le,
|
||||||
|
minor_operating_system_version: u16le,
|
||||||
|
major_image_version: u16le,
|
||||||
|
minor_image_version: u16le,
|
||||||
|
major_subsystem_version: u16le,
|
||||||
|
minor_subsystem_version: u16le,
|
||||||
|
win32_version_value: u32le,
|
||||||
|
size_of_image: u32le,
|
||||||
|
size_of_headers: u32le,
|
||||||
|
check_sum: u32le,
|
||||||
|
subsystem: IMAGE_SUBSYSTEM,
|
||||||
|
dll_characteristics: IMAGE_DLLCHARACTERISTICS,
|
||||||
|
size_of_stack_reserve: u64le,
|
||||||
|
size_of_stack_commit: u64le,
|
||||||
|
size_of_heap_reserve: u64le,
|
||||||
|
size_of_heap_commit: u64le,
|
||||||
|
loader_flags: u32le,
|
||||||
|
number_of_rva_and_sizes: u32le,
|
||||||
|
data_directory: [16]Data_Directory,
|
||||||
|
}
|
||||||
|
|
||||||
|
// .debug section
|
||||||
|
Debug_Directory_Entry :: struct {
|
||||||
|
characteristics: u32le,
|
||||||
|
time_date_stamp: u32le,
|
||||||
|
major_version: u16le,
|
||||||
|
minor_version: u16le,
|
||||||
|
type: IMAGE_DEBUG_TYPE,
|
||||||
|
size_of_data: u32le,
|
||||||
|
address_of_raw_data: u32le,
|
||||||
|
pointer_to_raw_data: u32le,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_FILE_MACHINE :: enum u16le {
|
||||||
|
UNKNOWN = 0x0,
|
||||||
|
AM33 = 0x1d3,
|
||||||
|
AMD64 = 0x8664,
|
||||||
|
ARM = 0x1c0,
|
||||||
|
ARMNT = 0x1c4,
|
||||||
|
ARM64 = 0xaa64,
|
||||||
|
EBC = 0xebc,
|
||||||
|
I386 = 0x14c,
|
||||||
|
IA64 = 0x200,
|
||||||
|
LOONGARCH32 = 0x6232,
|
||||||
|
LOONGARCH64 = 0x6264,
|
||||||
|
M32R = 0x9041,
|
||||||
|
MIPS16 = 0x266,
|
||||||
|
MIPSFPU = 0x366,
|
||||||
|
MIPSFPU16 = 0x466,
|
||||||
|
POWERPC = 0x1f0,
|
||||||
|
POWERPCFP = 0x1f1,
|
||||||
|
R4000 = 0x166,
|
||||||
|
SH3 = 0x1a2,
|
||||||
|
SH3DSP = 0x1a3,
|
||||||
|
SH4 = 0x1a6,
|
||||||
|
SH5 = 0x1a8,
|
||||||
|
THUMB = 0x1c2,
|
||||||
|
WCEMIPSV2 = 0x169,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMAGE_DIRECTORY_ENTRY constants
|
||||||
|
IMAGE_DIRECTORY_ENTRY :: enum u8 {
|
||||||
|
EXPORT = 0,
|
||||||
|
IMPORT = 1,
|
||||||
|
RESOURCE = 2,
|
||||||
|
EXCEPTION = 3,
|
||||||
|
SECURITY = 4,
|
||||||
|
BASERELOC = 5,
|
||||||
|
DEBUG = 6,
|
||||||
|
ARCHITECTURE = 7, // reserved
|
||||||
|
GLOBALPTR = 8,
|
||||||
|
TLS = 9,
|
||||||
|
LOAD_CONFIG = 10,
|
||||||
|
BOUND_IMPORT = 11,
|
||||||
|
IAT = 12,
|
||||||
|
DELAY_IMPORT = 13,
|
||||||
|
COM_DESCRIPTOR = 14, // DLR Runtime headers
|
||||||
|
_RESERVED = 15,
|
||||||
|
}
|
||||||
|
#assert(len(IMAGE_DIRECTORY_ENTRY) == 16)
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_FILE_CHARACTERISTICS :: distinct bit_set[IMAGE_FILE_CHARACTERISTIC; u16le]
|
||||||
|
IMAGE_FILE_CHARACTERISTIC :: enum u16le {
|
||||||
|
RELOCS_STRIPPED = 0,
|
||||||
|
EXECUTABLE_IMAGE = 1,
|
||||||
|
LINE_NUMS_STRIPPED = 2,
|
||||||
|
LOCAL_SYMS_STRIPPED = 3,
|
||||||
|
AGGRESIVE_WS_TRIM = 4,
|
||||||
|
LARGE_ADDRESS_AWARE = 5,
|
||||||
|
|
||||||
|
BYTES_REVERSED_LO = 7,
|
||||||
|
MACHINE_32BIT = 8, // IMAGE_FILE_32BIT_MACHINE originally
|
||||||
|
DEBUG_STRIPPED = 9,
|
||||||
|
REMOVABLE_RUN_FROM_SWAP = 10,
|
||||||
|
NET_RUN_FROM_SWAP = 11,
|
||||||
|
SYSTEM = 12,
|
||||||
|
DLL = 13,
|
||||||
|
UP_SYSTEM_ONLY = 14,
|
||||||
|
BYTES_REVERSED_HI = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_SUBSYSTEM :: enum u16le {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
NATIVE = 1,
|
||||||
|
WINDOWS_GUI = 2,
|
||||||
|
WINDOWS_CUI = 3,
|
||||||
|
OS2_CUI = 5,
|
||||||
|
POSIX_CUI = 7,
|
||||||
|
NATIVE_WINDOWS = 8,
|
||||||
|
WINDOWS_CE_GUI = 9,
|
||||||
|
EFI_APPLICATION = 10,
|
||||||
|
EFI_BOOT_SERVICE_DRIVER = 11,
|
||||||
|
EFI_RUNTIME_DRIVER = 12,
|
||||||
|
EFI_ROM = 13,
|
||||||
|
XBOX = 14,
|
||||||
|
WINDOWS_BOOT_APPLICATION = 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_DLLCHARACTERISTICS :: distinct bit_set[IMAGE_DLLCHARACTERISTIC; u16le]
|
||||||
|
IMAGE_DLLCHARACTERISTIC :: enum u16le {
|
||||||
|
HIGH_ENTROPY_VA = 5,
|
||||||
|
DYNAMIC_BASE = 6,
|
||||||
|
FORCE_INTEGRITY = 7,
|
||||||
|
NX_COMPAT = 8,
|
||||||
|
NO_ISOLATION = 9,
|
||||||
|
NO_SEH = 10,
|
||||||
|
NO_BIND = 11,
|
||||||
|
APPCONTAINER = 12,
|
||||||
|
WDM_DRIVER = 13,
|
||||||
|
GUARD_CF = 14,
|
||||||
|
TERMINAL_SERVER_AWARE = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_DEBUG_TYPE :: enum u32le {
|
||||||
|
UNKNOWN = 0, // An unknown value that is ignored by all tools.
|
||||||
|
COFF = 1, // The COFF debug information (line numbers, symbol table, and string table). This type of debug information is also pointed to by fields in the file headers.
|
||||||
|
CODEVIEW = 2, // The Visual C++ debug information.
|
||||||
|
FPO = 3, // The frame pointer omission (FPO) information. This information tells the debugger how to interpret nonstandard stack frames, which use the EBP register for a purpose other than as a frame pointer.
|
||||||
|
MISC = 4, // The location of DBG file.
|
||||||
|
EXCEPTION = 5, // A copy of .pdata section.
|
||||||
|
FIXUP = 6, // Reserved.
|
||||||
|
OMAP_TO_SRC = 7, // The mapping from an RVA in image to an RVA in source image.
|
||||||
|
OMAP_FROM_SRC = 8, // The mapping from an RVA in source image to an RVA in image.
|
||||||
|
BORLAND = 9, // Reserved for Borland.
|
||||||
|
RESERVED10 = 10, // Reserved.
|
||||||
|
CLSID = 11, // Reserved.
|
||||||
|
REPRO = 16, // PE determinism or reproducibility.
|
||||||
|
EX_DLLCHARACTERISTICS = 20, // Extended DLL characteristics bits.
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package debug_pe
|
||||||
|
|
||||||
|
import "core:runtime"
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
|
Section_Header32 :: struct {
|
||||||
|
name: [8]u8,
|
||||||
|
virtual_size: u32le,
|
||||||
|
virtual_address: u32le,
|
||||||
|
size_of_raw_data: u32le,
|
||||||
|
pointer_to_raw_data: u32le,
|
||||||
|
pointer_to_relocations: u32le,
|
||||||
|
pointer_to_line_numbers: u32le,
|
||||||
|
number_of_relocations: u16le,
|
||||||
|
number_of_line_numbers: u16le,
|
||||||
|
characteristics: IMAGE_SCN_CHARACTERISTICS,
|
||||||
|
}
|
||||||
|
|
||||||
|
Reloc :: struct {
|
||||||
|
virtual_address: u32le,
|
||||||
|
symbol_table_index: u32le,
|
||||||
|
type: IMAGE_REL,
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_SCN_CHARACTERISTICS :: enum u32le {
|
||||||
|
TYPE_NO_PAD = 0x00000008, // The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files. = 0x00000010, // Reserved for future use.
|
||||||
|
CNT_CODE = 0x00000020, // The section contains executable code.
|
||||||
|
CNT_INITIALIZED_DATA = 0x00000040, // The section contains initialized data.
|
||||||
|
CNT_UNINITIALIZED_DATA = 0x00000080, // The section contains uninitialized data.
|
||||||
|
LNK_OTHER = 0x00000100, // Reserved for future use.
|
||||||
|
LNK_INFO = 0x00000200, // The section contains comments or other information. The .drectve section has this type. This is valid for object files only. = 0x00000400, // Reserved for future use.
|
||||||
|
LNK_REMOVE = 0x00000800, // The section will not become part of the image. This is valid only for object files.
|
||||||
|
LNK_COMDAT = 0x00001000, // The section contains COMDAT data. For more information, see COMDAT Sections (Object Only). This is valid only for object files.
|
||||||
|
GPREL = 0x00008000, // The section contains data referenced through the global pointer (GP).
|
||||||
|
MEM_PURGEABLE = 0x00020000, // Reserved for future use.
|
||||||
|
MEM_16BIT = 0x00020000, // Reserved for future use.
|
||||||
|
MEM_LOCKED = 0x00040000, // Reserved for future use.
|
||||||
|
MEM_PRELOAD = 0x00080000, // Reserved for future use.
|
||||||
|
ALIGN_1BYTES = 0x00100000, // Align data on a 1-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_2BYTES = 0x00200000, // Align data on a 2-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_4BYTES = 0x00300000, // Align data on a 4-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_8BYTES = 0x00400000, // Align data on an 8-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_16BYTES = 0x00500000, // Align data on a 16-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_32BYTES = 0x00600000, // Align data on a 32-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_64BYTES = 0x00700000, // Align data on a 64-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_128BYTES = 0x00800000, // Align data on a 128-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_256BYTES = 0x00900000, // Align data on a 256-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_512BYTES = 0x00A00000, // Align data on a 512-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_1024BYTES = 0x00B00000, // Align data on a 1024-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_2048BYTES = 0x00C00000, // Align data on a 2048-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_4096BYTES = 0x00D00000, // Align data on a 4096-byte boundary. Valid only for object files.
|
||||||
|
ALIGN_8192BYTES = 0x00E00000, // Align data on an 8192-byte boundary. Valid only for object files.
|
||||||
|
LNK_NRELOC_OVFL = 0x01000000, // The section contains extended relocations.
|
||||||
|
MEM_DISCARDABLE = 0x02000000, // The section can be discarded as needed.
|
||||||
|
MEM_NOT_CACHED = 0x04000000, // The section cannot be cached.
|
||||||
|
MEM_NOT_PAGED = 0x08000000, // The section is not pageable.
|
||||||
|
MEM_SHARED = 0x10000000, // The section can be shared in memory.
|
||||||
|
MEM_EXECUTE = 0x20000000, // The section can be executed as code.
|
||||||
|
MEM_READ = 0x40000000, // The section can be read.
|
||||||
|
MEM_WRITE = 0x80000000, // The section can be written to.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_REL :: enum u16le {
|
||||||
|
I386_ABSOLUTE = 0x0000,
|
||||||
|
I386_DIR16 = 0x0001,
|
||||||
|
I386_REL16 = 0x0002,
|
||||||
|
I386_DIR32 = 0x0006,
|
||||||
|
I386_DIR32NB = 0x0007,
|
||||||
|
I386_SEG12 = 0x0009,
|
||||||
|
I386_SECTION = 0x000A,
|
||||||
|
I386_SECREL = 0x000B,
|
||||||
|
I386_TOKEN = 0x000C,
|
||||||
|
I386_SECREL7 = 0x000D,
|
||||||
|
I386_REL32 = 0x0014,
|
||||||
|
|
||||||
|
AMD64_ABSOLUTE = 0x0000,
|
||||||
|
AMD64_ADDR64 = 0x0001,
|
||||||
|
AMD64_ADDR32 = 0x0002,
|
||||||
|
AMD64_ADDR32NB = 0x0003,
|
||||||
|
AMD64_REL32 = 0x0004,
|
||||||
|
AMD64_REL32_1 = 0x0005,
|
||||||
|
AMD64_REL32_2 = 0x0006,
|
||||||
|
AMD64_REL32_3 = 0x0007,
|
||||||
|
AMD64_REL32_4 = 0x0008,
|
||||||
|
AMD64_REL32_5 = 0x0009,
|
||||||
|
AMD64_SECTION = 0x000A,
|
||||||
|
AMD64_SECREL = 0x000B,
|
||||||
|
AMD64_SECREL7 = 0x000C,
|
||||||
|
AMD64_TOKEN = 0x000D,
|
||||||
|
AMD64_SREL32 = 0x000E,
|
||||||
|
AMD64_PAIR = 0x000F,
|
||||||
|
AMD64_SSPAN32 = 0x0010,
|
||||||
|
|
||||||
|
ARM_ABSOLUTE = 0x0000,
|
||||||
|
ARM_ADDR32 = 0x0001,
|
||||||
|
ARM_ADDR32NB = 0x0002,
|
||||||
|
ARM_BRANCH24 = 0x0003,
|
||||||
|
ARM_BRANCH11 = 0x0004,
|
||||||
|
ARM_SECTION = 0x000E,
|
||||||
|
ARM_SECREL = 0x000F,
|
||||||
|
ARM_MOV32 = 0x0010,
|
||||||
|
|
||||||
|
THUMB_MOV32 = 0x0011,
|
||||||
|
THUMB_BRANCH20 = 0x0012,
|
||||||
|
THUMB_BRANCH24 = 0x0014,
|
||||||
|
THUMB_BLX23 = 0x0015,
|
||||||
|
|
||||||
|
ARM_PAIR = 0x0016,
|
||||||
|
|
||||||
|
ARM64_ABSOLUTE = 0x0000,
|
||||||
|
ARM64_ADDR32 = 0x0001,
|
||||||
|
ARM64_ADDR32NB = 0x0002,
|
||||||
|
ARM64_BRANCH26 = 0x0003,
|
||||||
|
ARM64_PAGEBASE_REL21 = 0x0004,
|
||||||
|
ARM64_REL21 = 0x0005,
|
||||||
|
ARM64_PAGEOFFSET_12A = 0x0006,
|
||||||
|
ARM64_PAGEOFFSET_12L = 0x0007,
|
||||||
|
ARM64_SECREL = 0x0008,
|
||||||
|
ARM64_SECREL_LOW12A = 0x0009,
|
||||||
|
ARM64_SECREL_HIGH12A = 0x000A,
|
||||||
|
ARM64_SECREL_LOW12L = 0x000B,
|
||||||
|
ARM64_TOKEN = 0x000C,
|
||||||
|
ARM64_SECTION = 0x000D,
|
||||||
|
ARM64_ADDR64 = 0x000E,
|
||||||
|
ARM64_BRANCH19 = 0x000F,
|
||||||
|
ARM64_BRANCH14 = 0x0010,
|
||||||
|
ARM64_REL32 = 0x0011,
|
||||||
|
}
|
||||||
|
|
||||||
|
PE_CODE_VIEW_SIGNATURE_RSDS :: u32le(0x5344_5352)
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package debug_pe
|
||||||
|
|
||||||
|
COFF_SYMBOL_SIZE :: 18
|
||||||
|
|
||||||
|
COFF_Symbol :: struct {
|
||||||
|
name: [8]u8,
|
||||||
|
value: u32le,
|
||||||
|
section_number: i16le,
|
||||||
|
type: IMAGE_SYM_TYPE,
|
||||||
|
storage_class: IMAGE_SYM_CLASS,
|
||||||
|
number_of_aux_symbols: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// COFF_Symbol_Aux_Format5 describes the expected form of an aux symbol
|
||||||
|
// attached to a section definition symbol. The PE format defines a
|
||||||
|
// number of different aux symbol formats: format 1 for function
|
||||||
|
// definitions, format 2 for .be and .ef symbols, and so on. Format 5
|
||||||
|
// holds extra info associated with a section definition, including
|
||||||
|
// number of relocations + line numbers, as well as COMDAT info. See
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#auxiliary-format-5-section-definitions
|
||||||
|
// for more on what's going on here.
|
||||||
|
COFF_Symbol_Aux_Format5 :: struct {
|
||||||
|
size: u32le,
|
||||||
|
num_relocs: u16le,
|
||||||
|
num_line_numbers: u16le,
|
||||||
|
checksum: u32le,
|
||||||
|
sec_num: u16le,
|
||||||
|
selection: IMAGE_COMDAT_SELECT,
|
||||||
|
_: [3]u8, // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_COMDAT_SELECT :: enum u8 {
|
||||||
|
NODUPLICATES = 1,
|
||||||
|
ANY = 2,
|
||||||
|
SAME_SIZE = 3,
|
||||||
|
EXACT_MATCH = 4,
|
||||||
|
ASSOCIATIVE = 5,
|
||||||
|
LARGEST = 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The symbol record is not yet assigned a section. A value of zero indicates
|
||||||
|
// that a reference to an external symbol is defined elsewhere. A value of
|
||||||
|
// non-zero is a common symbol with a size that is specified by the value.
|
||||||
|
IMAGE_SYM_UNDEFINED :: 0
|
||||||
|
// The symbol has an absolute (non-relocatable) value and is not an address.
|
||||||
|
IMAGE_SYM_ABSOLUTE :: -1
|
||||||
|
// The symbol provides general type or debugging information but does not
|
||||||
|
// correspond to a section. Microsoft tools use this setting along
|
||||||
|
// with .file records (storage class FILE).
|
||||||
|
IMAGE_SYM_DEBUG :: -2
|
||||||
|
|
||||||
|
IMAGE_SYM_TYPE :: enum u16le {
|
||||||
|
NULL = 0,
|
||||||
|
VOID = 1,
|
||||||
|
CHAR = 2,
|
||||||
|
SHORT = 3,
|
||||||
|
INT = 4,
|
||||||
|
LONG = 5,
|
||||||
|
FLOAT = 6,
|
||||||
|
DOUBLE = 7,
|
||||||
|
STRUCT = 8,
|
||||||
|
UNION = 9,
|
||||||
|
ENUM = 10,
|
||||||
|
MOE = 11,
|
||||||
|
BYTE = 12,
|
||||||
|
WORD = 13,
|
||||||
|
UINT = 14,
|
||||||
|
DWORD = 15,
|
||||||
|
PCODE = 32768,
|
||||||
|
|
||||||
|
DTYPE_NULL = 0,
|
||||||
|
DTYPE_POINTER = 0x10,
|
||||||
|
DTYPE_FUNCTION = 0x20,
|
||||||
|
DTYPE_ARRAY = 0x30,
|
||||||
|
}
|
||||||
|
|
||||||
|
IMAGE_SYM_CLASS :: enum u8 {
|
||||||
|
NULL = 0,
|
||||||
|
AUTOMATIC = 1,
|
||||||
|
EXTERNAL = 2,
|
||||||
|
STATIC = 3,
|
||||||
|
REGISTER = 4,
|
||||||
|
EXTERNAL_DEF = 5,
|
||||||
|
LABEL = 6,
|
||||||
|
UNDEFINED_LABEL = 7,
|
||||||
|
MEMBER_OF_STRUCT = 8,
|
||||||
|
ARGUMENT = 9,
|
||||||
|
STRUCT_TAG = 10,
|
||||||
|
MEMBER_OF_UNION = 11,
|
||||||
|
UNION_TAG = 12,
|
||||||
|
TYPE_DEFINITION = 13,
|
||||||
|
UNDEFINED_STATIC = 14,
|
||||||
|
ENUM_TAG = 15,
|
||||||
|
MEMBER_OF_ENUM = 16,
|
||||||
|
REGISTER_PARAM = 17,
|
||||||
|
BIT_FIELD = 18,
|
||||||
|
FAR_EXTERNAL = 68, // Not in PECOFF v8 spec
|
||||||
|
BLOCK = 100,
|
||||||
|
FUNCTION = 101,
|
||||||
|
END_OF_STRUCT = 102,
|
||||||
|
FILE = 103,
|
||||||
|
SECTION = 104,
|
||||||
|
WEAK_EXTERNAL = 105,
|
||||||
|
CLR_TOKEN = 107,
|
||||||
|
|
||||||
|
END_OF_FUNCTION = 255,
|
||||||
|
}
|
||||||
@@ -1,3 +1,15 @@
|
|||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
Library :: distinct rawptr
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// +build linux, darwin, freebsd
|
//+build linux, darwin, freebsd, openbsd
|
||||||
|
//+private
|
||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
import "core:os"
|
import "core:os"
|
||||||
|
|
||||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||||
flags := os.RTLD_NOW
|
flags := os.RTLD_NOW
|
||||||
if global_symbols {
|
if global_symbols {
|
||||||
flags |= os.RTLD_GLOBAL
|
flags |= os.RTLD_GLOBAL
|
||||||
@@ -12,11 +13,11 @@ load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
|||||||
return Library(lib), lib != nil
|
return Library(lib), lib != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unload_library :: proc(library: Library) {
|
_unload_library :: proc(library: Library) -> bool {
|
||||||
os.dlclose(rawptr(library))
|
return os.dlclose(rawptr(library))
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||||
ptr = os.dlsym(rawptr(library), symbol)
|
ptr = os.dlsym(rawptr(library), symbol)
|
||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
//+build windows
|
//+build windows
|
||||||
|
//+private
|
||||||
package dynlib
|
package dynlib
|
||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
import "core:strings"
|
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
|
// 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)
|
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
|
return handle, handle != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
unload_library :: proc(library: Library) -> bool {
|
_unload_library :: proc(library: Library) -> bool {
|
||||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library)
|
ok := win32.FreeLibrary(cast(win32.HMODULE)library)
|
||||||
return bool(ok)
|
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)
|
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
|
|||||||
@@ -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
|
// 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,
|
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
|
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
|
||||||
// for performance
|
// for performance
|
||||||
// By default, each call to 'read' returns a newly allocated slice
|
// By default, each call to 'read' returns a newly allocated slice
|
||||||
@@ -194,6 +198,7 @@ is_valid_delim :: proc(r: rune) -> bool {
|
|||||||
@private
|
@private
|
||||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||||
|
if !r.multiline_fields {
|
||||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||||
if err == .Buffer_Full {
|
if err == .Buffer_Full {
|
||||||
clear(&r.raw_buffer)
|
clear(&r.raw_buffer)
|
||||||
@@ -218,8 +223,47 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
|
|||||||
line[n-2] = '\n'
|
line[n-2] = '\n'
|
||||||
line = line[:n-1]
|
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 {
|
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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# License
|
||||||
|
|
||||||
|
By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions.
|
||||||
|
|
||||||
|
Permission to copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications:
|
||||||
|
|
||||||
|
The full text of this NOTICE in a location viewable to users of the redistributed or derivative work.
|
||||||
|
Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software Short Notice should be included (hypertext is preferred, text is permitted) within the body of any redistributed or derivative code.
|
||||||
|
|
||||||
|
Notice of any changes or modifications to the files, including the date changes were made. (We recommend you provide URIs to the location from which the code is derived.)
|
||||||
|
|
||||||
|
# Disclaimers
|
||||||
|
|
||||||
|
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
|
||||||
|
|
||||||
|
COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION.
|
||||||
|
|
||||||
|
The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the software without specific, written prior permission. Title to copyright in this software and any associated documentation will at all times remain with copyright holders.
|
||||||
|
|
||||||
|
# Notes
|
||||||
|
This version: http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
|
||||||
@@ -0,0 +1,374 @@
|
|||||||
|
package unicode_entity
|
||||||
|
/*
|
||||||
|
A unicode entity encoder/decoder
|
||||||
|
|
||||||
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
This code has several procedures to map unicode runes to/from different textual encodings.
|
||||||
|
- SGML/XML/HTML entity
|
||||||
|
-- &#<decimal>;
|
||||||
|
-- &#x<hexadecimal>;
|
||||||
|
-- &<entity name>; (If the lookup tables are compiled in).
|
||||||
|
Reference: https://www.w3.org/2003/entities/2007xml/unicode.xml
|
||||||
|
|
||||||
|
- URL encode / decode %hex entity
|
||||||
|
Reference: https://datatracker.ietf.org/doc/html/rfc3986/#section-2.1
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
import "core:unicode"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
|
||||||
|
|
||||||
|
write_rune :: strings.write_rune
|
||||||
|
write_string :: strings.write_string
|
||||||
|
|
||||||
|
Error :: enum u8 {
|
||||||
|
None = 0,
|
||||||
|
Tokenizer_Is_Nil,
|
||||||
|
|
||||||
|
Illegal_NUL_Character,
|
||||||
|
Illegal_UTF_Encoding,
|
||||||
|
Illegal_BOM,
|
||||||
|
|
||||||
|
CDATA_Not_Terminated,
|
||||||
|
Comment_Not_Terminated,
|
||||||
|
Invalid_Entity_Encoding,
|
||||||
|
}
|
||||||
|
|
||||||
|
Tokenizer :: struct {
|
||||||
|
r: rune,
|
||||||
|
w: int,
|
||||||
|
|
||||||
|
src: string,
|
||||||
|
offset: int,
|
||||||
|
read_offset: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
CDATA_START :: "<![CDATA["
|
||||||
|
CDATA_END :: "]]>"
|
||||||
|
|
||||||
|
COMMENT_START :: "<!--"
|
||||||
|
COMMENT_END :: "-->"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Default: CDATA and comments are passed through unchanged.
|
||||||
|
*/
|
||||||
|
XML_Decode_Option :: enum u8 {
|
||||||
|
/*
|
||||||
|
Do not decode & entities. It decodes by default.
|
||||||
|
If given, overrides `Decode_CDATA`.
|
||||||
|
*/
|
||||||
|
No_Entity_Decode,
|
||||||
|
|
||||||
|
/*
|
||||||
|
CDATA is unboxed.
|
||||||
|
*/
|
||||||
|
Unbox_CDATA,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unboxed CDATA is decoded as well.
|
||||||
|
Ignored if `.Unbox_CDATA` is not given.
|
||||||
|
*/
|
||||||
|
Decode_CDATA,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Comments are stripped.
|
||||||
|
*/
|
||||||
|
Comment_Strip,
|
||||||
|
}
|
||||||
|
XML_Decode_Options :: bit_set[XML_Decode_Option; u8]
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decode a string that may include SGML/XML/HTML entities.
|
||||||
|
The caller has to free the result.
|
||||||
|
*/
|
||||||
|
decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
l := len(input)
|
||||||
|
if l == 0 { return "", .None }
|
||||||
|
|
||||||
|
builder := strings.builder_make()
|
||||||
|
defer strings.builder_destroy(&builder)
|
||||||
|
|
||||||
|
t := Tokenizer{src=input}
|
||||||
|
in_data := false
|
||||||
|
|
||||||
|
loop: for {
|
||||||
|
advance(&t) or_return
|
||||||
|
if t.r < 0 { break loop }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Below here we're never inside a CDATA tag.
|
||||||
|
At most we'll see the start of one, but that doesn't affect the logic.
|
||||||
|
*/
|
||||||
|
switch t.r {
|
||||||
|
case '<':
|
||||||
|
/*
|
||||||
|
Might be the start of a CDATA tag or comment.
|
||||||
|
|
||||||
|
We don't need to check if we need to write a `<`, because if it isn't CDATA or a comment,
|
||||||
|
it couldn't have been part of an XML tag body to be decoded here.
|
||||||
|
|
||||||
|
Keep in mind that we could already *be* inside a CDATA tag.
|
||||||
|
If so, write `>` as a literal and continue.
|
||||||
|
*/
|
||||||
|
if in_data {
|
||||||
|
write_rune(&builder, '<')
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
in_data = _handle_xml_special(&t, &builder, options) or_return
|
||||||
|
|
||||||
|
case ']':
|
||||||
|
/*
|
||||||
|
If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
|
||||||
|
*/
|
||||||
|
if in_data {
|
||||||
|
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||||
|
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||||
|
in_data = false
|
||||||
|
t.read_offset += len(CDATA_END) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
write_rune(&builder, ']')
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
if in_data && .Decode_CDATA not_in options {
|
||||||
|
/*
|
||||||
|
Unboxed, but undecoded.
|
||||||
|
*/
|
||||||
|
write_rune(&builder, t.r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.r == '&' {
|
||||||
|
if entity, entity_err := _extract_xml_entity(&t); entity_err != .None {
|
||||||
|
/*
|
||||||
|
We read to the end of the string without closing the entity.
|
||||||
|
Pass through as-is.
|
||||||
|
*/
|
||||||
|
write_string(&builder, entity)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if .No_Entity_Decode not_in options {
|
||||||
|
if decoded, ok := xml_decode_entity(entity); ok {
|
||||||
|
write_rune(&builder, decoded)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Literal passthrough because the decode failed or we want entities not decoded.
|
||||||
|
*/
|
||||||
|
write_string(&builder, "&")
|
||||||
|
write_string(&builder, entity)
|
||||||
|
write_string(&builder, ";")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write_rune(&builder, t.r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.clone(strings.to_string(builder), allocator), err
|
||||||
|
}
|
||||||
|
|
||||||
|
advance :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||||
|
if t == nil { return .Tokenizer_Is_Nil }
|
||||||
|
using t
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if read_offset < len(src) {
|
||||||
|
offset = read_offset
|
||||||
|
r, w = rune(src[read_offset]), 1
|
||||||
|
switch {
|
||||||
|
case r == 0:
|
||||||
|
return .Illegal_NUL_Character
|
||||||
|
case r >= utf8.RUNE_SELF:
|
||||||
|
r, w = utf8.decode_rune_in_string(src[read_offset:])
|
||||||
|
if r == utf8.RUNE_ERROR && w == 1 {
|
||||||
|
return .Illegal_UTF_Encoding
|
||||||
|
} else if r == utf8.RUNE_BOM && offset > 0 {
|
||||||
|
return .Illegal_BOM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read_offset += w
|
||||||
|
return .None
|
||||||
|
} else {
|
||||||
|
offset = len(src)
|
||||||
|
r = -1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||||
|
entity := entity
|
||||||
|
if len(entity) == 0 { return -1, false }
|
||||||
|
|
||||||
|
switch entity[0] {
|
||||||
|
case '#':
|
||||||
|
base := 10
|
||||||
|
val := 0
|
||||||
|
entity = entity[1:]
|
||||||
|
|
||||||
|
if len(entity) == 0 { return -1, false }
|
||||||
|
|
||||||
|
if entity[0] == 'x' || entity[0] == 'X' {
|
||||||
|
base = 16
|
||||||
|
entity = entity[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(entity) > 0 {
|
||||||
|
r := entity[0]
|
||||||
|
switch r {
|
||||||
|
case '0'..='9':
|
||||||
|
val *= base
|
||||||
|
val += int(r - '0')
|
||||||
|
|
||||||
|
case 'a'..='f':
|
||||||
|
if base == 10 { return -1, false }
|
||||||
|
val *= base
|
||||||
|
val += int(r - 'a' + 10)
|
||||||
|
|
||||||
|
case 'A'..='F':
|
||||||
|
if base == 10 { return -1, false }
|
||||||
|
val *= base
|
||||||
|
val += int(r - 'A' + 10)
|
||||||
|
|
||||||
|
case:
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if val > MAX_RUNE_CODEPOINT { return -1, false }
|
||||||
|
entity = entity[1:]
|
||||||
|
}
|
||||||
|
return rune(val), true
|
||||||
|
|
||||||
|
case:
|
||||||
|
/*
|
||||||
|
Named entity.
|
||||||
|
*/
|
||||||
|
return named_xml_entity_to_rune(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Private XML helper to extract `&<stuff>;` entity.
|
||||||
|
*/
|
||||||
|
@(private="file")
|
||||||
|
_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||||
|
assert(t != nil && t.r == '&')
|
||||||
|
|
||||||
|
/*
|
||||||
|
All of these would be in the ASCII range.
|
||||||
|
Even if one is not, it doesn't matter. All characters we need to compare to extract are.
|
||||||
|
*/
|
||||||
|
using t
|
||||||
|
|
||||||
|
length := len(t.src)
|
||||||
|
found := false
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
for read_offset < length {
|
||||||
|
if src[read_offset] == ';' {
|
||||||
|
found = true
|
||||||
|
read_offset += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
read_offset += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
return string(src[offset + 1 : read_offset - 1]), .None
|
||||||
|
}
|
||||||
|
return string(src[offset : read_offset]), .Invalid_Entity_Encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Private XML helper for CDATA and comments.
|
||||||
|
*/
|
||||||
|
@(private="file")
|
||||||
|
_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) {
|
||||||
|
assert(t != nil && t.r == '<')
|
||||||
|
if t.read_offset + len(CDATA_START) >= len(t.src) { return false, .None }
|
||||||
|
|
||||||
|
if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
|
||||||
|
t.read_offset += len(CDATA_START) - 1
|
||||||
|
|
||||||
|
if .Unbox_CDATA in options && .Decode_CDATA in options {
|
||||||
|
/*
|
||||||
|
We're unboxing _and_ decoding CDATA
|
||||||
|
*/
|
||||||
|
return true, .None
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
CDATA is passed through.
|
||||||
|
*/
|
||||||
|
offset := t.offset
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scan until end of CDATA.
|
||||||
|
*/
|
||||||
|
for {
|
||||||
|
advance(t) or_return
|
||||||
|
if t.r < 0 { return true, .CDATA_Not_Terminated }
|
||||||
|
|
||||||
|
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||||
|
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||||
|
t.read_offset += len(CDATA_END) - 1
|
||||||
|
|
||||||
|
cdata := string(t.src[offset : t.read_offset])
|
||||||
|
|
||||||
|
if .Unbox_CDATA in options {
|
||||||
|
cdata = cdata[len(CDATA_START):]
|
||||||
|
cdata = cdata[:len(cdata) - len(CDATA_END)]
|
||||||
|
}
|
||||||
|
|
||||||
|
write_string(builder, cdata)
|
||||||
|
return false, .None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
|
||||||
|
t.read_offset += len(COMMENT_START)
|
||||||
|
/*
|
||||||
|
Comment is passed through by default.
|
||||||
|
*/
|
||||||
|
offset := t.offset
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scan until end of Comment.
|
||||||
|
*/
|
||||||
|
for {
|
||||||
|
advance(t) or_return
|
||||||
|
if t.r < 0 { return true, .Comment_Not_Terminated }
|
||||||
|
|
||||||
|
if t.read_offset + len(COMMENT_END) < len(t.src) {
|
||||||
|
if string(t.src[t.offset:][:len(COMMENT_END)]) == COMMENT_END {
|
||||||
|
t.read_offset += len(COMMENT_END) - 1
|
||||||
|
|
||||||
|
if .Comment_Strip not_in options {
|
||||||
|
comment := string(t.src[offset : t.read_offset])
|
||||||
|
write_string(builder, comment)
|
||||||
|
}
|
||||||
|
return false, .None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false, .None
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package unicode_entity_example
|
||||||
|
|
||||||
|
import "core:encoding/xml"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
doc_print :: proc(doc: ^xml.Document) {
|
||||||
|
buf: strings.Builder
|
||||||
|
defer strings.builder_destroy(&buf)
|
||||||
|
w := strings.to_writer(&buf)
|
||||||
|
|
||||||
|
xml.print(w, doc)
|
||||||
|
fmt.println(strings.to_string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
_entities :: proc() {
|
||||||
|
doc: ^xml.Document
|
||||||
|
err: xml.Error
|
||||||
|
|
||||||
|
DOC :: #load("../../../../tests/core/assets/XML/unicode.xml")
|
||||||
|
|
||||||
|
OPTIONS :: xml.Options{
|
||||||
|
flags = {
|
||||||
|
.Ignore_Unsupported, .Intern_Comments,
|
||||||
|
},
|
||||||
|
expected_doctype = "",
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_duration: time.Duration
|
||||||
|
|
||||||
|
{
|
||||||
|
time.SCOPED_TICK_DURATION(&parse_duration)
|
||||||
|
doc, err = xml.parse(DOC, OPTIONS)
|
||||||
|
}
|
||||||
|
defer xml.destroy(doc)
|
||||||
|
|
||||||
|
doc_print(doc)
|
||||||
|
|
||||||
|
ms := time.duration_milliseconds(parse_duration)
|
||||||
|
|
||||||
|
speed := (f64(1000.0) / ms) * f64(len(DOC)) / 1_024.0 / 1_024.0
|
||||||
|
|
||||||
|
fmt.printf("Parse time: %.2f ms (%.2f MiB/s).\n", ms, speed)
|
||||||
|
fmt.printf("Error: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_main :: proc() {
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
options := xml.Options{ flags = { .Ignore_Unsupported, .Intern_Comments, .Unbox_CDATA, .Decode_SGML_Entities }}
|
||||||
|
|
||||||
|
doc, _ := xml.parse(#load("test.html"), options)
|
||||||
|
|
||||||
|
defer xml.destroy(doc)
|
||||||
|
doc_print(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
track: mem.Tracking_Allocator
|
||||||
|
mem.tracking_allocator_init(&track, context.allocator)
|
||||||
|
context.allocator = mem.tracking_allocator(&track)
|
||||||
|
|
||||||
|
// _main()
|
||||||
|
_entities()
|
||||||
|
|
||||||
|
if len(track.allocation_map) > 0 {
|
||||||
|
println()
|
||||||
|
for _, v in track.allocation_map {
|
||||||
|
printf("%v Leaked %v bytes.\n", v.location, v.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Entity Reference Test</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #000; color: #eee;
|
||||||
|
width: 40%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Entity Reference Test</h1>
|
||||||
|
<div id="test_cdata_in_comment" foo="">
|
||||||
|
Foozle]! © <!-- <![CDATA[ ® ]]> -->42&;1234&
|
||||||
|
</div>
|
||||||
|
<!-- EXPECTED: Foozle]! © 42&;1234& -->
|
||||||
|
<div id="test_cdata_unwrap_and_passthrough">
|
||||||
|
Foozle]! © <![CDATA[BOX ® /BOX]]>42&;1234&
|
||||||
|
</div>
|
||||||
|
<!-- EXPECTED: Foozle]! © BOX ® /BOX42&;1234& -->
|
||||||
|
<div>
|
||||||
|
| | | fj ` \ ® ϱ ∳ ⁏
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -107,7 +107,7 @@ Node :: struct {
|
|||||||
/* Conventions */
|
/* Conventions */
|
||||||
/* ------------
|
/* ------------
|
||||||
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
|
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
|
||||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
|
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
|
||||||
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
|
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
|
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
|
||||||
remaining := len(r.data) - r.offset
|
remaining := len(r.data) - r.offset
|
||||||
if remaining < size_of(T) {
|
if remaining < size_of(T) {
|
||||||
|
if r.print_error {
|
||||||
|
fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
|
||||||
|
}
|
||||||
err = .Short_Read
|
err = .Short_Read
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
|
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
|
||||||
remaining := len(r.data) - r.offset
|
remaining := len(r.data) - r.offset
|
||||||
if remaining < size_of(T)*count {
|
if remaining < size_of(T)*count {
|
||||||
|
if r.print_error {
|
||||||
|
fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
|
||||||
|
r.filename, count, r.offset)
|
||||||
|
}
|
||||||
err = .Short_Read
|
err = .Short_Read
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
type := read_value(r, Meta_Value_Type) or_return
|
type := read_value(r, Meta_Value_Type) or_return
|
||||||
if type > max(Meta_Value_Type) {
|
if type > max(Meta_Value_Type) {
|
||||||
if r.print_error {
|
if r.print_error {
|
||||||
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
|
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
|
||||||
|
r.filename, u8(type), u8(max(Meta_Value_Type)))
|
||||||
}
|
}
|
||||||
err = .Invalid_Data
|
err = .Invalid_Data
|
||||||
return
|
return
|
||||||
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
type := read_value(r, Layer_Data_Type) or_return
|
type := read_value(r, Layer_Data_Type) or_return
|
||||||
if type > max(type) {
|
if type > max(type) {
|
||||||
if r.print_error {
|
if r.print_error {
|
||||||
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
|
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
|
||||||
|
r.filename, u8(type), u8(max(Layer_Data_Type)))
|
||||||
}
|
}
|
||||||
err = .Invalid_Data
|
err = .Invalid_Data
|
||||||
return
|
return
|
||||||
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < size_of(Header) {
|
if len(data) < size_of(Header) {
|
||||||
|
if print_error {
|
||||||
|
fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
|
||||||
|
}
|
||||||
|
err = .Short_Read
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
header := cast(^Header)raw_data(data)
|
header := cast(^Header)raw_data(data)
|
||||||
assert(header.magic_number == MAGIC_NUMBER)
|
if (header.magic_number != MAGIC_NUMBER) {
|
||||||
|
if print_error {
|
||||||
|
fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
|
||||||
|
}
|
||||||
|
err = .Invalid_Data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
r := &Reader{
|
r := &Reader{
|
||||||
filename = filename,
|
filename = filename,
|
||||||
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
}
|
}
|
||||||
|
|
||||||
node_count := 0
|
node_count := 0
|
||||||
|
file.header = header^
|
||||||
file.nodes = make([]Node, header.internal_node_count)
|
file.nodes = make([]Node, header.internal_node_count)
|
||||||
defer if err != nil {
|
defer if err != nil {
|
||||||
nodes_destroy(file.nodes)
|
nodes_destroy(file.nodes)
|
||||||
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
|||||||
type := read_value(r, Node_Type) or_return
|
type := read_value(r, Node_Type) or_return
|
||||||
if type > max(Node_Type) {
|
if type > max(Node_Type) {
|
||||||
if r.print_error {
|
if r.print_error {
|
||||||
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
|
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
|
||||||
|
r.filename, u8(type), u8(max(Node_Type)))
|
||||||
}
|
}
|
||||||
err = .Invalid_Data
|
err = .Invalid_Data
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
|
|||||||
|
|
||||||
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
|
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
|
||||||
for m in meta_data {
|
for m in meta_data {
|
||||||
name_len := max(len(m.name), 255)
|
name_len := min(len(m.name), 255)
|
||||||
write_value(w, u8(name_len))
|
write_value(w, u8(name_len))
|
||||||
write_string(w, m.name[:name_len])
|
write_string(w, m.name[:name_len])
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
|
|||||||
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
|
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
|
||||||
write_value(w, u32(len(layers)))
|
write_value(w, u32(len(layers)))
|
||||||
for layer in layers {
|
for layer in layers {
|
||||||
name_len := max(len(layer.name), 255)
|
name_len := min(len(layer.name), 255)
|
||||||
write_value(w, u8(name_len))
|
write_value(w, u8(name_len))
|
||||||
write_string(w, layer .name[:name_len])
|
write_string(w, layer .name[:name_len])
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
write_value(w, &Header{
|
write_value(w, Header{
|
||||||
magic_number = MAGIC_NUMBER,
|
magic_number = MAGIC_NUMBER,
|
||||||
version = LATEST_VERSION,
|
version = LATEST_VERSION,
|
||||||
internal_node_count = u32le(len(file.nodes)),
|
internal_node_count = u32le(len(file.nodes)),
|
||||||
|
|||||||
+227
-46
@@ -5,36 +5,67 @@ import "core:math/bits"
|
|||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:reflect"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
Marshal_Data_Error :: enum {
|
Marshal_Data_Error :: enum {
|
||||||
|
None,
|
||||||
Unsupported_Type,
|
Unsupported_Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
Marshal_Error :: union {
|
Marshal_Error :: union #shared_nil {
|
||||||
Marshal_Data_Error,
|
Marshal_Data_Error,
|
||||||
io.Error,
|
io.Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
||||||
b := strings.make_builder(allocator)
|
Marshal_Options :: struct {
|
||||||
defer if err != .None {
|
// output based on spec
|
||||||
strings.destroy_builder(&b)
|
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_to_builder(&b, v) or_return
|
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||||
|
b := strings.builder_make(allocator)
|
||||||
|
defer if err != nil {
|
||||||
|
strings.builder_destroy(&b)
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := opt
|
||||||
|
marshal_to_builder(&b, v, &opt) or_return
|
||||||
|
|
||||||
if len(b.buf) != 0 {
|
if len(b.buf) != 0 {
|
||||||
data = b.buf[:]
|
data = b.buf[:]
|
||||||
}
|
}
|
||||||
return data, .None
|
|
||||||
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||||
return marshal_to_writer(strings.to_writer(b), v)
|
return marshal_to_writer(strings.to_writer(b), v, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
io.write_string(w, "null") or_return
|
io.write_string(w, "null") or_return
|
||||||
return
|
return
|
||||||
@@ -48,13 +79,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
case runtime.Type_Info_Integer:
|
case runtime.Type_Info_Integer:
|
||||||
buf: [21]byte
|
buf: [40]byte
|
||||||
u: u128
|
u: u128
|
||||||
switch i in a {
|
switch i in a {
|
||||||
case i8: u = u128(i)
|
case i8: u = u128(i)
|
||||||
case i16: u = u128(i)
|
case i16: u = u128(i)
|
||||||
case i32: u = u128(i)
|
case i32: u = u128(i)
|
||||||
case i64: u = u128(i)
|
case i64: u = u128(i)
|
||||||
|
case i128: u = u128(i)
|
||||||
case int: u = u128(i)
|
case int: u = u128(i)
|
||||||
case u8: u = u128(i)
|
case u8: u = u128(i)
|
||||||
case u16: u = u128(i)
|
case u16: u = u128(i)
|
||||||
@@ -81,7 +113,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
case u128be: u = u128(i)
|
case u128be: u = u128(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
s: string
|
||||||
|
|
||||||
|
// allow uints to be printed as hex
|
||||||
|
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||||
|
switch i in a {
|
||||||
|
case u8, u16, u32, u64, u128:
|
||||||
|
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||||
|
|
||||||
|
case:
|
||||||
|
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||||
|
}
|
||||||
|
|
||||||
io.write_string(w, s) or_return
|
io.write_string(w, s) or_return
|
||||||
|
|
||||||
|
|
||||||
@@ -146,6 +192,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
case runtime.Type_Info_Multi_Pointer:
|
case runtime.Type_Info_Multi_Pointer:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
|
case runtime.Type_Info_Soa_Pointer:
|
||||||
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Procedure:
|
case runtime.Type_Info_Procedure:
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
@@ -165,52 +214,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
|
|
||||||
case runtime.Type_Info_Array:
|
case runtime.Type_Info_Array:
|
||||||
io.write_byte(w, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
io.write_byte(w, ']') or_return
|
opt_write_end(w, opt, ']') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Enumerated_Array:
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||||
io.write_byte(w, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
io.write_byte(w, ']') or_return
|
opt_write_end(w, opt, ']') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Dynamic_Array:
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
io.write_byte(w, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
io.write_byte(w, ']') or_return
|
opt_write_end(w, opt, ']') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Slice:
|
case runtime.Type_Info_Slice:
|
||||||
io.write_byte(w, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
slice := cast(^mem.Raw_Slice)v.data
|
slice := cast(^mem.Raw_Slice)v.data
|
||||||
for i in 0..<slice.len {
|
for i in 0..<slice.len {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
|
|
||||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
io.write_byte(w, ']') or_return
|
opt_write_end(w, opt, ']') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Map:
|
case runtime.Type_Info_Map:
|
||||||
m := (^mem.Raw_Map)(v.data)
|
m := (^mem.Raw_Map)(v.data)
|
||||||
|
opt_write_start(w, opt, '{') or_return
|
||||||
|
|
||||||
io.write_byte(w, '{') or_return
|
|
||||||
if m != nil {
|
if m != nil {
|
||||||
if info.generated_struct == nil {
|
if info.generated_struct == nil {
|
||||||
return .Unsupported_Type
|
return .Unsupported_Type
|
||||||
@@ -222,31 +267,54 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
entry_size := ed.elem_size
|
entry_size := ed.elem_size
|
||||||
|
|
||||||
for i in 0..<entries.len {
|
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)
|
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||||
key := rawptr(data + entry_type.offsets[2])
|
key := rawptr(data + entry_type.offsets[2])
|
||||||
value := rawptr(data + entry_type.offsets[3])
|
value := rawptr(data + entry_type.offsets[3])
|
||||||
|
|
||||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
// check for string type
|
||||||
io.write_string(w, ": ") or_return
|
{
|
||||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
v := any{key, info.key.id}
|
||||||
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
|
a := any{v.data, ti.id}
|
||||||
|
name: string
|
||||||
|
|
||||||
|
#partial switch info in ti.variant {
|
||||||
|
case runtime.Type_Info_String:
|
||||||
|
switch s in a {
|
||||||
|
case string: name = s
|
||||||
|
case cstring: name = string(s)
|
||||||
|
}
|
||||||
|
opt_write_key(w, opt, name) or_return
|
||||||
|
|
||||||
|
case: return .Unsupported_Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
io.write_byte(w, '}') or_return
|
|
||||||
|
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_write_end(w, opt, '}') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Struct:
|
case runtime.Type_Info_Struct:
|
||||||
io.write_byte(w, '{') or_return
|
opt_write_start(w, opt, '{') or_return
|
||||||
|
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
if i > 0 { io.write_string(w, ", ") or_return }
|
opt_write_iteration(w, opt, i) or_return
|
||||||
io.write_quoted_string(w, name) or_return
|
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
|
||||||
io.write_string(w, ": ") or_return
|
opt_write_key(w, opt, json_name) or_return
|
||||||
|
} else {
|
||||||
|
opt_write_key(w, opt, name) or_return
|
||||||
|
}
|
||||||
|
|
||||||
id := info.types[i].id
|
id := info.types[i].id
|
||||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||||
marshal_to_writer(w, any{data, id}) or_return
|
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||||
}
|
}
|
||||||
io.write_byte(w, '}') or_return
|
|
||||||
|
opt_write_end(w, opt, '}') or_return
|
||||||
|
|
||||||
case runtime.Type_Info_Union:
|
case runtime.Type_Info_Union:
|
||||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||||
@@ -269,11 +337,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
io.write_string(w, "null") or_return
|
io.write_string(w, "null") or_return
|
||||||
} else {
|
} else {
|
||||||
id := info.variants[tag-1].id
|
id := info.variants[tag-1].id
|
||||||
return marshal_to_writer(w, any{v.data, id})
|
return marshal_to_writer(w, any{v.data, id}, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Enum:
|
case runtime.Type_Info_Enum:
|
||||||
return marshal_to_writer(w, any{v.data, info.base.id})
|
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||||
|
|
||||||
case runtime.Type_Info_Bit_Set:
|
case runtime.Type_Info_Bit_Set:
|
||||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||||
@@ -329,3 +397,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write key as quoted string or with optional quotes in mjson
|
||||||
|
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||||
|
switch opt.spec {
|
||||||
|
case .JSON, .JSON5:
|
||||||
|
io.write_quoted_string(w, name) or_return
|
||||||
|
io.write_string(w, ": ") or_return
|
||||||
|
|
||||||
|
case .MJSON:
|
||||||
|
if opt.mjson_keys_use_quotes {
|
||||||
|
io.write_quoted_string(w, name) or_return
|
||||||
|
} else {
|
||||||
|
io.write_string(w, name) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.mjson_keys_use_equal_sign {
|
||||||
|
io.write_string(w, " = ") or_return
|
||||||
|
} else {
|
||||||
|
io.write_string(w, ": ") or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert start byte and increase indentation on pretty
|
||||||
|
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||||
|
// skip mjson starting braces
|
||||||
|
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
||||||
|
opt.mjson_skipped_first_braces_start = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_byte(w, c) or_return
|
||||||
|
opt.indentation += 1
|
||||||
|
|
||||||
|
if opt.pretty {
|
||||||
|
io.write_byte(w, '\n') or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert comma seperation and write indentations
|
||||||
|
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||||
|
switch opt.spec {
|
||||||
|
case .JSON, .JSON5:
|
||||||
|
if iteration > 0 {
|
||||||
|
io.write_string(w, ", ") or_return
|
||||||
|
|
||||||
|
if opt.pretty {
|
||||||
|
io.write_byte(w, '\n') or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_write_indentation(w, opt) or_return
|
||||||
|
|
||||||
|
case .MJSON:
|
||||||
|
if iteration > 0 {
|
||||||
|
// on pretty no commas necessary
|
||||||
|
if opt.pretty {
|
||||||
|
io.write_byte(w, '\n') or_return
|
||||||
|
} else {
|
||||||
|
// comma seperation necessary for non pretty output!
|
||||||
|
io.write_string(w, ", ") or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt_write_indentation(w, opt) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// decrease indent, write spacing and insert end byte
|
||||||
|
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||||
|
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
||||||
|
if opt.indentation == 0 {
|
||||||
|
opt.mjson_skipped_first_braces_end = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.indentation -= 1
|
||||||
|
|
||||||
|
if opt.pretty {
|
||||||
|
io.write_byte(w, '\n') or_return
|
||||||
|
opt_write_indentation(w, opt) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.write_byte(w, c) or_return
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes current indentation level based on options
|
||||||
|
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
|
||||||
|
if !opt.pretty {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if opt.use_spaces {
|
||||||
|
spaces := opt.spaces == 0 ? 4 : opt.spaces
|
||||||
|
for _ in 0..<opt.indentation * spaces {
|
||||||
|
io.write_byte(w, ' ') or_return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _ in 0..<opt.indentation {
|
||||||
|
io.write_byte(w, '\t') or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
|||||||
return parse_object(&p)
|
return parse_object(&p)
|
||||||
case .JSON5:
|
case .JSON5:
|
||||||
return parse_value(&p)
|
return parse_value(&p)
|
||||||
case .MJSON:
|
case .SJSON:
|
||||||
#partial switch p.curr_token.kind {
|
#partial switch p.curr_token.kind {
|
||||||
case .Ident, .String:
|
case .Ident, .String:
|
||||||
return parse_object_body(&p, .EOF)
|
return parse_object_body(&p, .EOF)
|
||||||
@@ -354,6 +354,12 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
|||||||
|
|
||||||
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
|
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
|
||||||
w := copy(b, s[0:i])
|
w := copy(b, s[0:i])
|
||||||
|
|
||||||
|
if len(b) == 0 && allocator.data == nil {
|
||||||
|
// `unmarshal_count_array` calls us with a nil allocator
|
||||||
|
return string(b[:w]), nil
|
||||||
|
}
|
||||||
|
|
||||||
loop: for i < len(s) {
|
loop: for i < len(s) {
|
||||||
c := s[i]
|
c := s[i]
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -33,8 +33,9 @@ package json
|
|||||||
Specification :: enum {
|
Specification :: enum {
|
||||||
JSON,
|
JSON,
|
||||||
JSON5, // https://json5.org/
|
JSON5, // https://json5.org/
|
||||||
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||||
Bitsquid = MJSON,
|
Bitsquid = SJSON,
|
||||||
|
MJSON = SJSON,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
|||||||
variant := u.variants[0]
|
variant := u.variants[0]
|
||||||
v.id = variant.id
|
v.id = variant.id
|
||||||
ti = reflect.type_info_base(variant)
|
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}
|
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||||
assign_int(tag, 1)
|
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}
|
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
||||||
|
|
||||||
if end_token == .Close_Brace {
|
if end_token == .Close_Brace {
|
||||||
assert(expect_token(p, .Open_Brace) == nil)
|
unmarshal_expect_token(p, .Open_Brace)
|
||||||
}
|
}
|
||||||
|
|
||||||
v := v
|
v := v
|
||||||
@@ -380,13 +380,18 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
field := any{field_ptr, type.id}
|
field := any{field_ptr, type.id}
|
||||||
unmarshal_value(p, field) or_return
|
unmarshal_value(p, field) or_return
|
||||||
|
|
||||||
|
if parse_comma(p) {
|
||||||
|
break struct_loop
|
||||||
|
}
|
||||||
|
continue struct_loop
|
||||||
|
} else {
|
||||||
|
// allows skipping unused struct fields
|
||||||
|
parse_value(p) or_return
|
||||||
if parse_comma(p) {
|
if parse_comma(p) {
|
||||||
break struct_loop
|
break struct_loop
|
||||||
}
|
}
|
||||||
continue struct_loop
|
continue struct_loop
|
||||||
}
|
}
|
||||||
|
|
||||||
return Unsupported_Type_Error{v.id, p.curr_token}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Type_Info_Map:
|
case reflect.Type_Info_Map:
|
||||||
@@ -398,7 +403,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
raw_map.entries.allocator = p.allocator
|
raw_map.entries.allocator = p.allocator
|
||||||
}
|
}
|
||||||
|
|
||||||
header := runtime.__get_map_header_runtime(raw_map, t)
|
header := runtime.__get_map_header_table_runtime(t)
|
||||||
|
|
||||||
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||||
defer delete(elem_backing, p.allocator)
|
defer delete(elem_backing, p.allocator)
|
||||||
@@ -416,18 +421,16 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := runtime.Map_Hash {
|
key_hash := runtime.default_hasher_string(&key, 0)
|
||||||
hash = runtime.default_hasher_string(&key, 0),
|
key_ptr := rawptr(&key)
|
||||||
key_ptr = &key,
|
|
||||||
}
|
|
||||||
|
|
||||||
key_cstr: cstring
|
key_cstr: cstring
|
||||||
if reflect.is_cstring(t.key) {
|
if reflect.is_cstring(t.key) {
|
||||||
key_cstr = cstring(raw_data(key))
|
key_cstr = cstring(raw_data(key))
|
||||||
hash.key_ptr = &key_cstr
|
key_ptr = &key_cstr
|
||||||
}
|
}
|
||||||
|
|
||||||
set_ptr := runtime.__dynamic_map_set(header, hash, map_backing_value.data)
|
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
|
||||||
if set_ptr == nil {
|
if set_ptr == nil {
|
||||||
delete(key, p.allocator)
|
delete(key, p.allocator)
|
||||||
}
|
}
|
||||||
@@ -473,7 +476,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
}
|
}
|
||||||
|
|
||||||
if end_token == .Close_Brace {
|
if end_token == .Close_Brace {
|
||||||
assert(expect_token(p, .Close_Brace) == nil)
|
unmarshal_expect_token(p, .Close_Brace)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
|
||||||
|
|
||||||
|
Author of this Odin package: Jeroen van Rijn
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```odin
|
||||||
|
import "core:encoding/varint"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
buf: [varint.LEB128_MAX_BYTES]u8
|
||||||
|
|
||||||
|
value := u128(42)
|
||||||
|
|
||||||
|
encode_size, encode_err := varint.encode_uleb128(buf[:], value)
|
||||||
|
assert(encode_size == 1 && encode_err == .None)
|
||||||
|
|
||||||
|
fmt.printf("Encoded as %v\n", buf[:encode_size])
|
||||||
|
decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:])
|
||||||
|
|
||||||
|
assert(decoded_val == value && decode_size == encode_size && decode_err == .None)
|
||||||
|
fmt.printf("Decoded as %v, using %v byte%v\n", decoded_val, decode_size, "" if decode_size == 1 else "s")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*/
|
||||||
|
package varint
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// package varint implements variable length integer encoding and decoding using
|
||||||
|
// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
|
||||||
|
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
|
||||||
|
|
||||||
|
Error :: enum {
|
||||||
|
None = 0,
|
||||||
|
Buffer_Too_Small = 1,
|
||||||
|
Value_Too_Large = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
|
||||||
|
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
|
||||||
|
decode_uleb128_buffer :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return 0, 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
for v in buf {
|
||||||
|
val, size, err = decode_uleb128_byte(v, size, val)
|
||||||
|
if err != .Buffer_Too_Small {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == .Buffer_Too_Small {
|
||||||
|
val, size = 0, 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decodes an unsigned LEB128 integer into value a byte at a time.
|
||||||
|
// Returns `.None` when decoded properly, `.Value_Too_Large` when they value
|
||||||
|
// exceeds the limits of a u128, and `.Buffer_Too_Small` when it's not yet fully decoded.
|
||||||
|
decode_uleb128_byte :: proc(input: u8, offset: int, accumulator: u128) -> (val: u128, size: int, err: Error) {
|
||||||
|
size = offset + 1
|
||||||
|
|
||||||
|
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
|
||||||
|
if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0b0000_0011 {
|
||||||
|
return 0, 0, .Value_Too_Large
|
||||||
|
}
|
||||||
|
|
||||||
|
val = accumulator | u128(input & 0x7f) << uint(offset * 7)
|
||||||
|
|
||||||
|
if input < 128 {
|
||||||
|
// We're done
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the buffer runs out before the number ends, return an error.
|
||||||
|
return val, size, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
decode_uleb128 :: proc {decode_uleb128_buffer, decode_uleb128_byte}
|
||||||
|
|
||||||
|
// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
|
||||||
|
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
|
||||||
|
decode_ileb128_buffer :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return 0, 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
for v in buf {
|
||||||
|
val, size, err = decode_ileb128_byte(v, size, val)
|
||||||
|
if err != .Buffer_Too_Small {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == .Buffer_Too_Small {
|
||||||
|
val, size = 0, 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode a a signed LEB128 integer into value and number of bytes used, one byte at a time.
|
||||||
|
// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
|
||||||
|
decode_ileb128_byte :: proc(input: u8, offset: int, accumulator: i128) -> (val: i128, size: int, err: Error) {
|
||||||
|
size = offset + 1
|
||||||
|
shift := uint(offset * 7)
|
||||||
|
|
||||||
|
// 18 * 7 bits = 126, which including sign means we can have a 19th byte.
|
||||||
|
if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0x7f {
|
||||||
|
return 0, 0, .Value_Too_Large
|
||||||
|
}
|
||||||
|
|
||||||
|
val = accumulator | i128(input & 0x7f) << shift
|
||||||
|
|
||||||
|
if input < 128 {
|
||||||
|
if input & 0x40 == 0x40 {
|
||||||
|
val |= max(i128) << (shift + 7)
|
||||||
|
}
|
||||||
|
return val, size, .None
|
||||||
|
}
|
||||||
|
return val, size, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
decode_ileb128 :: proc{decode_ileb128_buffer, decode_ileb128_byte}
|
||||||
|
|
||||||
|
// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
|
||||||
|
// `buf` must be appropriately sized.
|
||||||
|
encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
|
||||||
|
val := val
|
||||||
|
|
||||||
|
for {
|
||||||
|
size += 1
|
||||||
|
|
||||||
|
if size > len(buf) {
|
||||||
|
return 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
low := val & 0x7f
|
||||||
|
val >>= 7
|
||||||
|
|
||||||
|
if val > 0 {
|
||||||
|
low |= 0x80 // more bytes to follow
|
||||||
|
}
|
||||||
|
buf[size - 1] = u8(low)
|
||||||
|
|
||||||
|
if val == 0 { break }
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
SIGN_MASK :: i128(1) << 121 // sign extend mask
|
||||||
|
|
||||||
|
val, more := val, true
|
||||||
|
|
||||||
|
for more {
|
||||||
|
size += 1
|
||||||
|
|
||||||
|
if size > len(buf) {
|
||||||
|
return 0, .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
|
||||||
|
low := val & 0x7f
|
||||||
|
val >>= 7
|
||||||
|
|
||||||
|
low = (low ~ SIGN_MASK) - SIGN_MASK
|
||||||
|
|
||||||
|
if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
|
||||||
|
more = false
|
||||||
|
} else {
|
||||||
|
low |= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[size - 1] = u8(low)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
|
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
package xml
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Just for debug purposes.
|
||||||
|
*/
|
||||||
|
print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
|
||||||
|
if doc == nil { return }
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
written += wprintf(writer, "[XML Prolog]\n")
|
||||||
|
|
||||||
|
for attr in doc.prologue {
|
||||||
|
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
written += wprintf(writer, "[Encoding] %v\n", doc.encoding)
|
||||||
|
|
||||||
|
if len(doc.doctype.ident) > 0 {
|
||||||
|
written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
|
||||||
|
|
||||||
|
if len(doc.doctype.rest) > 0 {
|
||||||
|
wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for comment in doc.comments {
|
||||||
|
written += wprintf(writer, "[Pre-root comment] %v\n", comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(doc.elements) > 0 {
|
||||||
|
wprintln(writer, " --- ")
|
||||||
|
print_element(writer, doc, 0)
|
||||||
|
wprintln(writer, " --- ")
|
||||||
|
}
|
||||||
|
|
||||||
|
return written, .None
|
||||||
|
}
|
||||||
|
|
||||||
|
print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
tab :: proc(writer: io.Writer, indent: int) {
|
||||||
|
for _ in 0..=indent {
|
||||||
|
wprintf(writer, "\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tab(writer, indent)
|
||||||
|
|
||||||
|
element := doc.elements[element_id]
|
||||||
|
|
||||||
|
if element.kind == .Element {
|
||||||
|
wprintf(writer, "<%v>\n", element.ident)
|
||||||
|
if len(element.value) > 0 {
|
||||||
|
tab(writer, indent + 1)
|
||||||
|
wprintf(writer, "[Value] %v\n", element.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
for attr in element.attribs {
|
||||||
|
tab(writer, indent + 1)
|
||||||
|
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in element.children {
|
||||||
|
print_element(writer, doc, child, indent + 1)
|
||||||
|
}
|
||||||
|
} else if element.kind == .Comment {
|
||||||
|
wprintf(writer, "[COMMENT] %v\n", element.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return written, .None
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package xml_example
|
||||||
|
|
||||||
|
import "core:encoding/xml"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:time"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:hash"
|
||||||
|
|
||||||
|
N :: 1
|
||||||
|
|
||||||
|
example :: proc() {
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
docs: [N]^xml.Document
|
||||||
|
errs: [N]xml.Error
|
||||||
|
times: [N]time.Duration
|
||||||
|
|
||||||
|
defer for round in 0..<N {
|
||||||
|
xml.destroy(docs[round])
|
||||||
|
}
|
||||||
|
|
||||||
|
DOC :: #load("../../../../tests/core/assets/XML/unicode.xml")
|
||||||
|
input := DOC
|
||||||
|
|
||||||
|
for round in 0..<N {
|
||||||
|
start := time.tick_now()
|
||||||
|
|
||||||
|
docs[round], errs[round] = xml.parse(input, xml.Options{
|
||||||
|
flags={.Ignore_Unsupported},
|
||||||
|
expected_doctype = "",
|
||||||
|
})
|
||||||
|
|
||||||
|
end := time.tick_now()
|
||||||
|
times[round] = time.tick_diff(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
fastest := max(time.Duration)
|
||||||
|
slowest := time.Duration(0)
|
||||||
|
total := time.Duration(0)
|
||||||
|
|
||||||
|
for round in 0..<N {
|
||||||
|
fastest = min(fastest, times[round])
|
||||||
|
slowest = max(slowest, times[round])
|
||||||
|
total += times[round]
|
||||||
|
}
|
||||||
|
|
||||||
|
fastest_ms := time.duration_milliseconds(fastest)
|
||||||
|
slowest_ms := time.duration_milliseconds(slowest)
|
||||||
|
average_ms := time.duration_milliseconds(time.Duration(f64(total) / f64(N)))
|
||||||
|
|
||||||
|
fastest_speed := (f64(1000.0) / fastest_ms) * f64(len(DOC)) / 1_024.0 / 1_024.0
|
||||||
|
slowest_speed := (f64(1000.0) / slowest_ms) * f64(len(DOC)) / 1_024.0 / 1_024.0
|
||||||
|
average_speed := (f64(1000.0) / average_ms) * f64(len(DOC)) / 1_024.0 / 1_024.0
|
||||||
|
|
||||||
|
fmt.printf("N = %v\n", N)
|
||||||
|
fmt.printf("[Fastest]: %v bytes in %.2f ms (%.2f MiB/s).\n", len(input), fastest_ms, fastest_speed)
|
||||||
|
fmt.printf("[Slowest]: %v bytes in %.2f ms (%.2f MiB/s).\n", len(input), slowest_ms, slowest_speed)
|
||||||
|
fmt.printf("[Average]: %v bytes in %.2f ms (%.2f MiB/s).\n", len(input), average_ms, average_speed)
|
||||||
|
|
||||||
|
if errs[0] != .None {
|
||||||
|
printf("Load/Parse error: %v\n", errs[0])
|
||||||
|
if errs[0] == .File_Error {
|
||||||
|
println("\"unicode.xml\" not found. Did you run \"tests\\download_assets.py\"?")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
charlist, charlist_ok := xml.find_child_by_ident(docs[0], 0, "charlist")
|
||||||
|
if !charlist_ok {
|
||||||
|
eprintln("Could not locate top-level `<charlist>` tag.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count)
|
||||||
|
|
||||||
|
crc32 := doc_hash(docs[0])
|
||||||
|
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32)
|
||||||
|
|
||||||
|
for round in 0..<N {
|
||||||
|
defer xml.destroy(docs[round])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
|
||||||
|
buf: strings.Builder
|
||||||
|
defer strings.builder_destroy(&buf)
|
||||||
|
w := strings.to_writer(&buf)
|
||||||
|
|
||||||
|
xml.print(w, doc)
|
||||||
|
tree := strings.to_string(buf)
|
||||||
|
if print { fmt.println(tree) }
|
||||||
|
return hash.crc32(transmute([]u8)tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
using fmt
|
||||||
|
|
||||||
|
track: mem.Tracking_Allocator
|
||||||
|
mem.tracking_allocator_init(&track, context.allocator)
|
||||||
|
context.allocator = mem.tracking_allocator(&track)
|
||||||
|
|
||||||
|
example()
|
||||||
|
|
||||||
|
if len(track.allocation_map) > 0 {
|
||||||
|
println()
|
||||||
|
for _, v in track.allocation_map {
|
||||||
|
printf("%v Leaked %v bytes.\n", v.location, v.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("Done and cleaned up!")
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
|
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
This file contains helper functions.
|
||||||
|
*/
|
||||||
|
package xml
|
||||||
|
|
||||||
|
// Find parent's nth child with a given ident.
|
||||||
|
find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
|
||||||
|
tag := doc.elements[parent_id]
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for child_id in tag.children {
|
||||||
|
child := doc.elements[child_id]
|
||||||
|
/*
|
||||||
|
Skip commments. They have no name.
|
||||||
|
*/
|
||||||
|
if child.kind != .Element { continue }
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the ident matches and it's the nth such child, return it.
|
||||||
|
*/
|
||||||
|
if child.ident == ident {
|
||||||
|
if count == nth { return child_id, true }
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an attribute by key.
|
||||||
|
find_attribute_val_by_key :: proc(doc: ^Document, parent_id: Element_ID, key: string) -> (val: string, found: bool) {
|
||||||
|
tag := doc.elements[parent_id]
|
||||||
|
|
||||||
|
for attr in tag.attribs {
|
||||||
|
/*
|
||||||
|
If the ident matches, we're done. There can only ever be one attribute with the same name.
|
||||||
|
*/
|
||||||
|
if attr.key == key { return attr.val, true }
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
@@ -0,0 +1,436 @@
|
|||||||
|
/*
|
||||||
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
|
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
A from-scratch XML implementation, loosely modeled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
package xml
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:unicode"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
|
||||||
|
|
||||||
|
Token :: struct {
|
||||||
|
kind: Token_Kind,
|
||||||
|
text: string,
|
||||||
|
pos: Pos,
|
||||||
|
}
|
||||||
|
|
||||||
|
Pos :: struct {
|
||||||
|
file: string,
|
||||||
|
offset: int, // starting at 0
|
||||||
|
line: int, // starting at 1
|
||||||
|
column: int, // starting at 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Token_Kind :: enum {
|
||||||
|
Invalid,
|
||||||
|
|
||||||
|
Ident,
|
||||||
|
Literal,
|
||||||
|
Rune,
|
||||||
|
String,
|
||||||
|
|
||||||
|
Double_Quote, // "
|
||||||
|
Single_Quote, // '
|
||||||
|
Colon, // :
|
||||||
|
|
||||||
|
Eq, // =
|
||||||
|
Lt, // <
|
||||||
|
Gt, // >
|
||||||
|
Exclaim, // !
|
||||||
|
Question, // ?
|
||||||
|
Hash, // #
|
||||||
|
Slash, // /
|
||||||
|
Dash, // -
|
||||||
|
|
||||||
|
Open_Bracket, // [
|
||||||
|
Close_Bracket, // ]
|
||||||
|
|
||||||
|
EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
CDATA_START :: "<![CDATA["
|
||||||
|
CDATA_END :: "]]>"
|
||||||
|
|
||||||
|
COMMENT_START :: "<!--"
|
||||||
|
COMMENT_END :: "-->"
|
||||||
|
|
||||||
|
Tokenizer :: struct {
|
||||||
|
// Immutable data
|
||||||
|
path: string,
|
||||||
|
src: string,
|
||||||
|
err: Error_Handler,
|
||||||
|
|
||||||
|
// Tokenizing state
|
||||||
|
ch: rune,
|
||||||
|
offset: int,
|
||||||
|
read_offset: int,
|
||||||
|
line_offset: int,
|
||||||
|
line_count: int,
|
||||||
|
|
||||||
|
// Mutable data
|
||||||
|
error_count: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) {
|
||||||
|
t.src = src
|
||||||
|
t.err = err
|
||||||
|
t.ch = ' '
|
||||||
|
t.offset = 0
|
||||||
|
t.read_offset = 0
|
||||||
|
t.line_offset = 0
|
||||||
|
t.line_count = len(src) > 0 ? 1 : 0
|
||||||
|
t.error_count = 0
|
||||||
|
t.path = path
|
||||||
|
|
||||||
|
advance_rune(t)
|
||||||
|
if t.ch == utf8.RUNE_BOM {
|
||||||
|
advance_rune(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
|
||||||
|
line := t.line_count
|
||||||
|
column := offset - t.line_offset + 1
|
||||||
|
|
||||||
|
return Pos {
|
||||||
|
file = t.path,
|
||||||
|
offset = offset,
|
||||||
|
line = line,
|
||||||
|
column = column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||||
|
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
|
||||||
|
fmt.eprintf(msg, ..args)
|
||||||
|
fmt.eprintf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||||
|
pos := offset_to_pos(t, offset)
|
||||||
|
if t.err != nil {
|
||||||
|
t.err(pos, msg, ..args)
|
||||||
|
}
|
||||||
|
t.error_count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="speed")
|
||||||
|
advance_rune :: proc(using t: ^Tokenizer) {
|
||||||
|
#no_bounds_check {
|
||||||
|
/*
|
||||||
|
Already bounds-checked here.
|
||||||
|
*/
|
||||||
|
if read_offset < len(src) {
|
||||||
|
offset = read_offset
|
||||||
|
if ch == '\n' {
|
||||||
|
line_offset = offset
|
||||||
|
line_count += 1
|
||||||
|
}
|
||||||
|
r, w := rune(src[read_offset]), 1
|
||||||
|
switch {
|
||||||
|
case r == 0:
|
||||||
|
error(t, t.offset, "illegal character NUL")
|
||||||
|
case r >= utf8.RUNE_SELF:
|
||||||
|
r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:])
|
||||||
|
if r == utf8.RUNE_ERROR && w == 1 {
|
||||||
|
error(t, t.offset, "illegal UTF-8 encoding")
|
||||||
|
} else if r == utf8.RUNE_BOM && offset > 0 {
|
||||||
|
error(t, t.offset, "illegal byte order mark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read_offset += w
|
||||||
|
ch = r
|
||||||
|
} else {
|
||||||
|
offset = len(src)
|
||||||
|
if ch == '\n' {
|
||||||
|
line_offset = offset
|
||||||
|
line_count += 1
|
||||||
|
}
|
||||||
|
ch = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
||||||
|
if t.read_offset+offset < len(t.src) {
|
||||||
|
#no_bounds_check return t.src[t.read_offset+offset]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="speed")
|
||||||
|
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||||
|
for {
|
||||||
|
switch t.ch {
|
||||||
|
case ' ', '\t', '\r', '\n':
|
||||||
|
advance_rune(t)
|
||||||
|
case:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="speed")
|
||||||
|
is_letter :: proc(r: rune) -> bool {
|
||||||
|
if r < utf8.RUNE_SELF {
|
||||||
|
switch r {
|
||||||
|
case '_':
|
||||||
|
return true
|
||||||
|
case 'A'..='Z', 'a'..='z':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unicode.is_letter(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
is_valid_identifier_rune :: proc(r: rune) -> bool {
|
||||||
|
if r < utf8.RUNE_SELF {
|
||||||
|
switch r {
|
||||||
|
case '_', '-', ':': return true
|
||||||
|
case 'A'..='Z', 'a'..='z': return true
|
||||||
|
case '0'..='9': return true
|
||||||
|
case -1: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode.is_letter(r) || unicode.is_digit(r) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||||
|
offset := t.offset
|
||||||
|
namespaced := false
|
||||||
|
|
||||||
|
for is_valid_identifier_rune(t.ch) {
|
||||||
|
advance_rune(t)
|
||||||
|
if t.ch == ':' {
|
||||||
|
/*
|
||||||
|
A namespaced attr can have at most two parts, `namespace:ident`.
|
||||||
|
*/
|
||||||
|
if namespaced {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
namespaced = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(t.src[offset : t.offset])
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A comment ends when we see -->, preceded by a character that's not a dash.
|
||||||
|
"For compatibility, the string "--" (double-hyphen) must not occur within comments."
|
||||||
|
|
||||||
|
See: https://www.w3.org/TR/2006/REC-xml11-20060816/#dt-comment
|
||||||
|
|
||||||
|
Thanks to the length (4) of the comment start, we also have enough lookback,
|
||||||
|
and the peek at the next byte asserts that there's at least one more character
|
||||||
|
that's a `>`.
|
||||||
|
*/
|
||||||
|
scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
|
||||||
|
offset := t.offset
|
||||||
|
|
||||||
|
for {
|
||||||
|
advance_rune(t)
|
||||||
|
ch := t.ch
|
||||||
|
|
||||||
|
if ch < 0 {
|
||||||
|
error(t, offset, "[parse] Comment was not terminated\n")
|
||||||
|
return "", .Unclosed_Comment
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(t.src[t.offset - 1:][:2]) == "--" {
|
||||||
|
if peek_byte(t) == '>' {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
error(t, t.offset - 1, "Invalid -- sequence in comment.\n")
|
||||||
|
return "", .Invalid_Sequence_In_Comment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(t, .Dash)
|
||||||
|
expect(t, .Gt)
|
||||||
|
|
||||||
|
return string(t.src[offset : t.offset - 1]), .None
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Skip CDATA
|
||||||
|
*/
|
||||||
|
skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||||
|
if t.read_offset + len(CDATA_START) >= len(t.src) {
|
||||||
|
/*
|
||||||
|
Can't be the start of a CDATA tag.
|
||||||
|
*/
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
|
||||||
|
t.read_offset += len(CDATA_START)
|
||||||
|
offset := t.offset
|
||||||
|
|
||||||
|
cdata_scan: for {
|
||||||
|
advance_rune(t)
|
||||||
|
if t.ch < 0 {
|
||||||
|
error(t, offset, "[scan_string] CDATA was not terminated\n")
|
||||||
|
return .Premature_EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scan until the end of a CDATA tag.
|
||||||
|
*/
|
||||||
|
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||||
|
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||||
|
t.read_offset += len(CDATA_END)
|
||||||
|
break cdata_scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="speed")
|
||||||
|
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
|
||||||
|
err = .None
|
||||||
|
|
||||||
|
loop: for {
|
||||||
|
ch := t.ch
|
||||||
|
|
||||||
|
switch ch {
|
||||||
|
case -1:
|
||||||
|
error(t, t.offset, "[scan_string] Premature end of file.\n")
|
||||||
|
return "", .Premature_EOF
|
||||||
|
|
||||||
|
case '<':
|
||||||
|
if peek_byte(t) == '!' {
|
||||||
|
if peek_byte(t, 1) == '[' {
|
||||||
|
/*
|
||||||
|
Might be the start of a CDATA tag.
|
||||||
|
*/
|
||||||
|
skip_cdata(t) or_return
|
||||||
|
} else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' {
|
||||||
|
/*
|
||||||
|
Comment start. Eat comment.
|
||||||
|
*/
|
||||||
|
t.read_offset += 3
|
||||||
|
_ = scan_comment(t) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
if !multiline {
|
||||||
|
error(t, offset, string(t.src[offset : t.offset]))
|
||||||
|
error(t, offset, "[scan_string] Not terminated\n")
|
||||||
|
err = .Invalid_Tag_Value
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.ch == close {
|
||||||
|
/*
|
||||||
|
If it's not a CDATA or comment, it's the end of this body.
|
||||||
|
*/
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
advance_rune(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Strip trailing whitespace.
|
||||||
|
*/
|
||||||
|
lit := string(t.src[offset : t.offset])
|
||||||
|
|
||||||
|
end := len(lit)
|
||||||
|
eat: for ; end > 0; end -= 1 {
|
||||||
|
ch := lit[end - 1]
|
||||||
|
switch ch {
|
||||||
|
case ' ', '\t', '\r', '\n':
|
||||||
|
case:
|
||||||
|
break eat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lit = lit[:end]
|
||||||
|
|
||||||
|
if consume_close {
|
||||||
|
advance_rune(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: Handle decoding escape characters and unboxing CDATA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return lit, err
|
||||||
|
}
|
||||||
|
|
||||||
|
peek :: proc(t: ^Tokenizer) -> (token: Token) {
|
||||||
|
old := t^
|
||||||
|
token = scan(t)
|
||||||
|
t^ = old
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
scan :: proc(t: ^Tokenizer) -> Token {
|
||||||
|
skip_whitespace(t)
|
||||||
|
|
||||||
|
offset := t.offset
|
||||||
|
|
||||||
|
kind: Token_Kind
|
||||||
|
err: Error
|
||||||
|
lit: string
|
||||||
|
pos := offset_to_pos(t, offset)
|
||||||
|
|
||||||
|
switch ch := t.ch; true {
|
||||||
|
case is_letter(ch):
|
||||||
|
lit = scan_identifier(t)
|
||||||
|
kind = .Ident
|
||||||
|
|
||||||
|
case:
|
||||||
|
advance_rune(t)
|
||||||
|
switch ch {
|
||||||
|
case -1:
|
||||||
|
kind = .EOF
|
||||||
|
|
||||||
|
case '<': kind = .Lt
|
||||||
|
case '>': kind = .Gt
|
||||||
|
case '!': kind = .Exclaim
|
||||||
|
case '?': kind = .Question
|
||||||
|
case '=': kind = .Eq
|
||||||
|
case '#': kind = .Hash
|
||||||
|
case '/': kind = .Slash
|
||||||
|
case '-': kind = .Dash
|
||||||
|
case ':': kind = .Colon
|
||||||
|
|
||||||
|
case '"', '\'':
|
||||||
|
kind = .Invalid
|
||||||
|
|
||||||
|
lit, err = scan_string(t, t.offset, ch, true, false)
|
||||||
|
if err == .None {
|
||||||
|
kind = .String
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
lit = "\n"
|
||||||
|
|
||||||
|
case:
|
||||||
|
kind = .Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kind != .String && lit == "" {
|
||||||
|
lit = string(t.src[offset : t.offset])
|
||||||
|
}
|
||||||
|
return Token{kind, lit, pos}
|
||||||
|
}
|
||||||
@@ -0,0 +1,713 @@
|
|||||||
|
/*
|
||||||
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
|
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
A from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
|
||||||
|
- Simple to understand and use. Small.
|
||||||
|
|
||||||
|
Caveats:
|
||||||
|
- We do NOT support HTML in this package, as that may or may not be valid XML.
|
||||||
|
If it works, great. If it doesn't, that's not considered a bug.
|
||||||
|
|
||||||
|
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
|
||||||
|
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
|
||||||
|
|
||||||
|
MAYBE:
|
||||||
|
- XML writer?
|
||||||
|
- Serialize/deserialize Odin types?
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
*/
|
||||||
|
package xml
|
||||||
|
// An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:encoding/entity"
|
||||||
|
import "core:intrinsics"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:os"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
likely :: intrinsics.expect
|
||||||
|
|
||||||
|
DEFAULT_OPTIONS :: Options{
|
||||||
|
flags = {.Ignore_Unsupported},
|
||||||
|
expected_doctype = "",
|
||||||
|
}
|
||||||
|
|
||||||
|
Option_Flag :: enum {
|
||||||
|
/*
|
||||||
|
If the caller says that input may be modified, we can perform in-situ parsing.
|
||||||
|
If this flag isn't provided, the XML parser first duplicates the input so that it can.
|
||||||
|
*/
|
||||||
|
Input_May_Be_Modified,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Document MUST start with `<?xml` prologue.
|
||||||
|
*/
|
||||||
|
Must_Have_Prolog,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Document MUST have a `<!DOCTYPE`.
|
||||||
|
*/
|
||||||
|
Must_Have_DocType,
|
||||||
|
|
||||||
|
/*
|
||||||
|
By default we skip comments. Use this option to intern a comment on a parented Element.
|
||||||
|
*/
|
||||||
|
Intern_Comments,
|
||||||
|
|
||||||
|
/*
|
||||||
|
How to handle unsupported parts of the specification, like <! other than <!DOCTYPE and <![CDATA[
|
||||||
|
*/
|
||||||
|
Error_on_Unsupported,
|
||||||
|
Ignore_Unsupported,
|
||||||
|
|
||||||
|
/*
|
||||||
|
By default CDATA tags are passed-through as-is.
|
||||||
|
This option unwraps them when encountered.
|
||||||
|
*/
|
||||||
|
Unbox_CDATA,
|
||||||
|
|
||||||
|
/*
|
||||||
|
By default SGML entities like `>`, ` ` and ` ` are passed-through as-is.
|
||||||
|
This option decodes them when encountered.
|
||||||
|
*/
|
||||||
|
Decode_SGML_Entities,
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a tag body has a comment, it will be stripped unless this option is given.
|
||||||
|
*/
|
||||||
|
Keep_Tag_Body_Comments,
|
||||||
|
}
|
||||||
|
Option_Flags :: bit_set[Option_Flag; u16]
|
||||||
|
|
||||||
|
Document :: struct {
|
||||||
|
elements: [dynamic]Element,
|
||||||
|
element_count: Element_ID,
|
||||||
|
|
||||||
|
prologue: Attributes,
|
||||||
|
encoding: Encoding,
|
||||||
|
|
||||||
|
doctype: struct {
|
||||||
|
/*
|
||||||
|
We only scan the <!DOCTYPE IDENT part and skip the rest.
|
||||||
|
*/
|
||||||
|
ident: string,
|
||||||
|
rest: string,
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
|
||||||
|
Otherwise they'll be in the element tree.
|
||||||
|
*/
|
||||||
|
comments: [dynamic]string,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal
|
||||||
|
*/
|
||||||
|
tokenizer: ^Tokenizer,
|
||||||
|
allocator: mem.Allocator,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
|
||||||
|
*/
|
||||||
|
input: []u8,
|
||||||
|
strings_to_free: [dynamic]string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Element :: struct {
|
||||||
|
ident: string,
|
||||||
|
value: string,
|
||||||
|
attribs: Attributes,
|
||||||
|
|
||||||
|
kind: enum {
|
||||||
|
Element = 0,
|
||||||
|
Comment,
|
||||||
|
},
|
||||||
|
|
||||||
|
parent: Element_ID,
|
||||||
|
children: [dynamic]Element_ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
Attribute :: struct {
|
||||||
|
key: string,
|
||||||
|
val: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Attributes :: [dynamic]Attribute
|
||||||
|
|
||||||
|
Options :: struct {
|
||||||
|
flags: Option_Flags,
|
||||||
|
expected_doctype: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoding :: enum {
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
UTF_8,
|
||||||
|
ISO_8859_1,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Aliases
|
||||||
|
*/
|
||||||
|
LATIN_1 = ISO_8859_1,
|
||||||
|
}
|
||||||
|
|
||||||
|
Error :: enum {
|
||||||
|
/*
|
||||||
|
General return values.
|
||||||
|
*/
|
||||||
|
None = 0,
|
||||||
|
General_Error,
|
||||||
|
Unexpected_Token,
|
||||||
|
Invalid_Token,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Couldn't find, open or read file.
|
||||||
|
*/
|
||||||
|
File_Error,
|
||||||
|
|
||||||
|
/*
|
||||||
|
File too short.
|
||||||
|
*/
|
||||||
|
Premature_EOF,
|
||||||
|
|
||||||
|
/*
|
||||||
|
XML-specific errors.
|
||||||
|
*/
|
||||||
|
No_Prolog,
|
||||||
|
Invalid_Prolog,
|
||||||
|
Too_Many_Prologs,
|
||||||
|
|
||||||
|
No_DocType,
|
||||||
|
Too_Many_DocTypes,
|
||||||
|
DocType_Must_Preceed_Elements,
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a DOCTYPE is present _or_ the caller
|
||||||
|
asked for a specific DOCTYPE and the DOCTYPE
|
||||||
|
and root tag don't match, we return `.Invalid_DocType`.
|
||||||
|
*/
|
||||||
|
Invalid_DocType,
|
||||||
|
|
||||||
|
Invalid_Tag_Value,
|
||||||
|
Mismatched_Closing_Tag,
|
||||||
|
|
||||||
|
Unclosed_Comment,
|
||||||
|
Comment_Before_Root_Element,
|
||||||
|
Invalid_Sequence_In_Comment,
|
||||||
|
|
||||||
|
Unsupported_Version,
|
||||||
|
Unsupported_Encoding,
|
||||||
|
|
||||||
|
/*
|
||||||
|
<!FOO are usually skipped.
|
||||||
|
*/
|
||||||
|
Unhandled_Bang,
|
||||||
|
|
||||||
|
Duplicate_Attribute,
|
||||||
|
Conflicting_Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation starts here.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|
||||||
|
opts := validate_options(options) or_return
|
||||||
|
|
||||||
|
/*
|
||||||
|
If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
|
||||||
|
*/
|
||||||
|
if .Input_May_Be_Modified not_in opts.flags {
|
||||||
|
data = bytes.clone(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := &Tokenizer{}
|
||||||
|
init(t, string(data), path, error_handler)
|
||||||
|
|
||||||
|
doc = new(Document)
|
||||||
|
doc.allocator = allocator
|
||||||
|
doc.tokenizer = t
|
||||||
|
doc.input = data
|
||||||
|
|
||||||
|
doc.elements = make([dynamic]Element, 1024, 1024, allocator)
|
||||||
|
|
||||||
|
// strings.intern_init(&doc.intern, allocator, allocator)
|
||||||
|
|
||||||
|
err = .Unexpected_Token
|
||||||
|
element, parent: Element_ID
|
||||||
|
|
||||||
|
tag_is_open := false
|
||||||
|
first_element := true
|
||||||
|
open: Token
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a DOCTYPE is present, the root tag has to match.
|
||||||
|
If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
|
||||||
|
*/
|
||||||
|
expected_doctype := options.expected_doctype
|
||||||
|
|
||||||
|
loop: for {
|
||||||
|
skip_whitespace(t)
|
||||||
|
// NOTE(Jeroen): This is faster as a switch.
|
||||||
|
switch t.ch {
|
||||||
|
case '<':
|
||||||
|
/*
|
||||||
|
Consume peeked `<`
|
||||||
|
*/
|
||||||
|
advance_rune(t)
|
||||||
|
|
||||||
|
open = scan(t)
|
||||||
|
// NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
|
||||||
|
if likely(open.kind, Token_Kind.Ident) == .Ident {
|
||||||
|
/*
|
||||||
|
e.g. <odin - Start of new element.
|
||||||
|
*/
|
||||||
|
element = new_element(doc)
|
||||||
|
tag_is_open = true
|
||||||
|
|
||||||
|
if first_element {
|
||||||
|
/*
|
||||||
|
First element.
|
||||||
|
*/
|
||||||
|
parent = element
|
||||||
|
first_element = false
|
||||||
|
} else {
|
||||||
|
append(&doc.elements[parent].children, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.elements[element].parent = parent
|
||||||
|
doc.elements[element].ident = open.text
|
||||||
|
|
||||||
|
parse_attributes(doc, &doc.elements[element].attribs) or_return
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a DOCTYPE is present _or_ the caller
|
||||||
|
asked for a specific DOCTYPE and the DOCTYPE
|
||||||
|
and root tag don't match, we return .Invalid_Root_Tag.
|
||||||
|
*/
|
||||||
|
if element == 0 { // Root tag?
|
||||||
|
if len(expected_doctype) > 0 && expected_doctype != open.text {
|
||||||
|
error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
|
||||||
|
return doc, .Invalid_DocType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
One of these should follow:
|
||||||
|
- `>`, which means we've just opened this tag and expect a later element to close it.
|
||||||
|
- `/>`, which means this is an 'empty' or self-closing tag.
|
||||||
|
*/
|
||||||
|
end_token := scan(t)
|
||||||
|
#partial switch end_token.kind {
|
||||||
|
case .Gt:
|
||||||
|
/*
|
||||||
|
We're now the new parent.
|
||||||
|
*/
|
||||||
|
parent = element
|
||||||
|
|
||||||
|
case .Slash:
|
||||||
|
/*
|
||||||
|
Empty tag. Close it.
|
||||||
|
*/
|
||||||
|
expect(t, .Gt) or_return
|
||||||
|
parent = doc.elements[element].parent
|
||||||
|
element = parent
|
||||||
|
tag_is_open = false
|
||||||
|
|
||||||
|
case:
|
||||||
|
error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if open.kind == .Slash {
|
||||||
|
/*
|
||||||
|
Close tag.
|
||||||
|
*/
|
||||||
|
ident := expect(t, .Ident) or_return
|
||||||
|
_ = expect(t, .Gt) or_return
|
||||||
|
|
||||||
|
if doc.elements[element].ident != ident.text {
|
||||||
|
error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text)
|
||||||
|
return doc, .Mismatched_Closing_Tag
|
||||||
|
}
|
||||||
|
parent = doc.elements[element].parent
|
||||||
|
element = parent
|
||||||
|
tag_is_open = false
|
||||||
|
|
||||||
|
} else if open.kind == .Exclaim {
|
||||||
|
/*
|
||||||
|
<!
|
||||||
|
*/
|
||||||
|
next := scan(t)
|
||||||
|
#partial switch next.kind {
|
||||||
|
case .Ident:
|
||||||
|
switch next.text {
|
||||||
|
case "DOCTYPE":
|
||||||
|
if len(doc.doctype.ident) > 0 {
|
||||||
|
return doc, .Too_Many_DocTypes
|
||||||
|
}
|
||||||
|
if doc.element_count > 0 {
|
||||||
|
return doc, .DocType_Must_Preceed_Elements
|
||||||
|
}
|
||||||
|
parse_doctype(doc) or_return
|
||||||
|
|
||||||
|
if len(expected_doctype) > 0 && expected_doctype != doc.doctype.ident {
|
||||||
|
error(t, t.offset, "Invalid DOCTYPE. Expected: %v, got: %v\n", expected_doctype, doc.doctype.ident)
|
||||||
|
return doc, .Invalid_DocType
|
||||||
|
}
|
||||||
|
expected_doctype = doc.doctype.ident
|
||||||
|
|
||||||
|
case:
|
||||||
|
if .Error_on_Unsupported in opts.flags {
|
||||||
|
error(t, t.offset, "Unhandled: <!%v\n", next.text)
|
||||||
|
return doc, .Unhandled_Bang
|
||||||
|
}
|
||||||
|
skip_element(t) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Dash:
|
||||||
|
/*
|
||||||
|
Comment: <!-- -->.
|
||||||
|
The grammar does not allow a comment to end in --->
|
||||||
|
*/
|
||||||
|
expect(t, .Dash)
|
||||||
|
comment := scan_comment(t) or_return
|
||||||
|
|
||||||
|
if .Intern_Comments in opts.flags {
|
||||||
|
if len(doc.elements) == 0 {
|
||||||
|
append(&doc.comments, comment)
|
||||||
|
} else {
|
||||||
|
el := new_element(doc)
|
||||||
|
doc.elements[el].parent = element
|
||||||
|
doc.elements[el].kind = .Comment
|
||||||
|
doc.elements[el].value = comment
|
||||||
|
append(&doc.elements[element].children, el)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
error(t, t.offset, "Invalid Token after <!. Expected .Ident, got %#v\n", next)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if open.kind == .Question {
|
||||||
|
/*
|
||||||
|
<?xml
|
||||||
|
*/
|
||||||
|
next := scan(t)
|
||||||
|
#partial switch next.kind {
|
||||||
|
case .Ident:
|
||||||
|
if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
|
||||||
|
parse_prologue(doc) or_return
|
||||||
|
} else if len(doc.prologue) > 0 {
|
||||||
|
/*
|
||||||
|
We've already seen a prologue.
|
||||||
|
*/
|
||||||
|
return doc, .Too_Many_Prologs
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
Could be `<?xml-stylesheet`, etc. Ignore it.
|
||||||
|
*/
|
||||||
|
skip_element(t) or_return
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
error(t, t.offset, "Expected \"<?xml\", got \"<?%v\".", next.text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
error(t, t.offset, "Invalid Token after <: %#v\n", open)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case -1:
|
||||||
|
/*
|
||||||
|
End of file.
|
||||||
|
*/
|
||||||
|
if tag_is_open {
|
||||||
|
return doc, .Premature_EOF
|
||||||
|
}
|
||||||
|
break loop
|
||||||
|
|
||||||
|
case:
|
||||||
|
/*
|
||||||
|
This should be a tag's body text.
|
||||||
|
*/
|
||||||
|
body_text := scan_string(t, t.offset) or_return
|
||||||
|
needs_processing := .Unbox_CDATA in opts.flags
|
||||||
|
needs_processing |= .Decode_SGML_Entities in opts.flags
|
||||||
|
|
||||||
|
if !needs_processing {
|
||||||
|
doc.elements[element].value = body_text
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_opts := entity.XML_Decode_Options{}
|
||||||
|
if .Keep_Tag_Body_Comments not_in opts.flags {
|
||||||
|
decode_opts += { .Comment_Strip }
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Decode_SGML_Entities not_in opts.flags {
|
||||||
|
decode_opts += { .No_Entity_Decode }
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Unbox_CDATA in opts.flags {
|
||||||
|
decode_opts += { .Unbox_CDATA }
|
||||||
|
if .Decode_SGML_Entities in opts.flags {
|
||||||
|
decode_opts += { .Decode_CDATA }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded, decode_err := entity.decode_xml(body_text, decode_opts)
|
||||||
|
if decode_err == .None {
|
||||||
|
doc.elements[element].value = decoded
|
||||||
|
append(&doc.strings_to_free, decoded)
|
||||||
|
} else {
|
||||||
|
doc.elements[element].value = body_text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Must_Have_Prolog in opts.flags && len(doc.prologue) == 0 {
|
||||||
|
return doc, .No_Prolog
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Must_Have_DocType in opts.flags && len(doc.doctype.ident) == 0 {
|
||||||
|
return doc, .No_DocType
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(&doc.elements, int(doc.element_count))
|
||||||
|
return doc, .None
|
||||||
|
}
|
||||||
|
|
||||||
|
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_bytes(_data, options, path, error_handler, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
context.allocator = allocator
|
||||||
|
options := options
|
||||||
|
|
||||||
|
data, data_ok := os.read_entire_file(filename)
|
||||||
|
if !data_ok { return {}, .File_Error }
|
||||||
|
|
||||||
|
options.flags += { .Input_May_Be_Modified }
|
||||||
|
|
||||||
|
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy :: proc(doc: ^Document) {
|
||||||
|
if doc == nil { return }
|
||||||
|
|
||||||
|
for el in doc.elements {
|
||||||
|
delete(el.attribs)
|
||||||
|
delete(el.children)
|
||||||
|
}
|
||||||
|
delete(doc.elements)
|
||||||
|
|
||||||
|
delete(doc.prologue)
|
||||||
|
delete(doc.comments)
|
||||||
|
delete(doc.input)
|
||||||
|
|
||||||
|
for s in doc.strings_to_free {
|
||||||
|
delete(s)
|
||||||
|
}
|
||||||
|
delete(doc.strings_to_free)
|
||||||
|
|
||||||
|
free(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Helpers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
validate_options :: proc(options: Options) -> (validated: Options, err: Error) {
|
||||||
|
validated = options
|
||||||
|
|
||||||
|
if .Error_on_Unsupported in validated.flags && .Ignore_Unsupported in validated.flags {
|
||||||
|
return options, .Conflicting_Options
|
||||||
|
}
|
||||||
|
return validated, .None
|
||||||
|
}
|
||||||
|
|
||||||
|
expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
|
||||||
|
tok = scan(t)
|
||||||
|
if tok.kind == kind { return tok, .None }
|
||||||
|
|
||||||
|
error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind)
|
||||||
|
return tok, .Unexpected_Token
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: Error) {
|
||||||
|
assert(doc != nil)
|
||||||
|
context.allocator = doc.allocator
|
||||||
|
t := doc.tokenizer
|
||||||
|
|
||||||
|
key := expect(t, .Ident) or_return
|
||||||
|
offset = t.offset - len(key.text)
|
||||||
|
|
||||||
|
_ = expect(t, .Eq) or_return
|
||||||
|
value := expect(t, .String) or_return
|
||||||
|
|
||||||
|
attr.key = key.text
|
||||||
|
attr.val = value.text
|
||||||
|
|
||||||
|
err = .None
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return .Duplicate_Attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_attributes :: proc(doc: ^Document, attribs: ^Attributes) -> (err: Error) {
|
||||||
|
assert(doc != nil)
|
||||||
|
context.allocator = doc.allocator
|
||||||
|
t := doc.tokenizer
|
||||||
|
|
||||||
|
for peek(t).kind == .Ident {
|
||||||
|
attr, offset := parse_attribute(doc) or_return
|
||||||
|
check_duplicate_attributes(t, attribs^, attr, offset) or_return
|
||||||
|
append(attribs, attr)
|
||||||
|
}
|
||||||
|
skip_whitespace(t)
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
||||||
|
assert(doc != nil)
|
||||||
|
context.allocator = doc.allocator
|
||||||
|
t := doc.tokenizer
|
||||||
|
|
||||||
|
offset := t.offset
|
||||||
|
parse_attributes(doc, &doc.prologue) or_return
|
||||||
|
|
||||||
|
for attr in doc.prologue {
|
||||||
|
switch attr.key {
|
||||||
|
case "version":
|
||||||
|
switch attr.val {
|
||||||
|
case "1.0", "1.1":
|
||||||
|
case:
|
||||||
|
error(t, offset, "[parse_prologue] Warning: Unhandled XML version: %v\n", attr.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "encoding":
|
||||||
|
switch strings.to_lower(attr.val, context.temp_allocator) {
|
||||||
|
case "utf-8", "utf8":
|
||||||
|
doc.encoding = .UTF_8
|
||||||
|
|
||||||
|
case "latin-1", "latin1", "iso-8859-1":
|
||||||
|
doc.encoding = .LATIN_1
|
||||||
|
|
||||||
|
case:
|
||||||
|
/*
|
||||||
|
Unrecognized encoding, assume UTF-8.
|
||||||
|
*/
|
||||||
|
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
// Ignored.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = expect(t, .Question) or_return
|
||||||
|
_ = expect(t, .Gt) or_return
|
||||||
|
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_element :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||||
|
close := 1
|
||||||
|
|
||||||
|
loop: for {
|
||||||
|
tok := scan(t)
|
||||||
|
#partial switch tok.kind {
|
||||||
|
case .EOF:
|
||||||
|
error(t, t.offset, "[skip_element] Premature EOF\n")
|
||||||
|
return .Premature_EOF
|
||||||
|
|
||||||
|
case .Lt:
|
||||||
|
close += 1
|
||||||
|
|
||||||
|
case .Gt:
|
||||||
|
close -= 1
|
||||||
|
if close == 0 {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_doctype :: proc(doc: ^Document) -> (err: Error) {
|
||||||
|
/*
|
||||||
|
<!DOCTYPE greeting SYSTEM "hello.dtd">
|
||||||
|
|
||||||
|
<!DOCTYPE greeting [
|
||||||
|
<!ELEMENT greeting (#PCDATA)>
|
||||||
|
]>
|
||||||
|
*/
|
||||||
|
assert(doc != nil)
|
||||||
|
context.allocator = doc.allocator
|
||||||
|
t := doc.tokenizer
|
||||||
|
|
||||||
|
tok := expect(t, .Ident) or_return
|
||||||
|
doc.doctype.ident = tok.text
|
||||||
|
|
||||||
|
skip_whitespace(t)
|
||||||
|
offset := t.offset
|
||||||
|
skip_element(t) or_return
|
||||||
|
|
||||||
|
/*
|
||||||
|
-1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
|
||||||
|
*/
|
||||||
|
doc.doctype.rest = string(t.src[offset : t.offset - 1])
|
||||||
|
return .None
|
||||||
|
}
|
||||||
|
|
||||||
|
Element_ID :: u32
|
||||||
|
|
||||||
|
new_element :: proc(doc: ^Document) -> (id: Element_ID) {
|
||||||
|
element_space := len(doc.elements)
|
||||||
|
|
||||||
|
// Need to resize
|
||||||
|
if int(doc.element_count) + 1 > element_space {
|
||||||
|
if element_space < 65536 {
|
||||||
|
element_space *= 2
|
||||||
|
} else {
|
||||||
|
element_space += 65536
|
||||||
|
}
|
||||||
|
resize(&doc.elements, element_space)
|
||||||
|
}
|
||||||
|
|
||||||
|
cur := doc.element_count
|
||||||
|
doc.element_count += 1
|
||||||
|
|
||||||
|
return cur
|
||||||
|
}
|
||||||
+820
-651
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ foreign odin_env {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
write_vtable := &io.Stream_VTable{
|
write_vtable := io.Stream_VTable{
|
||||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||||
fd := u32(uintptr(s.stream_data))
|
fd := u32(uintptr(s.stream_data))
|
||||||
write(fd, p)
|
write(fd, p)
|
||||||
@@ -22,14 +22,14 @@ write_vtable := &io.Stream_VTable{
|
|||||||
@(private="file")
|
@(private="file")
|
||||||
stdout := io.Writer{
|
stdout := io.Writer{
|
||||||
stream = {
|
stream = {
|
||||||
stream_vtable = write_vtable,
|
stream_vtable = &write_vtable,
|
||||||
stream_data = rawptr(uintptr(1)),
|
stream_data = rawptr(uintptr(1)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@(private="file")
|
@(private="file")
|
||||||
stderr := io.Writer{
|
stderr := io.Writer{
|
||||||
stream = {
|
stream = {
|
||||||
stream_vtable = write_vtable,
|
stream_vtable = &write_vtable,
|
||||||
stream_data = rawptr(uintptr(2)),
|
stream_data = rawptr(uintptr(2)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||||
result := u64(seed)
|
result = seed
|
||||||
#no_bounds_check for b in data {
|
#no_bounds_check for b in data {
|
||||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
|||||||
length := len(data)
|
length := len(data)
|
||||||
|
|
||||||
for length != 0 && uintptr(buffer) & 7 != 0 {
|
for length != 0 && uintptr(buffer) & 7 != 0 {
|
||||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
buffer = buffer[1:]
|
||||||
length -= 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[1][buf[6]] ~
|
||||||
crc32_table[0][buf[7]]
|
crc32_table[0][buf[7]]
|
||||||
|
|
||||||
buffer = intrinsics.ptr_offset(buffer, 8)
|
buffer = buffer[8:]
|
||||||
length -= 8
|
length -= 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for length != 0 {
|
for length != 0 {
|
||||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
buffer = buffer[1:]
|
||||||
length -= 1
|
length -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-16
@@ -15,7 +15,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
|||||||
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
||||||
a = (a + u64(buf[0]))
|
a = (a + u64(buf[0]))
|
||||||
b = (b + a)
|
b = (b + a)
|
||||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
buffer = buffer[1:]
|
||||||
buf = buf[1:]
|
buf = buf[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,9 +130,9 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
|||||||
h1: u32 = seed
|
h1: u32 = seed
|
||||||
nblocks := len(data)/4
|
nblocks := len(data)/4
|
||||||
p := raw_data(data)
|
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 := (cast(^u32)p)^
|
||||||
|
|
||||||
k1 *= c1_32
|
k1 *= c1_32
|
||||||
@@ -151,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
|||||||
k1 ~= u32(tail[2]) << 16
|
k1 ~= u32(tail[2]) << 16
|
||||||
fallthrough
|
fallthrough
|
||||||
case 2:
|
case 2:
|
||||||
k1 ~= u32(tail[2]) << 8
|
k1 ~= u32(tail[1]) << 8
|
||||||
fallthrough
|
fallthrough
|
||||||
case 1:
|
case 1:
|
||||||
k1 ~= u32(tail[0])
|
k1 ~= u32(tail[0])
|
||||||
@@ -172,14 +172,14 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
|||||||
return h1
|
return h1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
when size_of(int) == 8 {
|
|
||||||
m :: 0xc6a4a7935bd1e995
|
m :: 0xc6a4a7935bd1e995
|
||||||
r :: 47
|
r :: 47
|
||||||
|
|
||||||
h: u64 = seed ~ (u64(len(data)) * m)
|
h: u64 = seed ~ (u64(len(data)) * m)
|
||||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
|
data64 := mem.slice_data_cast([]u64, data)
|
||||||
|
|
||||||
for _, i in data64 {
|
for _, i in data64 {
|
||||||
k := data64[i]
|
k := data64[i]
|
||||||
@@ -192,15 +192,17 @@ murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
h *= m
|
h *= m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset := len(data64) * size_of(u64)
|
||||||
|
|
||||||
switch len(data)&7 {
|
switch len(data)&7 {
|
||||||
case 7: h ~= u64(data[6]) << 48; fallthrough
|
case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
|
||||||
case 6: h ~= u64(data[5]) << 40; fallthrough
|
case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
|
||||||
case 5: h ~= u64(data[4]) << 32; fallthrough
|
case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
|
||||||
case 4: h ~= u64(data[3]) << 24; fallthrough
|
case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
|
||||||
case 3: h ~= u64(data[2]) << 16; fallthrough
|
case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
|
||||||
case 2: h ~= u64(data[1]) << 8; fallthrough
|
case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
|
||||||
case 1:
|
case 1:
|
||||||
h ~= u64(data[0])
|
h ~= u64(data[offset + 0])
|
||||||
h *= m
|
h *= m
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,12 +211,17 @@ murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
h ~= h>>r
|
h ~= h>>r
|
||||||
|
|
||||||
return h
|
return h
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
// 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
|
m :: 0x5bd1e995
|
||||||
r :: 24
|
r :: 24
|
||||||
|
|
||||||
h1 := u32(seed) ~ u32(len(data))
|
h1 := u32(seed) ~ u32(len(data))
|
||||||
h2 := u32(seed) >> 32
|
h2 := u32(seed) >> 32
|
||||||
|
|
||||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||||
len := len(data)
|
len := len(data)
|
||||||
i := 0
|
i := 0
|
||||||
@@ -274,7 +281,6 @@ murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
|
|
||||||
return u64(h1)<<32 | u64(h2)
|
return u64(h1)<<32 | u64(h2)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="speed")
|
||||||
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
|
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||||
|
|||||||
@@ -52,9 +52,6 @@ XXH3_128_reset_with_seed :: proc(state: ^XXH3_state, seed: XXH64_hash) -> (err:
|
|||||||
XXH3_64_reset_with_seed :: XXH3_128_reset_with_seed
|
XXH3_64_reset_with_seed :: XXH3_128_reset_with_seed
|
||||||
|
|
||||||
XXH3_128_update :: proc(state: ^XXH3_state, input: []u8) -> (err: Error) {
|
XXH3_128_update :: proc(state: ^XXH3_state, input: []u8) -> (err: Error) {
|
||||||
if len(input) < XXH3_MIDSIZE_MAX {
|
|
||||||
return .Error
|
|
||||||
}
|
|
||||||
return XXH3_update(state, input, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_update(state, input, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
XXH3_64_update :: XXH3_128_update
|
XXH3_64_update :: XXH3_128_update
|
||||||
@@ -127,6 +124,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
|
|||||||
err = nil if mem_error == nil else .Error
|
err = nil if mem_error == nil else .Error
|
||||||
|
|
||||||
XXH3_init_state(state)
|
XXH3_init_state(state)
|
||||||
|
XXH3_128_reset(state)
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +211,9 @@ XXH3_update :: #force_inline proc(
|
|||||||
length := len(input)
|
length := len(input)
|
||||||
secret := state.custom_secret[:] if len(state.external_secret) == 0 else state.external_secret[:]
|
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)
|
state.total_length += u64(length)
|
||||||
assert(state.buffered_size <= XXH3_INTERNAL_BUFFER_SIZE)
|
assert(state.buffered_size <= XXH3_INTERNAL_BUFFER_SIZE)
|
||||||
@@ -234,7 +234,9 @@ XXH3_update :: #force_inline proc(
|
|||||||
*/
|
*/
|
||||||
if state.buffered_size > 0 {
|
if state.buffered_size > 0 {
|
||||||
load_size := int(XXH3_INTERNAL_BUFFER_SIZE - state.buffered_size)
|
load_size := int(XXH3_INTERNAL_BUFFER_SIZE - state.buffered_size)
|
||||||
mem_copy(&state.buffer[state.buffered_size], &input[0], load_size)
|
|
||||||
|
state_ptr := rawptr(uintptr(raw_data(state.buffer[:])) + uintptr(state.buffered_size))
|
||||||
|
mem_copy(state_ptr, raw_data(input), load_size)
|
||||||
input = input[load_size:]
|
input = input[load_size:]
|
||||||
|
|
||||||
XXH3_consume_stripes(
|
XXH3_consume_stripes(
|
||||||
|
|||||||
@@ -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) {
|
XXH32_create_state :: proc(allocator := context.allocator) -> (res: ^XXH32_state, err: Error) {
|
||||||
state := new(XXH32_state, allocator)
|
state := new(XXH32_state, allocator)
|
||||||
|
XXH32_reset_state(state)
|
||||||
return state, .None if state != nil else .Error
|
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
|
v3 := state.v3
|
||||||
v4 := state.v4
|
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 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 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:]
|
#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) {
|
XXH64_create_state :: proc(allocator := context.allocator) -> (res: ^XXH64_state, err: Error) {
|
||||||
state := new(XXH64_state, allocator)
|
state := new(XXH64_state, allocator)
|
||||||
|
XXH64_reset_state(state)
|
||||||
return state, .None if state != nil else .Error
|
return state, .None if state != nil else .Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+945
-24
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
Formats:
|
||||||
|
PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
|
||||||
|
PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
|
||||||
|
PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
|
||||||
|
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:
|
||||||
|
All formats fill out header fields `format`, `width`, `height`, `channels`, `depth`
|
||||||
|
Specific formats use more fields
|
||||||
|
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:
|
||||||
|
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` (maximum of 65535)
|
||||||
|
PAM also uses `tupltype`, though it may be left as default (empty or nil string)
|
||||||
|
PFM requires `scale`, and optionally `little_endian`
|
||||||
|
|
||||||
|
Some syntax differences from the specifications:
|
||||||
|
`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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package netpbm
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package netpbm
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:image"
|
||||||
|
|
||||||
|
destroy :: proc(img: ^image.Image) -> bool {
|
||||||
|
if img == nil do return false
|
||||||
|
|
||||||
|
defer free(img)
|
||||||
|
bytes.buffer_destroy(&img.pixels)
|
||||||
|
|
||||||
|
info, ok := img.metadata.(^image.Netpbm_Info)
|
||||||
|
if !ok do return false
|
||||||
|
|
||||||
|
header_destroy(&info.header)
|
||||||
|
free(info)
|
||||||
|
img.metadata = nil
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
header_destroy :: proc(using header: ^Header) {
|
||||||
|
if format == .P7 && tupltype != "" {
|
||||||
|
delete(tupltype)
|
||||||
|
tupltype = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,763 @@
|
|||||||
|
package netpbm
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:image"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:os"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:unicode"
|
||||||
|
|
||||||
|
Image :: image.Image
|
||||||
|
Format :: image.Netpbm_Format
|
||||||
|
Header :: image.Netpbm_Header
|
||||||
|
Info :: image.Netpbm_Info
|
||||||
|
Error :: image.Error
|
||||||
|
Format_Error :: image.Netpbm_Error
|
||||||
|
|
||||||
|
Formats :: bit_set[Format]
|
||||||
|
PBM :: Formats{.P1, .P4}
|
||||||
|
PGM :: Formats{.P2, .P5}
|
||||||
|
PPM :: Formats{.P3, .P6}
|
||||||
|
PNM :: PBM + PGM + PPM
|
||||||
|
PAM :: Formats{.P7}
|
||||||
|
PFM :: Formats{.Pf, .PF}
|
||||||
|
ASCII :: Formats{.P1, .P2, .P3}
|
||||||
|
BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM
|
||||||
|
|
||||||
|
load :: proc {
|
||||||
|
load_from_file,
|
||||||
|
load_from_bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
data, ok := os.read_entire_file(filename); defer delete(data)
|
||||||
|
if !ok {
|
||||||
|
err = .Unable_To_Read_File
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return load_from_bytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
header, header_size = parse_header(data) or_return
|
||||||
|
|
||||||
|
img_data := data[header_size:]
|
||||||
|
decode_image(img, header, img_data) or_return
|
||||||
|
|
||||||
|
info := new(Info)
|
||||||
|
info.header = header
|
||||||
|
if header.format == .P7 && header.tupltype != "" {
|
||||||
|
info.header.tupltype = strings.clone(header.tupltype)
|
||||||
|
}
|
||||||
|
img.metadata = info
|
||||||
|
|
||||||
|
return img, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
save :: proc {
|
||||||
|
save_to_file,
|
||||||
|
save_to_buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
data: []byte; defer delete(data)
|
||||||
|
data = save_to_buffer(img, custom_info) or_return
|
||||||
|
|
||||||
|
if ok := os.write_entire_file(filename, data); !ok {
|
||||||
|
return .Unable_To_Write_File
|
||||||
|
}
|
||||||
|
|
||||||
|
return Format_Error.None
|
||||||
|
}
|
||||||
|
|
||||||
|
save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (buffer: []byte, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
info: Info = {}
|
||||||
|
if custom_info.header.width > 0 {
|
||||||
|
// Custom info has been set, use it.
|
||||||
|
info = custom_info
|
||||||
|
} else {
|
||||||
|
img_info, ok := img.metadata.(^image.Netpbm_Info)
|
||||||
|
if !ok {
|
||||||
|
// image doesn't have .Netpbm info, guess it
|
||||||
|
auto_info, auto_info_found := autoselect_pbm_format_from_image(img)
|
||||||
|
if auto_info_found {
|
||||||
|
info = auto_info
|
||||||
|
} else {
|
||||||
|
return {}, .Invalid_Input_Image
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// use info as stored on image
|
||||||
|
info = img_info^
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// using info so we can just talk about the header
|
||||||
|
using info
|
||||||
|
|
||||||
|
// 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
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.format in (PNM + PAM) {
|
||||||
|
if header.maxval <= int(max(u8)) && img.depth != 8 \
|
||||||
|
|| header.maxval > int(max(u8)) && header.maxval <= int(max(u16)) && img.depth != 16 {
|
||||||
|
err = .Invalid_Image_Depth
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if header.format in PFM && img.depth != 32 {
|
||||||
|
err = .Invalid_Image_Depth
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// we will write to a string builder
|
||||||
|
data: strings.Builder
|
||||||
|
strings.builder_init(&data)
|
||||||
|
|
||||||
|
// all PNM headers start with the format
|
||||||
|
fmt.sbprintf(&data, "%s\n", header.format)
|
||||||
|
if header.format in PNM {
|
||||||
|
fmt.sbprintf(&data, "%i %i\n", img.width, img.height)
|
||||||
|
if header.format not_in PBM {
|
||||||
|
fmt.sbprintf(&data, "%i\n", header.maxval)
|
||||||
|
}
|
||||||
|
} else if header.format in PAM {
|
||||||
|
if len(header.tupltype) > 0 {
|
||||||
|
fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nTUPLTYPE %s\nENDHDR\n",
|
||||||
|
img.width, img.height, header.maxval, img.channels, header.tupltype)
|
||||||
|
} else {
|
||||||
|
fmt.sbprintf(&data, "WIDTH %i\nHEIGHT %i\nMAXVAL %i\nDEPTH %i\nENDHDR\n",
|
||||||
|
img.width, img.height, header.maxval, img.channels)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if header.format in PFM {
|
||||||
|
scale := -header.scale if header.little_endian else header.scale
|
||||||
|
fmt.sbprintf(&data, "%i %i\n%f\n", img.width, img.height, scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch header.format {
|
||||||
|
// Compressed binary
|
||||||
|
case .P4:
|
||||||
|
header_buf := data.buf[:]
|
||||||
|
pixels := img.pixels.buf[:]
|
||||||
|
|
||||||
|
p4_buffer_size := (img.width / 8 + 1) * img.height
|
||||||
|
reserve(&data.buf, len(header_buf) + p4_buffer_size)
|
||||||
|
|
||||||
|
// we build up a byte value until it is completely filled
|
||||||
|
// or we reach the end the row
|
||||||
|
for y in 0 ..< img.height {
|
||||||
|
b: byte
|
||||||
|
|
||||||
|
for x in 0 ..< img.width {
|
||||||
|
i := y * img.width + x
|
||||||
|
bit := byte(7 - (x % 8))
|
||||||
|
v : byte = 0 if pixels[i] == 0 else 1
|
||||||
|
b |= (v << bit)
|
||||||
|
|
||||||
|
if bit == 0 {
|
||||||
|
append(&data.buf, b)
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if b != 0 {
|
||||||
|
append(&data.buf, b)
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple binary
|
||||||
|
case .P5, .P6, .P7, .Pf, .PF:
|
||||||
|
header_buf := data.buf[:]
|
||||||
|
pixels := img.pixels.buf[:]
|
||||||
|
|
||||||
|
resize(&data.buf, len(header_buf) + len(pixels))
|
||||||
|
mem.copy(raw_data(data.buf[len(header_buf):]), raw_data(pixels), len(pixels))
|
||||||
|
|
||||||
|
// convert from native endianness
|
||||||
|
if img.depth == 16 {
|
||||||
|
pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):])
|
||||||
|
for p in &pixels {
|
||||||
|
p = u16be(transmute(u16) p)
|
||||||
|
}
|
||||||
|
} else if header.format in PFM {
|
||||||
|
if header.little_endian {
|
||||||
|
pixels := mem.slice_data_cast([]f32le, data.buf[len(header_buf):])
|
||||||
|
for p in &pixels {
|
||||||
|
p = f32le(transmute(f32) p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pixels := mem.slice_data_cast([]f32be, data.buf[len(header_buf):])
|
||||||
|
for p in &pixels {
|
||||||
|
p = f32be(transmute(f32) p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If-it-looks-like-a-bitmap ASCII
|
||||||
|
case .P1:
|
||||||
|
pixels := img.pixels.buf[:]
|
||||||
|
for y in 0 ..< img.height {
|
||||||
|
for x in 0 ..< img.width {
|
||||||
|
i := y * img.width + x
|
||||||
|
append(&data.buf, '0' if pixels[i] == 0 else '1')
|
||||||
|
}
|
||||||
|
append(&data.buf, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token ASCII
|
||||||
|
case .P2, .P3:
|
||||||
|
switch img.depth {
|
||||||
|
case 8:
|
||||||
|
pixels := img.pixels.buf[:]
|
||||||
|
for y in 0 ..< img.height {
|
||||||
|
for x in 0 ..< img.width {
|
||||||
|
i := y * img.width + x
|
||||||
|
for c in 0 ..< img.channels {
|
||||||
|
i := i * img.channels + c
|
||||||
|
fmt.sbprintf(&data, "%i ", pixels[i])
|
||||||
|
}
|
||||||
|
fmt.sbprint(&data, "\n")
|
||||||
|
}
|
||||||
|
fmt.sbprint(&data, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
pixels := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||||
|
for y in 0 ..< img.height {
|
||||||
|
for x in 0 ..< img.width {
|
||||||
|
i := y * img.width + x
|
||||||
|
for c in 0 ..< img.channels {
|
||||||
|
i := i * img.channels + c
|
||||||
|
fmt.sbprintf(&data, "%i ", pixels[i])
|
||||||
|
}
|
||||||
|
fmt.sbprint(&data, "\n")
|
||||||
|
}
|
||||||
|
fmt.sbprint(&data, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
return data.buf[:], .Invalid_Image_Depth
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
return data.buf[:], .Invalid_Format
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.buf[:], Format_Error.None
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_header :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
// we need the signature and a space
|
||||||
|
if len(data) < 3 {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if data[0] == 'P' {
|
||||||
|
switch data[1] {
|
||||||
|
case '1' ..= '6':
|
||||||
|
return _parse_header_pnm(data)
|
||||||
|
case '7':
|
||||||
|
return _parse_header_pam(data, allocator)
|
||||||
|
case 'F', 'f':
|
||||||
|
return _parse_header_pfm(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = .Invalid_Signature
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Error) {
|
||||||
|
SIG_LENGTH :: 2
|
||||||
|
|
||||||
|
{
|
||||||
|
header_formats := []Format{.P1, .P2, .P3, .P4, .P5, .P6}
|
||||||
|
header.format = header_formats[data[1] - '0' - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// have a list of fielda for easy iteration
|
||||||
|
header_fields: []^int
|
||||||
|
if header.format in PBM {
|
||||||
|
header_fields = {&header.width, &header.height}
|
||||||
|
header.maxval = 1 // we know maxval for a bitmap
|
||||||
|
} else {
|
||||||
|
header_fields = {&header.width, &header.height, &header.maxval}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're keeping track of the header byte length
|
||||||
|
length = SIG_LENGTH
|
||||||
|
|
||||||
|
// loop state
|
||||||
|
in_comment := false
|
||||||
|
already_in_space := true
|
||||||
|
current_field := 0
|
||||||
|
current_value := header_fields[0]
|
||||||
|
|
||||||
|
parse_loop: for d, i in data[SIG_LENGTH:] {
|
||||||
|
length += 1
|
||||||
|
|
||||||
|
// handle comments
|
||||||
|
if in_comment {
|
||||||
|
switch d {
|
||||||
|
// comments only go up to next carriage return or line feed
|
||||||
|
case '\r', '\n':
|
||||||
|
in_comment = false
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else if d == '#' {
|
||||||
|
in_comment = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle whitespace
|
||||||
|
in_space := unicode.is_white_space(rune(d))
|
||||||
|
if in_space {
|
||||||
|
if already_in_space {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
already_in_space = true
|
||||||
|
|
||||||
|
// switch to next value
|
||||||
|
current_field += 1
|
||||||
|
if current_field == len(header_fields) {
|
||||||
|
// header byte length is 1-index so we'll increment again
|
||||||
|
length += 1
|
||||||
|
break parse_loop
|
||||||
|
}
|
||||||
|
current_value = header_fields[current_field]
|
||||||
|
} else {
|
||||||
|
already_in_space = false
|
||||||
|
|
||||||
|
if !unicode.is_digit(rune(d)) {
|
||||||
|
err = Format_Error.Invalid_Header_Token_Character
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := int(d - '0')
|
||||||
|
current_value^ = current_value^ * 10 + val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set extra info
|
||||||
|
header.channels = 3 if header.format in PPM else 1
|
||||||
|
header.depth = 16 if header.maxval > int(max(u8)) else 8
|
||||||
|
|
||||||
|
// limit checking
|
||||||
|
if current_field < len(header_fields) {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.width < 1 \
|
||||||
|
|| header.height < 1 \
|
||||||
|
|| header.maxval < 1 || header.maxval > int(max(u16)) {
|
||||||
|
fmt.printf("[pnm] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval)
|
||||||
|
err = .Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
length -= 1
|
||||||
|
err = Format_Error.None
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
// the spec needs the newline apparently
|
||||||
|
if string(data[0:3]) != "P7\n" {
|
||||||
|
err = .Invalid_Signature
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.format = .P7
|
||||||
|
|
||||||
|
SIGNATURE_LENGTH :: 3
|
||||||
|
HEADER_END :: "ENDHDR\n"
|
||||||
|
|
||||||
|
// we can already work out the size of the header
|
||||||
|
header_end_index := strings.index(string(data), HEADER_END)
|
||||||
|
if header_end_index == -1 {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
length = header_end_index + len(HEADER_END)
|
||||||
|
|
||||||
|
// string buffer for the tupltype
|
||||||
|
tupltype: strings.Builder
|
||||||
|
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
|
||||||
|
fmt.sbprint(&tupltype, "")
|
||||||
|
|
||||||
|
// PAM uses actual lines, so we can iterate easily
|
||||||
|
line_iterator := string(data[SIGNATURE_LENGTH : header_end_index])
|
||||||
|
parse_loop: for line in strings.split_lines_iterator(&line_iterator) {
|
||||||
|
line := line
|
||||||
|
|
||||||
|
if len(line) == 0 || line[0] == '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
field, ok := strings.fields_iterator(&line)
|
||||||
|
value := strings.trim_space(line)
|
||||||
|
|
||||||
|
// the field will change, but the logic stays the same
|
||||||
|
current_field: ^int
|
||||||
|
|
||||||
|
switch field {
|
||||||
|
case "WIDTH": current_field = &header.width
|
||||||
|
case "HEIGHT": current_field = &header.height
|
||||||
|
case "DEPTH": current_field = &header.channels
|
||||||
|
case "MAXVAL": current_field = &header.maxval
|
||||||
|
|
||||||
|
case "TUPLTYPE":
|
||||||
|
if len(value) == 0 {
|
||||||
|
err = .Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tupltype.buf) == 0 {
|
||||||
|
fmt.sbprint(&tupltype, value)
|
||||||
|
} else {
|
||||||
|
fmt.sbprint(&tupltype, "", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
case:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_field^ != 0 {
|
||||||
|
err = Format_Error.Duplicate_Header_Field
|
||||||
|
return
|
||||||
|
}
|
||||||
|
current_field^, ok = strconv.parse_int(value)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// extra info
|
||||||
|
header.depth = 16 if header.maxval > int(max(u8)) else 8
|
||||||
|
|
||||||
|
// limit checking
|
||||||
|
if header.width < 1 \
|
||||||
|
|| header.height < 1 \
|
||||||
|
|| header.maxval < 1 \
|
||||||
|
|| header.maxval > int(max(u16)) {
|
||||||
|
fmt.printf("[pam] Header: {{width = %v, height = %v, maxval: %v}}\n", header.width, header.height, header.maxval)
|
||||||
|
err = Format_Error.Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
header.tupltype = strings.clone(strings.to_string(tupltype))
|
||||||
|
err = Format_Error.None
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_parse_header_pfm :: proc(data: []byte) -> (header: Header, length: int, err: Error) {
|
||||||
|
// we can just cycle through tokens for PFM
|
||||||
|
field_iterator := string(data)
|
||||||
|
field, ok := strings.fields_iterator(&field_iterator)
|
||||||
|
|
||||||
|
switch field {
|
||||||
|
case "Pf":
|
||||||
|
header.format = .Pf
|
||||||
|
header.channels = 1
|
||||||
|
case "PF":
|
||||||
|
header.format = .PF
|
||||||
|
header.channels = 3
|
||||||
|
case:
|
||||||
|
err = .Invalid_Signature
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point
|
||||||
|
header.depth = 32
|
||||||
|
|
||||||
|
// width
|
||||||
|
field, ok = strings.fields_iterator(&field_iterator)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.width, ok = strconv.parse_int(field)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// height
|
||||||
|
field, ok = strings.fields_iterator(&field_iterator)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.height, ok = strconv.parse_int(field)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// scale (sign is endianness)
|
||||||
|
field, ok = strings.fields_iterator(&field_iterator)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Incomplete_Header
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header.scale, ok = strconv.parse_f32(field)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.scale < 0.0 {
|
||||||
|
header.little_endian = true
|
||||||
|
header.scale = -header.scale
|
||||||
|
}
|
||||||
|
|
||||||
|
// pointer math to get header size
|
||||||
|
length = int((uintptr(raw_data(field_iterator)) + 1) - uintptr(raw_data(data)))
|
||||||
|
|
||||||
|
// limit checking
|
||||||
|
if header.width < 1 \
|
||||||
|
|| header.height < 1 \
|
||||||
|
|| header.scale == 0.0 {
|
||||||
|
fmt.printf("[pfm] Header: {{width = %v, height = %v, scale: %v}}\n", header.width, header.height, header.scale)
|
||||||
|
err = .Invalid_Header_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Format_Error.None
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_image :: proc(img: ^Image, header: Header, data: []byte, allocator := context.allocator) -> (err: Error) {
|
||||||
|
assert(img != nil)
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
img.width = header.width
|
||||||
|
img.height = header.height
|
||||||
|
img.channels = header.channels
|
||||||
|
img.depth = header.depth
|
||||||
|
|
||||||
|
buffer_size := image.compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||||
|
|
||||||
|
// we can check data size for binary formats
|
||||||
|
if header.format in BINARY {
|
||||||
|
if len(data) < buffer_size {
|
||||||
|
fmt.printf("len(data): %v, buffer size: %v\n", len(data), buffer_size)
|
||||||
|
return .Buffer_Too_Small
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for ASCII and P4, we use length for the termination condition, so start at 0
|
||||||
|
// BINARY will be a simple memcopy so the buffer length should also be initialised
|
||||||
|
if header.format in ASCII || header.format == .P4 {
|
||||||
|
bytes.buffer_init_allocator(&img.pixels, 0, buffer_size)
|
||||||
|
} else {
|
||||||
|
bytes.buffer_init_allocator(&img.pixels, buffer_size, buffer_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch header.format {
|
||||||
|
// Compressed binary
|
||||||
|
case .P4:
|
||||||
|
for d in data {
|
||||||
|
for b in 1 ..= 8 {
|
||||||
|
bit := byte(8 - b)
|
||||||
|
pix := (d >> bit) & 1
|
||||||
|
bytes.buffer_write_byte(&img.pixels, pix)
|
||||||
|
if len(img.pixels.buf) % img.width == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(img.pixels.buf) == cap(img.pixels.buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple binary
|
||||||
|
case .P5, .P6, .P7, .Pf, .PF:
|
||||||
|
copy(img.pixels.buf[:], data[:])
|
||||||
|
|
||||||
|
// convert to native endianness
|
||||||
|
if header.format in PFM {
|
||||||
|
pixels := mem.slice_data_cast([]f32, img.pixels.buf[:])
|
||||||
|
if header.little_endian {
|
||||||
|
for p in &pixels {
|
||||||
|
p = f32(transmute(f32le) p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for p in &pixels {
|
||||||
|
p = f32(transmute(f32be) p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if img.depth == 16 {
|
||||||
|
pixels := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||||
|
for p in &pixels {
|
||||||
|
p = u16(transmute(u16be) p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If-it-looks-like-a-bitmap ASCII
|
||||||
|
case .P1:
|
||||||
|
for c in data {
|
||||||
|
switch c {
|
||||||
|
case '0', '1':
|
||||||
|
bytes.buffer_write_byte(&img.pixels, c - '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(img.pixels.buf) == cap(img.pixels.buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(img.pixels.buf) < cap(img.pixels.buf) {
|
||||||
|
err = Format_Error.Buffer_Too_Small
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token ASCII
|
||||||
|
case .P2, .P3:
|
||||||
|
field_iterator := string(data)
|
||||||
|
for field in strings.fields_iterator(&field_iterator) {
|
||||||
|
value, ok := strconv.parse_int(field)
|
||||||
|
if !ok {
|
||||||
|
err = Format_Error.Invalid_Buffer_ASCII_Token
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//? do we want to enforce the maxval, the limit, or neither
|
||||||
|
if value > int(max(u16)) /*header.maxval*/ {
|
||||||
|
err = Format_Error.Invalid_Buffer_Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch img.depth {
|
||||||
|
case 8:
|
||||||
|
bytes.buffer_write_byte(&img.pixels, u8(value))
|
||||||
|
case 16:
|
||||||
|
vb := transmute([2]u8) u16(value)
|
||||||
|
bytes.buffer_write(&img.pixels, vb[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(img.pixels.buf) == cap(img.pixels.buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(img.pixels.buf) < cap(img.pixels.buf) {
|
||||||
|
err = Format_Error.Buffer_Too_Small
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Format_Error.None
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically try to select an appropriate format to save to based on `img.channel` and `img.depth`
|
||||||
|
autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, force_black_and_white := false, pfm_scale := f32(1.0)) -> (res: Info, ok: bool) {
|
||||||
|
/*
|
||||||
|
PBM (P1, P4): Portable Bit Map, stores black and white images (1 channel)
|
||||||
|
PGM (P2, P5): Portable Gray Map, stores greyscale images (1 channel, 1 or 2 bytes per value)
|
||||||
|
PPM (P3, P6): Portable Pixel Map, stores colour images (3 channel, 1 or 2 bytes per value)
|
||||||
|
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)
|
||||||
|
|
||||||
|
ASCII :: Formats{.P1, .P2, .P3}
|
||||||
|
*/
|
||||||
|
using res.header
|
||||||
|
|
||||||
|
width = img.width
|
||||||
|
height = img.height
|
||||||
|
channels = img.channels
|
||||||
|
depth = img.depth
|
||||||
|
maxval = 255 if img.depth == 8 else 65535
|
||||||
|
little_endian = true if ODIN_ENDIAN == .Little else false
|
||||||
|
|
||||||
|
// Assume we'll find a suitable format
|
||||||
|
ok = true
|
||||||
|
|
||||||
|
switch img.channels {
|
||||||
|
case 1:
|
||||||
|
// Must be Portable Float Map
|
||||||
|
if img.depth == 32 {
|
||||||
|
format = .Pf
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if force_black_and_white {
|
||||||
|
// Portable Bit Map
|
||||||
|
format = .P4 if prefer_binary else .P1
|
||||||
|
maxval = 1
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// Portable Gray Map
|
||||||
|
format = .P5 if prefer_binary else .P2
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
// Must be Portable Float Map
|
||||||
|
if img.depth == 32 {
|
||||||
|
format = .PF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Portable Pixel Map
|
||||||
|
format = .P6 if prefer_binary else .P3
|
||||||
|
return
|
||||||
|
|
||||||
|
case:
|
||||||
|
// Portable Arbitrary Map
|
||||||
|
if img.depth == 8 || img.depth == 16 {
|
||||||
|
format = .P7
|
||||||
|
scale = pfm_scale
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
@@ -207,7 +207,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
|||||||
}
|
}
|
||||||
|
|
||||||
mode: int = 0
|
mode: int = 0
|
||||||
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
|
when ODIN_OS == .Linux || ODIN_OS == .Darwin {
|
||||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
|||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|
||||||
write_string(fd,
|
write_string(fd,
|
||||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if channels == 3 {
|
if channels == 3 {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user